Compare commits
216 Commits
v0.13.0-b0
...
v0.13.0-b6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e6866c160 | ||
|
|
7101ad81c4 | ||
|
|
8643263227 | ||
|
|
66bad2b6f8 | ||
|
|
46ec504743 | ||
|
|
cadda12371 | ||
|
|
a643b56555 | ||
|
|
f7404085de | ||
|
|
33036e7599 | ||
|
|
f6e5b67f0d | ||
|
|
9547ac353d | ||
|
|
48339b19d4 | ||
|
|
11c7ffad4e | ||
|
|
1973424e05 | ||
|
|
16d97d3c63 | ||
|
|
3e6728fedb | ||
|
|
3e9aea072d | ||
|
|
9f3e66fff0 | ||
|
|
624993fc89 | ||
|
|
ba8a00764a | ||
|
|
3dec4a6651 | ||
|
|
02fb2550d0 | ||
|
|
37bd525638 | ||
|
|
ea0f37f5b9 | ||
|
|
97b3c3db7b | ||
|
|
b97b6dc144 | ||
|
|
c8d5218c65 | ||
|
|
80a657965e | ||
|
|
b3324d22f5 | ||
|
|
31b7cdff9b | ||
|
|
0465298507 | ||
|
|
d31e4c7815 | ||
|
|
4af1f62aab | ||
|
|
bc403440ba | ||
|
|
38d8dfe5ab | ||
|
|
eb92c0bbf5 | ||
|
|
6df64d0d31 | ||
|
|
83753a5f81 | ||
|
|
3161f5fa47 | ||
|
|
5784092c1b | ||
|
|
d6ad089c60 | ||
|
|
446b4b084c | ||
|
|
d590e01a58 | ||
|
|
adeb9bccb1 | ||
|
|
b44ffffed8 | ||
|
|
2bdaf53ecf | ||
|
|
46e7db6d94 | ||
|
|
7e1920dc4b | ||
|
|
a93f05c047 | ||
|
|
00238247cd | ||
|
|
b33e28835d | ||
|
|
f55f803531 | ||
|
|
8ca298b299 | ||
|
|
090e29effd | ||
|
|
0acca2e313 | ||
|
|
0d77027f60 | ||
|
|
39b7b3ad53 | ||
|
|
00f1b483eb | ||
|
|
c3d48acb4c | ||
|
|
392bda7d8c | ||
|
|
10cfcdab8c | ||
|
|
3f71d3b250 | ||
|
|
1b50fbab22 | ||
|
|
303fc65a6a | ||
|
|
445b6ee13f | ||
|
|
8afaac1e30 | ||
|
|
0327f9428e | ||
|
|
a5de66bbd5 | ||
|
|
d47157eec3 | ||
|
|
f4b47ed399 | ||
|
|
8b2145bd88 | ||
|
|
de454e8b78 | ||
|
|
6cd770b4c7 | ||
|
|
355525c248 | ||
|
|
47d4e7381f | ||
|
|
5dac6690d7 | ||
|
|
b89f7180db | ||
|
|
2ebb837a15 | ||
|
|
849aa64678 | ||
|
|
cbb12e1b7c | ||
|
|
cc87ba4962 | ||
|
|
fb2e556726 | ||
|
|
3f0eb0a046 | ||
|
|
7d6d9eddc4 | ||
|
|
cf87da0ef3 | ||
|
|
0775acedc0 | ||
|
|
8f1cee2e61 | ||
|
|
caa9cc32d7 | ||
|
|
b750f827c5 | ||
|
|
5d147163e5 | ||
|
|
75fe1a19eb | ||
|
|
5c9405fffc | ||
|
|
6457314794 | ||
|
|
84f4e3eedc | ||
|
|
b003ed3f03 | ||
|
|
330da137db | ||
|
|
9e5d45d0de | ||
|
|
b5c15d97fa | ||
|
|
6ddcba8917 | ||
|
|
91598cbbbf | ||
|
|
772c80aa85 | ||
|
|
a28345d858 | ||
|
|
05b532b9eb | ||
|
|
0b0d18f182 | ||
|
|
c1b0877956 | ||
|
|
46b66c76ef | ||
|
|
d00b4335b5 | ||
|
|
7a129e6de1 | ||
|
|
17c20276a9 | ||
|
|
dc9dedf220 | ||
|
|
3ac772badc | ||
|
|
22fc58d93b | ||
|
|
ccd3152b24 | ||
|
|
7d929dcde6 | ||
|
|
3a874bc8c7 | ||
|
|
8453cd82e9 | ||
|
|
f62e56b7ec | ||
|
|
2ac90bbb96 | ||
|
|
f85f2d5d22 | ||
|
|
a94269ceb9 | ||
|
|
476ac263fb | ||
|
|
51a4f61a8f | ||
|
|
267f5159a3 | ||
|
|
96422de031 | ||
|
|
f2043dc181 | ||
|
|
e416ec9279 | ||
|
|
5eb4ffb1cc | ||
|
|
8fae964ee8 | ||
|
|
baf49b88f4 | ||
|
|
3577da05ac | ||
|
|
b8e8028eb9 | ||
|
|
a899666e68 | ||
|
|
10a52f8cf9 | ||
|
|
72d04a0120 | ||
|
|
6dbed30008 | ||
|
|
c5eac298e6 | ||
|
|
bc18eda336 | ||
|
|
3cefb14297 | ||
|
|
4bc401278e | ||
|
|
d7e3765efe | ||
|
|
3d51d1e345 | ||
|
|
bd23942893 | ||
|
|
c8610b8ad2 | ||
|
|
d0440122b9 | ||
|
|
8d4636bbab | ||
|
|
f59c6e7a7c | ||
|
|
c24ab1b21d | ||
|
|
6a01658355 | ||
|
|
f1e2439e66 | ||
|
|
4d89ed701d | ||
|
|
e80594d61d | ||
|
|
83c6f72eb0 | ||
|
|
e26299b998 | ||
|
|
a839809eb8 | ||
|
|
88ceba59cf | ||
|
|
f368bbec32 | ||
|
|
021c4ba68a | ||
|
|
54f4658dae | ||
|
|
dbc67e077d | ||
|
|
e968917dbc | ||
|
|
d8240bb683 | ||
|
|
b481c13829 | ||
|
|
77c0ba990d | ||
|
|
1d4487b6cd | ||
|
|
ff8145b745 | ||
|
|
530e8b39e5 | ||
|
|
50aeee288b | ||
|
|
72e001b0d5 | ||
|
|
f04c9d101e | ||
|
|
2ecc53ba56 | ||
|
|
3eb1fe0eb2 | ||
|
|
aec998acc1 | ||
|
|
91e758f66f | ||
|
|
441416b241 | ||
|
|
e541d8697e | ||
|
|
4b817208aa | ||
|
|
7fea0c3244 | ||
|
|
bd13336256 | ||
|
|
815940913b | ||
|
|
f7191c0381 | ||
|
|
07d11c845c | ||
|
|
2e9bd477d9 | ||
|
|
b058fb8db4 | ||
|
|
9f0f6181a1 | ||
|
|
f702e1a80d | ||
|
|
e1527fcbb9 | ||
|
|
9ba7e5d567 | ||
|
|
02b6d53544 | ||
|
|
123bd0bb92 | ||
|
|
6a8ed1192f | ||
|
|
0862859f93 | ||
|
|
3ad336a1eb | ||
|
|
a17f83cedd | ||
|
|
2c6850f6e4 | ||
|
|
5da47636cf | ||
|
|
e04b965659 | ||
|
|
17d2fb80f2 | ||
|
|
14b7ec2a80 | ||
|
|
f27b31b581 | ||
|
|
8c44147a45 | ||
|
|
ec05215a5e | ||
|
|
5903e8256f | ||
|
|
c879351063 | ||
|
|
1bb7e36a65 | ||
|
|
793a01f7ca | ||
|
|
40c8fdbf64 | ||
|
|
dc01c907f1 | ||
|
|
801df94446 | ||
|
|
0197d89976 | ||
|
|
e16a67242e | ||
|
|
4c678a5010 | ||
|
|
3754088a44 | ||
|
|
c4f084a991 | ||
|
|
4c73df4ba6 | ||
|
|
4aa53aa5a5 | ||
|
|
d6337f7500 |
27
.github/ISSUE_TEMPLATE/bug.md
vendored
27
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: Bug
|
||||
about: Noticed an issue with your lights?
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. Please quickly search existing issues first!
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior, if consistently possible
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**WLED version**
|
||||
- Board: [e.g. Wemos D1, ESP32 dev]
|
||||
- Version [e.g. 0.10.0, dev200603]
|
||||
- Format [e.g. Binary, self-compiled]
|
||||
|
||||
**Additional context**
|
||||
Anything else you'd like to say about the problem?
|
||||
|
||||
Thank you for your help!
|
||||
83
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
83
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please quickly search existing issues first before submitting a bug.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: Tell us what the problem is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: To Reproduce Bug
|
||||
description: Steps to reproduce the behavior, if consistently possible.
|
||||
placeholder: Tell us how to make the bug appear.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
placeholder: Tell us what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: install_format
|
||||
attributes:
|
||||
label: Install Method
|
||||
description: How did you install WLED?
|
||||
options:
|
||||
- Binary from WLED.me
|
||||
- Self-Compiled
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: What version of WLED?
|
||||
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
||||
placeholder: "e.g. WLED 0.13.0-b4 (build 2110110)"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: Board
|
||||
attributes:
|
||||
label: Which microcontroller/board are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- ESP8266
|
||||
- ESP32
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log/trace output
|
||||
description: Please copy and paste any relevant log output if you have it. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Aircoookie/WLED/blob/master/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
179
CHANGELOG.md
179
CHANGELOG.md
@@ -2,6 +2,184 @@
|
||||
|
||||
### Builds after release 0.12.0
|
||||
|
||||
#### Build 2112080
|
||||
|
||||
- Version bump to 0.13.0-b6 "Toki"
|
||||
- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries
|
||||
|
||||
#### Build 2112070
|
||||
|
||||
- Added new effect "Fairy", replacing "Police All"
|
||||
- Added new effect "Fairytwinkle", replacing "Two Areas"
|
||||
- Static single JSON buffer (performance and stability improvement) (PR #2336)
|
||||
|
||||
#### Build 2112030
|
||||
|
||||
- Fixed ESP32 crash on Colortwinkles brightness change
|
||||
- Fixed setting picker to black resetting hue and saturation
|
||||
- Fixed auto white mode not saved to config
|
||||
|
||||
#### Build 2111300
|
||||
|
||||
- Added CCT and white balance correction support (PR #2285)
|
||||
- Unified UI slider style
|
||||
- Added LED settings config template upload
|
||||
|
||||
#### Build 2111220
|
||||
|
||||
- Fixed preset cycle not working from preset called by UI
|
||||
- Reintroduced permanent min. and max. cycle bounds
|
||||
|
||||
#### Build 2111190
|
||||
|
||||
- Changed default ESP32 LED pin from 16 to 2
|
||||
- Renamed "Running 2" to "Chase 2"
|
||||
- Renamed "Tri Chase" to "Chase 3"
|
||||
|
||||
#### Build 2111170
|
||||
|
||||
- Version bump to 0.13.0-b5 "Toki"
|
||||
- Improv Serial support (PR #2334)
|
||||
- Button improvements (PR #2284)
|
||||
- Added two time zones (PR #2264, 2311)
|
||||
- JSON in/decrementing support for brightness and presets
|
||||
- Fixed no gamma correction for JSON individual LED control
|
||||
- Preset cycle bugfix
|
||||
- Removed ledCount
|
||||
- LED settings buffer bugfix
|
||||
- Network pin conflict bugfix
|
||||
- Changed default ESP32 partition layout to 4M, 1M FS
|
||||
|
||||
#### Build 2110110
|
||||
|
||||
- Version bump to 0.13.0-b4 "Toki"
|
||||
- Added option for bus refresh if off (PR #2259)
|
||||
- New auto segment logic
|
||||
- Fixed current calculations for virtual or non-linear configs (PR #2262)
|
||||
|
||||
#### Build 2110060
|
||||
|
||||
- Added virtual network DDP busses (PR #2245)
|
||||
- Allow playlist as end preset in playlist
|
||||
- Improved bus start field UX
|
||||
- Pin reservations improvements (PR #2214)
|
||||
|
||||
#### Build 2109220
|
||||
|
||||
- Version bump to 0.13.0-b3 "Toki"
|
||||
- Added segment names (PR #2184)
|
||||
- Improved Police and other effects (PR #2184)
|
||||
- Reverted PR #1902 (Live color correction - will be implemented as usermod) (PR #2175)
|
||||
- Added transitions for segment on/off
|
||||
- Improved number of sparks/stars in Fireworks effect with low number of segments
|
||||
- Fixed segment name edit pencil disappearing with request
|
||||
- Fixed color transition active even if the segment is off
|
||||
- Disallowed file upload with OTA lock active
|
||||
- Fixed analog invert option missing (PR #2219)
|
||||
|
||||
#### Build 2109100
|
||||
|
||||
- Added an auto create segments per bus setting
|
||||
- Added 15 new palettes from SR branch (PR #2134)
|
||||
- Fixed segment runtime not reset on FX change via HTTP API
|
||||
- Changed AsyncTCP dependency to pbolduc fork v1.2.0
|
||||
|
||||
#### Build 2108250
|
||||
|
||||
- Added Sync groups (PR #2150)
|
||||
- Added JSON API over Serial support
|
||||
- Live color correction (PR #1902)
|
||||
|
||||
#### Build 2108180
|
||||
|
||||
- Fixed JSON IR remote not working with codes greater than 0xFFFFFF (fixes #2135)
|
||||
- Fixed transition 0 edge case
|
||||
|
||||
#### Build 2108170
|
||||
|
||||
- Added application level pong websockets reply (#2139)
|
||||
- Use AsyncTCP 1.0.3 as it mitigates the flickering issue from 0.13.0-b2
|
||||
- Fixed transition manually updated in preset overriden by field value
|
||||
|
||||
#### Build 2108050
|
||||
|
||||
- Fixed undesirable color transition from Orange to boot preset color on first boot
|
||||
- Removed misleading Delete button on new playlist with one entry
|
||||
- Updated NeoPixelBus to 2.6.7 and AsyncTCP to 1.1.1
|
||||
|
||||
#### Build 2107230
|
||||
|
||||
- Added skinning (extra custom CSS) (PR #2084)
|
||||
- Added presets/config backup/restore (PR #2084)
|
||||
- Added option for using length instead of Stop LED in UI (PR #2048)
|
||||
- Added custom `holidays.json` holiday list (PR #2048)
|
||||
|
||||
#### Build 2107100
|
||||
|
||||
- Version bump to 0.13.0-b2 "Toki"
|
||||
- Accept hex color strings in individual LED API
|
||||
- Fixed transition property not applying unless power/bri/color changed next
|
||||
- Moved transition field below segments (temporarily)
|
||||
- Reduced unneeded websockets pushes
|
||||
|
||||
#### Build 2107091
|
||||
|
||||
- Fixed presets using wrong call mode (e.g. causing buttons to send UDP under direct change type)
|
||||
- Increased hue buffer
|
||||
- Renamed `NOTIFIER_CALL_MODE_` to `CALL_MODE_`
|
||||
|
||||
#### Build 2107090
|
||||
|
||||
- Busses extend total configured LEDs if required
|
||||
- Fixed extra button pins defaulting to 0 on first boot
|
||||
|
||||
#### Build 2107080
|
||||
|
||||
- Made Peek use the main websocket connection instead of opening a second one
|
||||
- Temperature usermod fix (from @blazoncek's dev branch)
|
||||
|
||||
#### Build 2107070
|
||||
|
||||
- More robust initial resource loading in UI
|
||||
- Added `getJsonValue()` for usermod config parsing (PR #2061)
|
||||
- Fixed preset saving over websocket
|
||||
- Alpha ESP32 S2 support (filesystem does not work) (PR #2067)
|
||||
|
||||
#### Build 2107042
|
||||
|
||||
- Updated ArduinoJson to 6.18.1
|
||||
- Improved Twinkleup effect
|
||||
- Fixed preset immediately deselecting when set via HTTP API `PL=`
|
||||
|
||||
#### Build 2107041
|
||||
|
||||
- Restored support for "PL=~" mistakenly removed in 2106300
|
||||
- JSON IR improvements
|
||||
|
||||
#### Build 2107040
|
||||
|
||||
- Playlist entries are now more compact
|
||||
- Added the possibility to enter negative numbers for segment offset
|
||||
|
||||
#### Build 2107021
|
||||
|
||||
- Added WebSockets support to UI
|
||||
|
||||
#### Build 2107020
|
||||
|
||||
- Send websockets on every state change
|
||||
- Improved Aurora effect
|
||||
|
||||
#### Build 2107011
|
||||
|
||||
- Added MQTT button feedback option (PR #2011)
|
||||
|
||||
#### Build 2107010
|
||||
|
||||
- Added JSON IR codes (PR #1941)
|
||||
- Adjusted the width of WiFi and LED settings input fields
|
||||
- Fixed a minor visual issue with slider trail not reaching thumb on low values
|
||||
|
||||
#### Build 2106302
|
||||
|
||||
- Fixed settings page broken by using "%" in input fields
|
||||
@@ -244,6 +422,7 @@
|
||||
- Added support for WESP32 ethernet board (PR #1764)
|
||||
- Added Caching for main UI (PR #1704)
|
||||
- Added Tetrix mode (PR #1729)
|
||||
- Removed Merry Christmas mode (use "Chase 2" - called Running 2 before 0.13.0)
|
||||
- Added memory check on Bus creation
|
||||
|
||||
#### Build 2102050
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.0-b0",
|
||||
"version": "0.13.0-b6",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.0-b0",
|
||||
"version": "0.13.0-b6",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
||||
@@ -32,7 +32,7 @@ def bin_rename_copy(source, target, env):
|
||||
|
||||
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
|
||||
|
||||
if release_name and os.getenv("WLED_RELEASE"):
|
||||
if release_name:
|
||||
_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)
|
||||
|
||||
265
platformio.ini
265
platformio.ini
@@ -8,15 +8,19 @@
|
||||
# Please uncomment one of the lines below to select your board(s)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Travis CI binaries (comment this out with a ';' when building for your own board)
|
||||
;default_envs = travis_esp8266, travis_esp32
|
||||
# Travis CI binaries (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
|
||||
; default_envs = travis_esp8266, travis_esp32
|
||||
|
||||
# Release binaries
|
||||
default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth
|
||||
|
||||
# Build everything
|
||||
; default_envs = esp32dev, esp8285_4CH_MagicHome, esp8285_4CH_H801, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_5CH_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
|
||||
|
||||
# Single binaries (uncomment your board)
|
||||
; default_envs = elekstube_ips
|
||||
; default_envs = nodemcuv2
|
||||
; default_envs = esp8266_2m
|
||||
; default_envs = esp01_1m_full
|
||||
; default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
@@ -73,7 +77,6 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
|
||||
# ldscript_512k ( 512 KB) = 487 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
|
||||
# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
|
||||
# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved
|
||||
# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota?
|
||||
@@ -129,28 +132,6 @@ ldscript_2m512k = eagle.flash.2m512.ld
|
||||
ldscript_2m1m = eagle.flash.2m1m.ld
|
||||
ldscript_4m1m = eagle.flash.4m1m.ld
|
||||
|
||||
[esp8266]
|
||||
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
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; lwIP 1.4 - Higher Bandwidth (Aircoookie has)
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
; VTABLES in Flash
|
||||
-DVTABLES_IN_FLASH
|
||||
; restrict to minimal mime-types
|
||||
-DMIMETYPE_MINIMAL
|
||||
|
||||
[esp32]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
|
||||
[scripts_defaults]
|
||||
extra_scripts =
|
||||
pre:pio-scripts/set_version.py
|
||||
@@ -179,12 +160,8 @@ upload_speed = 115200
|
||||
# ------------------------------------------------------------------------------
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.3.2
|
||||
NeoPixelBus @ 2.6.0
|
||||
ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncUDP
|
||||
AsyncTCP @ 1.0.3
|
||||
IRremoteESP8266 @ 2.7.3
|
||||
fastled/FastLED @ 3.4.0
|
||||
IRremoteESP8266 @ 2.7.18
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
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
|
||||
@@ -192,19 +169,66 @@ lib_deps =
|
||||
#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
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
[esp8266]
|
||||
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
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; lwIP 1.4 - Higher Bandwidth (Aircoookie has)
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
; VTABLES in Flash
|
||||
-DVTABLES_IN_FLASH
|
||||
; restrict to minimal mime-types
|
||||
-DMIMETYPE_MINIMAL
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
# ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncUDP
|
||||
makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 and newer do not compile on ESP core < 3.0.0
|
||||
|
||||
[esp32]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32s2]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
-DCONFIG_IDF_TARGET_ESP32S2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-DCO
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# WLED BUILDS
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -216,6 +240,14 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8266_2m]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp01_1m_full]
|
||||
board = esp01_1m
|
||||
@@ -224,6 +256,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp07]
|
||||
board = esp07
|
||||
@@ -232,6 +265,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
@@ -241,6 +275,7 @@ upload_speed = 921600
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:heltec_wifi_kit_8]
|
||||
@@ -250,6 +285,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:h803wf]
|
||||
board = d1_mini
|
||||
@@ -258,25 +294,37 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
platform = espressif32@2.0
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32_eth]
|
||||
board = esp32-poe
|
||||
platform = espressif32@3.2
|
||||
platform = espressif32@2.0
|
||||
upload_speed = 921600
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32s2_saola]
|
||||
board = esp32dev
|
||||
board_build.mcu = esp32s2
|
||||
platform = espressif32
|
||||
platform_packages =
|
||||
toolchain-xtensa32s2
|
||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.0-alpha1
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
@@ -285,6 +333,7 @@ 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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_4CH_H801]
|
||||
board = esp8285
|
||||
@@ -293,6 +342,7 @@ 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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_5CH_H801]
|
||||
board = esp8285
|
||||
@@ -301,6 +351,7 @@ 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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
board = d1_mini
|
||||
@@ -309,6 +360,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# DEVELOPMENT BOARDS
|
||||
@@ -322,6 +374,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
board = d1_mini
|
||||
@@ -333,6 +386,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
board = d1_mini
|
||||
@@ -341,105 +395,59 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# custom board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:custom_LEDPIN_4]
|
||||
board = d1_mini
|
||||
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 LEDPIN=4 -D IRPIN=5
|
||||
|
||||
[env:custom_LEDPIN_16]
|
||||
board = d1_mini
|
||||
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 LEDPIN=16
|
||||
|
||||
|
||||
[env:custom_LEDPIN_3]
|
||||
board = d1_mini
|
||||
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 LEDPIN=3
|
||||
|
||||
[env:custom_APA102]
|
||||
board = d1_mini
|
||||
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 USE_APA102
|
||||
|
||||
[env:custom_WS2801]
|
||||
board = d1_mini
|
||||
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 USE_WS2801
|
||||
|
||||
[env:custom32_LEDPIN_16]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
|
||||
[env:custom32_APA102]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D USE_APA102
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
|
||||
[env:custom32_TOUCHPIN_T0]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D TOUCHPIN=T0
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
upload_port = /dev/cu.SLAB_USBtoUART
|
||||
monitor_port = /dev/cu.SLAB_USBtoUART
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19 -D BTNPIN=17
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D LEDPIN=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D IRPIN=18
|
||||
-D UWLED_USE_MY_CONFIG
|
||||
-D USERMOD_DALLASTEMPERATURE
|
||||
-D USERMOD_FOUR_LINE_DISPLAY
|
||||
-D TEMPERATURE_PIN=23
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
OneWire@~2.3.5
|
||||
olikraus/U8g2 @ ^2.28.8
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:m5atom]
|
||||
board = esp32dev
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
platform = espressif32@3.2
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:sp501e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:athom7w]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# travis test board configurations
|
||||
@@ -469,6 +477,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0.6-rev2]
|
||||
board = esp_wroom_02
|
||||
@@ -477,6 +486,7 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EleksTube-IPS
|
||||
@@ -485,8 +495,6 @@ build_flags = ${common.build_flags_esp8266}
|
||||
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
|
||||
@@ -508,6 +516,7 @@ build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_D
|
||||
-D SPI_FREQUENCY=40000000
|
||||
-D USER_SETUP_LOADED
|
||||
monitor_filters = esp32_exception_decoder
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
TFT_eSPI @ ^2.3.70
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
@@ -12,6 +12,7 @@ board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
; *********************************************************************
|
||||
@@ -43,4 +44,4 @@ build_flags = ${common.build_flags_esp8266}
|
||||
; 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
|
||||
; for the BW-LT11 controller use PINs 12,4,14,5
|
||||
|
||||
10
readme.md
10
readme.md
@@ -4,12 +4,12 @@
|
||||
<a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/wled?color=blue&style=flat-square"></a>
|
||||
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
|
||||
<a href="https://discord.gg/KuqP7NE"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
|
||||
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
|
||||
<a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
# Welcome to my project WLED! ✨
|
||||
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
|
||||
@@ -49,9 +49,9 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
|
||||
|
||||
## 📲 Quick start guide and documentation
|
||||
|
||||
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
|
||||
See the [documentation on our official site](https://kno.wled.ge)!
|
||||
|
||||
[On this page](https://github.com/Aircoookie/WLED/wiki/Learning-the-ropes) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running!
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running!
|
||||
|
||||
## 🖼️ Images
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
@@ -82,7 +82,7 @@ Any | 5v 3-pin ARGB for PC | Any PC RGB device that supports the 5v 3-pin ARGB m
|
||||
## ✌️ Other
|
||||
|
||||
Licensed under the MIT license
|
||||
Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)!
|
||||
Credits [here](https://kno.wled.ge/about/contributors/)!
|
||||
|
||||
Uses Linearicons by Perxis!
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ function writeHtmlGzipped(sourceFile, resultFile) {
|
||||
* Binary array for the Web UI.
|
||||
* gzip is used for smaller size and improved speeds.
|
||||
*
|
||||
* Please see https://github.com/Aircoookie/WLED/wiki/Add-own-functionality#web-ui
|
||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
|
||||
@@ -175,7 +175,7 @@ function writeChunks(srcDir, specs, resultFile) {
|
||||
let src = `/*
|
||||
* More web UI HTML source arrays.
|
||||
* This file is auto generated, please don't make any changes manually.
|
||||
* Instead, see https://github.com/Aircoookie/WLED/wiki/Add-own-functionality#web-ui
|
||||
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
`;
|
||||
|
||||
16
tools/multi-update.cmd
Normal file
16
tools/multi-update.cmd
Normal file
@@ -0,0 +1,16 @@
|
||||
@echo off
|
||||
SETLOCAL
|
||||
SET FWPATH=c:\path\to\your\WLED\build_output\firmware
|
||||
GOTO ESPS
|
||||
|
||||
:UPDATEONE
|
||||
IF NOT EXIST %FWPATH%\%2 GOTO SKIP
|
||||
ping -w 1000 -n 1 %1 | find "TTL=" || GOTO SKIP
|
||||
ECHO Updating %1
|
||||
curl -s -F "update=@%FWPATH%/%2" %1/update >nul
|
||||
:SKIP
|
||||
GOTO:EOF
|
||||
|
||||
:ESPS
|
||||
call :UPDATEONE 192.168.x.x firmware.bin
|
||||
call :UPDATEONE ....
|
||||
19
tools/multi-update.sh
Normal file
19
tools/multi-update.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
FWPATH=/path/to/your/WLED/build_output/firmware
|
||||
|
||||
update_one() {
|
||||
if [ -f $FWPATH/$2 ]; then
|
||||
ping -c 1 $1 >/dev/null
|
||||
PINGRESULT=$?
|
||||
if [ $PINGRESULT -eq 0 ]; then
|
||||
echo Updating $1
|
||||
curl -s -F "update=@${FWPATH}/$2" $1/update >/dev/null
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_one 192.168.x.x firmware.bin
|
||||
update_one 192.168.x.x firmware.bin
|
||||
# ...
|
||||
@@ -123,7 +123,7 @@ class Animated_Staircase : public Usermod {
|
||||
// Always mark segments as "transitional", we are animating the staircase
|
||||
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1);
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -298,7 +298,7 @@ class Animated_Staircase : public Usermod {
|
||||
}
|
||||
segments->setOption(SEG_OPTION_ON, 1, 1);
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
DEBUG_PRINTLN(F("Animated Staircase disabled."));
|
||||
}
|
||||
enabled = enable;
|
||||
@@ -306,22 +306,26 @@ class Animated_Staircase : public Usermod {
|
||||
|
||||
public:
|
||||
void setup() {
|
||||
// standardize invalid pin numbers to -1
|
||||
if (topPIRorTriggerPin < 0) topPIRorTriggerPin = -1;
|
||||
if (topEchoPin < 0) topEchoPin = -1;
|
||||
if (bottomPIRorTriggerPin < 0) bottomPIRorTriggerPin = -1;
|
||||
if (bottomEchoPin < 0) bottomEchoPin = -1;
|
||||
// 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;
|
||||
PinManagerPinType pins[4] = {
|
||||
{ topPIRorTriggerPin, useUSSensorTop },
|
||||
{ topEchoPin, false },
|
||||
{ bottomPIRorTriggerPin, useUSSensorBottom },
|
||||
{ bottomEchoPin, false },
|
||||
};
|
||||
// NOTE: this *WILL* return TRUE if all the pins are set to -1.
|
||||
// this is *BY DESIGN*.
|
||||
if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
|
||||
topPIRorTriggerPin = -1;
|
||||
topEchoPin = -1;
|
||||
bottomPIRorTriggerPin = -1;
|
||||
bottomEchoPin = -1;
|
||||
enabled = false;
|
||||
}
|
||||
enable(enabled);
|
||||
initDone = true;
|
||||
@@ -480,10 +484,10 @@ class Animated_Staircase : public Usermod {
|
||||
(oldBottomAPin != bottomPIRorTriggerPin) ||
|
||||
(oldBottomBPin != bottomEchoPin)) {
|
||||
changed = true;
|
||||
pinManager.deallocatePin(oldTopAPin);
|
||||
pinManager.deallocatePin(oldTopBPin);
|
||||
pinManager.deallocatePin(oldBottomAPin);
|
||||
pinManager.deallocatePin(oldBottomBPin);
|
||||
pinManager.deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
|
||||
pinManager.deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
|
||||
pinManager.deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
|
||||
pinManager.deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
|
||||
}
|
||||
if (changed) setup();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ void RGBNET_readValues() {
|
||||
int channel = UDP.read();
|
||||
|
||||
//channel data is not used we only supports one channel
|
||||
int len = UDP.read(RGBNET_packet, ledCount*3);
|
||||
int len = UDP.read(RGBNET_packet, strip.getLengthTotal()*3);
|
||||
if(len==0){
|
||||
return;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ void handleConfig(AsyncWebServerRequest *request)
|
||||
\"channels\": [\
|
||||
{\
|
||||
\"channel\": 1,\
|
||||
\"leds\": " + ledCount + "\
|
||||
\"leds\": " + strip.getLengthTotal() + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 2,\
|
||||
|
||||
16
usermods/BH1750_v2/platformio_override.ini
Normal file
16
usermods/BH1750_v2/platformio_override.ini
Normal file
@@ -0,0 +1,16 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_BH1750 - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - the max number of milliseconds between measurements, defaults to 10000ms
|
||||
; USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL - the min number of milliseconds between measurements, defaults to 500ms
|
||||
; USERMOD_BH1750_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
; USERMOD_BH1750_OFFSET_VALUE - the offset value to report on, defaults to 1
|
||||
;
|
||||
[env:usermod_BH1750_d1_mini]
|
||||
extends = env:d1_mini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BH1750
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
claws/BH1750 @ ^1.2.0
|
||||
24
usermods/BH1750_v2/readme.md
Normal file
24
usermods/BH1750_v2/readme.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# BH1750 usermod
|
||||
|
||||
This usermod will read from an ambient light sensor like the BH1750 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_BH1750` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
||||
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
||||
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
||||
|
||||
All parameters can be configured at runtime using Usermods settings page.
|
||||
|
||||
### 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_BH1750_d1_mini`.
|
||||
|
||||
## Change Log
|
||||
177
usermods/BH1750_v2/usermod_bh1750.h
Normal file
177
usermods/BH1750_v2/usermod_bh1750.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <Wire.h>
|
||||
#include <BH1750.h>
|
||||
|
||||
// the max frequency to check photoresistor, 10 seconds
|
||||
#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000
|
||||
#endif
|
||||
|
||||
// the min frequency to check photoresistor, 500 ms
|
||||
#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500
|
||||
#endif
|
||||
|
||||
// how many seconds after boot to take first measurement, 10 seconds
|
||||
#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT
|
||||
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
|
||||
#endif
|
||||
|
||||
// only report if differance grater than offset value
|
||||
#ifndef USERMOD_BH1750_OFFSET_VALUE
|
||||
#define USERMOD_BH1750_OFFSET_VALUE 1
|
||||
#endif
|
||||
|
||||
class Usermod_BH1750 : public Usermod
|
||||
{
|
||||
private:
|
||||
int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
|
||||
|
||||
unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
|
||||
unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
|
||||
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
||||
unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
||||
// flag to indicate we have finished the first readLightLevel call
|
||||
// allows this library to report to the user how long until the first
|
||||
// measurement
|
||||
bool getLuminanceComplete = false;
|
||||
|
||||
// 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 _maxReadInterval[];
|
||||
static const char _minReadInterval[];
|
||||
static const char _offset[];
|
||||
|
||||
BH1750 lightMeter;
|
||||
float lastLux = -1000;
|
||||
|
||||
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
{
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Wire.begin();
|
||||
lightMeter.begin();
|
||||
}
|
||||
|
||||
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 < minReadingInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||
|
||||
float lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
|
||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||
{
|
||||
lastLux = lux;
|
||||
lastSend = millis();
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
char subuf[45];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/luminance"));
|
||||
mqtt->publish(subuf, 0, true, String(lux).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_json = 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_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux_json.add(F(" sec until read"));
|
||||
return;
|
||||
}
|
||||
|
||||
lux_json.add(lastLux);
|
||||
lux_json.add(F(" lx"));
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BH1750;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
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);
|
||||
maxReadingInterval = (top[FPSTR(_maxReadInterval)] | maxReadingInterval); // ms
|
||||
minReadingInterval = (top[FPSTR(_minReadInterval)] | minReadingInterval); // ms
|
||||
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_BH1750::_name[] PROGMEM = "BH1750";
|
||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
|
||||
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
|
||||
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
|
||||
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";
|
||||
14
usermods/BH1750_v2/usermods_list.cpp
Normal file
14
usermods/BH1750_v2/usermods_list.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
*/
|
||||
#ifdef USERMOD_BH1750
|
||||
#include "../usermods/BH1750_v2/usermod_BH1750.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
#ifdef USERMOD_BH1750
|
||||
usermods.add(new Usermod_BH1750());
|
||||
#endif
|
||||
}
|
||||
@@ -23,12 +23,20 @@
|
||||
//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;
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool testBool = false;
|
||||
unsigned long testULong = 42424242;
|
||||
float testFloat = 42.42;
|
||||
String testString = "Forty-Two";
|
||||
|
||||
// These config variables have defaults set inside readFromConfig()
|
||||
int testInt;
|
||||
long testLong;
|
||||
int8_t testPins[2];
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
@@ -118,40 +126,85 @@ class MyExampleUsermod : public Usermod {
|
||||
* 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.
|
||||
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
|
||||
*
|
||||
* Usermod Settings Overview:
|
||||
* - Numeric values are treated as floats in the browser.
|
||||
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
|
||||
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
|
||||
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
|
||||
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
|
||||
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
|
||||
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
|
||||
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
|
||||
* used in the Usermod when reading the value from ArduinoJson.
|
||||
* - Pin values can be treated differently from an integer value by using the key name "pin"
|
||||
* - "pin" can contain a single or array of integer values
|
||||
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
|
||||
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
|
||||
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
|
||||
*
|
||||
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
|
||||
*
|
||||
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
|
||||
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
|
||||
*
|
||||
* 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("exampleUsermod");
|
||||
top["great"] = userVar0; //save this var persistently whenever settings are saved
|
||||
top["great"] = userVar0; //save these vars persistently whenever settings are saved
|
||||
top["testBool"] = testBool;
|
||||
top["testInt"] = testInt;
|
||||
top["testLong"] = testLong;
|
||||
top["testULong"] = testULong;
|
||||
top["testFloat"] = testFloat;
|
||||
top["testString"] = testString;
|
||||
JsonArray pinArray = top.createNestedArray("pin");
|
||||
pinArray.add(testPins[0]);
|
||||
pinArray.add(testPins[1]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
|
||||
*
|
||||
* 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
|
||||
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
|
||||
*
|
||||
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
|
||||
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
|
||||
*
|
||||
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
//set defaults for variables when declaring the variable (class definition or constructor)
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root["exampleUsermod"];
|
||||
if (!top.isNull()) return false;
|
||||
|
||||
userVar0 = top["great"] | userVar0;
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
configComplete &= getJsonValue(top["great"], userVar0);
|
||||
configComplete &= getJsonValue(top["testBool"], testBool);
|
||||
configComplete &= getJsonValue(top["testULong"], testULong);
|
||||
configComplete &= getJsonValue(top["testFloat"], testFloat);
|
||||
configComplete &= getJsonValue(top["testString"], testString);
|
||||
|
||||
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
|
||||
configComplete &= getJsonValue(top["testInt"], testInt, 42);
|
||||
configComplete &= getJsonValue(top["testLong"], testLong, -42424242);
|
||||
configComplete &= getJsonValue(top["pin"][0], testPins[0], -1);
|
||||
configComplete &= getJsonValue(top["pin"][1], testPins[1], -1);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -58,12 +58,12 @@ public:
|
||||
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); }
|
||||
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); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
119
usermods/JSON_IR_remote/21-key_ir.json
Normal file
119
usermods/JSON_IR_remote/21-key_ir.json
Normal file
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"desc": "21-key",
|
||||
"0xFFA25D": {
|
||||
"label": "On",
|
||||
"pos": "1x1",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF629D": {
|
||||
"label": "Off",
|
||||
"pos": "1x2",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFFE21D": {
|
||||
"label": "Flash",
|
||||
"pos": "1x3",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "Strobe",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "Fade",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFFC23D": {
|
||||
"label": "Smooth",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Aurora",
|
||||
"cmd": "CY=0&FX=38"
|
||||
},
|
||||
"0xFFE01F": {
|
||||
"label": "Bright +",
|
||||
"pos": "3x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "Bright -",
|
||||
"pos": "3x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "White",
|
||||
"pos": "3x3",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "Red",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Green",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Blue",
|
||||
"pos": "4x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Tomato",
|
||||
"pos": "5x1",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "LightGreen",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF7A85": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "5x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Orange",
|
||||
"pos": "6x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Aqua",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=hFFFF&C2=h7FFF&C3=h39A895"
|
||||
},
|
||||
"0xFF5AA5": {
|
||||
"label": "Purple",
|
||||
"pos": "6x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF42BD": {
|
||||
"label": "Yellow",
|
||||
"pos": "7x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
|
||||
},
|
||||
"0xFF4AB5": {
|
||||
"label": "Cyan",
|
||||
"pos": "7x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFF52AD": {
|
||||
"label": "Pink",
|
||||
"pos": "7x3",
|
||||
"cmd": "FP=5&CL=hFFC0CB&C2=hFFD4C0&C3=hA88C96"
|
||||
}
|
||||
}
|
||||
147
usermods/JSON_IR_remote/24-key_ir.json
Normal file
147
usermods/JSON_IR_remote/24-key_ir.json
Normal file
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"desc": "24-key",
|
||||
"0xF700FF": {
|
||||
"label": "+",
|
||||
"pos": "1x1",
|
||||
"cmnt": "Speed +",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xF7807F": {
|
||||
"label": "-",
|
||||
"pos": "1x2",
|
||||
"cmnt": "Speed -",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xF740BF": {
|
||||
"label": "On/Off",
|
||||
"pos": "1x3",
|
||||
"cmnt": "Toggle On/Off",
|
||||
"cmd": "T=2"
|
||||
},
|
||||
"0xF7C03F": {
|
||||
"label": "W",
|
||||
"pos": "1x4",
|
||||
"cmnt": "Cycle color palette",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xF720DF": {
|
||||
"label": "R",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xF7A05F": {
|
||||
"label": "G",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xF7609F": {
|
||||
"label": "B",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xF7E01F": {
|
||||
"label": "Bright -",
|
||||
"pos": "2x4",
|
||||
"cmnt": "Bright -",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xF710EF": {
|
||||
"label": "Timer1H",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Timer 60 min",
|
||||
"cmd": "NL=60&NT=0"
|
||||
},
|
||||
"0xF7906F": {
|
||||
"label": "Timer4H",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Timer 30 min",
|
||||
"cmd": "NL=30&NT=0"
|
||||
},
|
||||
"0xF750AF": {
|
||||
"label": "Timer8H",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Timer 15 min",
|
||||
"cmd": "NL=15&NT=0"
|
||||
},
|
||||
"0xF7D02F": {
|
||||
"label": "Bright128",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Bright 128",
|
||||
"cmd": "A=128"
|
||||
},
|
||||
"0xF730CF": {
|
||||
"label": "Music1",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Cycle FX +",
|
||||
"cmd": "FX=~"
|
||||
},
|
||||
"0xF7B04F": {
|
||||
"label": "Music2",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Cycle FX -",
|
||||
"cmd": "FX=~-1"
|
||||
},
|
||||
"0xF7708F": {
|
||||
"label": "Music3",
|
||||
"pos": "4x3",
|
||||
"cmnt": "Reset FX and FP",
|
||||
"cmd": "FX=1&PF=6"
|
||||
},
|
||||
"0xF7F00F": {
|
||||
"label": "Bright +",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Bright +",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xF708F7": {
|
||||
"label": "Mode1",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Preset 1",
|
||||
"cmd": "PL=1"
|
||||
},
|
||||
"0xF78877": {
|
||||
"label": "Mode2",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Preset 2",
|
||||
"cmd": "PL=2"
|
||||
},
|
||||
"0xF748B7": {
|
||||
"label": "Mode3",
|
||||
"pos": "5x3",
|
||||
"cmnt": "Preset 3",
|
||||
"cmd": "PL=3"
|
||||
},
|
||||
"0xF7C837": {
|
||||
"label": "Up",
|
||||
"pos": "5x4",
|
||||
"cmnt": "Intensity +",
|
||||
"cmd": "IX=~16"
|
||||
},
|
||||
"0xF728D7": {
|
||||
"label": "Mode4",
|
||||
"pos": "6x1",
|
||||
"cmnt": "Preset 4",
|
||||
"cmd": "PL=4"
|
||||
},
|
||||
"0xF7A857": {
|
||||
"label": "Mode5",
|
||||
"pos": "6x2",
|
||||
"cmnt": "Preset 5",
|
||||
"cmd": "PL=5"
|
||||
},
|
||||
"0xF76897": {
|
||||
"label": "Cycle",
|
||||
"pos": "6x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=1&PT=60000"
|
||||
},
|
||||
"0xF7E817": {
|
||||
"label": "Down",
|
||||
"pos": "6x4",
|
||||
"cmnt": "Intensity -",
|
||||
"cmd": "IX=~-16"
|
||||
}
|
||||
}
|
||||
185
usermods/JSON_IR_remote/32-key_ir.json
Normal file
185
usermods/JSON_IR_remote/32-key_ir.json
Normal file
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"desc": "32-key",
|
||||
"0xFF08F7": {
|
||||
"label": "On",
|
||||
"pos": "1x1",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFFC03F": {
|
||||
"label": "Off",
|
||||
"pos": "1x2",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF807F": {
|
||||
"label": "Auto",
|
||||
"pos": "1x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFF609F": {
|
||||
"label": "Mode",
|
||||
"pos": "1x4",
|
||||
"cmnt": "Cycle effects",
|
||||
"cmd": "FX=~&CY=0"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "4H",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Timer 60min",
|
||||
"cmd": "NL=60&NT=0"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "6H",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Timer 90min",
|
||||
"cmd": "NL=90&NT=0"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "8H",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Timer 120min",
|
||||
"cmd": "NL=120&NT=0"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Timer Off",
|
||||
"pos": "2x4",
|
||||
"cmd": "NL=0"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Red",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "Green",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "Blue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "White",
|
||||
"pos": "3x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFE4CD&C3=hE4E4FF"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "OrangeRed",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "SeaGreen",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "RoyalBlue",
|
||||
"pos": "4x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "DarkBlue",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "Orange",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "YellowGreen",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Aurora",
|
||||
"cmd": "FP=37"
|
||||
},
|
||||
"0xFF20DF": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "5x3",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFF00FF": {
|
||||
"label": "Orchid",
|
||||
"pos": "5x4",
|
||||
"cmd": "FP=5&CL=hDA70D6&C2=hDA70A0&C3=h89618F"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "DarkGreen",
|
||||
"pos": "6x2",
|
||||
"cmnt": "Orange and Teal",
|
||||
"cmd": "FP=44"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "RebeccaPurple",
|
||||
"pos": "6x3",
|
||||
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Plum",
|
||||
"pos": "6x4",
|
||||
"cmd": "FP=5&CL=hDDA0DD&C2=hDDA0BE&C3=h8D7791"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Strobe",
|
||||
"pos": "7x1",
|
||||
"cmnt": "Dancing Shadows",
|
||||
"cmd": "FX=112&CY=0"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "In Waves",
|
||||
"pos": "7x2",
|
||||
"cmnt": "Noise 1",
|
||||
"cmd": "FX=70&CY=0"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Speed +",
|
||||
"pos": "7x3",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Speed -",
|
||||
"pos": "7x4",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xFF40BF": {
|
||||
"label": "Jump",
|
||||
"pos": "8x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "FX=74&CY=0"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "Fade",
|
||||
"pos": "8x2",
|
||||
"cmnt": "Sunrise",
|
||||
"cmd": "FX=104&CY=0"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Flash",
|
||||
"pos": "8x3",
|
||||
"cmnt": "Railway",
|
||||
"cmd": "FX=78&CY=0"
|
||||
},
|
||||
"0xFFA05F": {
|
||||
"label": "Chase Flash",
|
||||
"pos": "8x4",
|
||||
"cmnt": "Washing Machine",
|
||||
"cmd": "FX=113&CY=0"
|
||||
}
|
||||
}
|
||||
233
usermods/JSON_IR_remote/40-key-black_ir.json
Normal file
233
usermods/JSON_IR_remote/40-key-black_ir.json
Normal file
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"desc": "40-key-black",
|
||||
"0xFF3AC5": {
|
||||
"label": "Bright +",
|
||||
"pos": "1x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFBA45": {
|
||||
"label": "Bright -",
|
||||
"pos": "1x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF827D": {
|
||||
"label": "Off",
|
||||
"pos": "1x3",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "On",
|
||||
"pos": "1x4",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF1AE5": {
|
||||
"label": "Red",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9A65": {
|
||||
"label": "Green",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFA25D": {
|
||||
"label": "Blue",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "White",
|
||||
"pos": "2x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Tomato",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Yelmag",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFFAA55": {
|
||||
"label": "LightGreen",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF926D": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "WarmWhite",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Warm White",
|
||||
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
|
||||
},
|
||||
"0xFF0AF5": {
|
||||
"label": "OrangeRed",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF8A75": {
|
||||
"label": "Cyan",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "RebeccaPurple",
|
||||
"pos": "4x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "CoolWhite",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Cool White",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Orange",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "Turquoise",
|
||||
"pos": "5x2",
|
||||
"cmd": "FP=5&CL=h40E0D0&C2=h40A0E0&C3=h4E9381"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "Purple",
|
||||
"pos": "5x3",
|
||||
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "MedGray",
|
||||
"pos": "5x4",
|
||||
"cmnt": "Cycle palette +",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=h7FFF00&C3=hA89539"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "DarkCyan",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=h8B8B&C2=h458B&C3=h1F5B51"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Plum",
|
||||
"pos": "6x3",
|
||||
"cmnt": "Magenta",
|
||||
"cmd": "FP=40"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "DarkGray",
|
||||
"pos": "6x4",
|
||||
"cmnt": "Cycle palette -",
|
||||
"cmd": "FP=~-"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "Jump3",
|
||||
"pos": "7x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "CY=0&FX=74"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "Fade3",
|
||||
"pos": "7x2",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "Flash",
|
||||
"pos": "7x3",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "Quick",
|
||||
"pos": "7x4",
|
||||
"cmnt": "Fx speed +16",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF08F7": {
|
||||
"label": "Jump7",
|
||||
"pos": "8x1",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "Fade7",
|
||||
"pos": "8x2",
|
||||
"cmnt": "Lighthouse",
|
||||
"cmd": "CY=0&FX=41"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "Auto",
|
||||
"pos": "8x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFFC837": {
|
||||
"label": "Slow",
|
||||
"pos": "8x4",
|
||||
"cmnt": "FX speed -16",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Custom1",
|
||||
"pos": "9x1",
|
||||
"cmnt": "Noise 1",
|
||||
"cmd": "CY=0&FX=70"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Custom2",
|
||||
"pos": "9x2",
|
||||
"cmnt": "Dancing Shadows",
|
||||
"cmd": "CY=0&FX=112"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "Music +",
|
||||
"pos": "9x3",
|
||||
"cmnt": "FX Intensity +16",
|
||||
"cmd": "IX=~16"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Timer60",
|
||||
"pos": "9x4",
|
||||
"cmnt": "Timer 60 min",
|
||||
"cmd": "NL=60&NT=0"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Custom3",
|
||||
"pos": "10x1",
|
||||
"cmnt": "Twinklefox",
|
||||
"cmd": "CY=0&FX=80"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "Custom4",
|
||||
"pos": "10x2",
|
||||
"cmnt": "Twinklecat",
|
||||
"cmd": "CY=0&FX=81"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Music -",
|
||||
"pos": "10x3",
|
||||
"cmnt": "FX Intesity -16",
|
||||
"cmd": "IX=~-16"
|
||||
},
|
||||
"0xFFD02F": {
|
||||
"label": "Timer120",
|
||||
"pos": "10x4",
|
||||
"cmnt": "Timer 120 min",
|
||||
"cmd": "NL=120&NT=0"
|
||||
}
|
||||
}
|
||||
217
usermods/JSON_IR_remote/40-key-blue_ir.json
Normal file
217
usermods/JSON_IR_remote/40-key-blue_ir.json
Normal file
@@ -0,0 +1,217 @@
|
||||
{
|
||||
"desc": "40-key-blue",
|
||||
"0xFF3AC5": {
|
||||
"label": "Bright +",
|
||||
"pos": "1x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFBA45": {
|
||||
"label": "Bright -",
|
||||
"pos": "1x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF827D": {
|
||||
"label": "Off",
|
||||
"pos": "1x3",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "On",
|
||||
"pos": "1x4",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF1AE5": {
|
||||
"label": "Red",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9A65": {
|
||||
"label": "Green",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFA25D": {
|
||||
"label": "Blue",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "White",
|
||||
"pos": "2x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Tomato",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Yelmag",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFFAA55": {
|
||||
"label": "LightGreen",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF926D": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "WarmWhite",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Warm White",
|
||||
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
|
||||
},
|
||||
"0xFF0AF5": {
|
||||
"label": "OrangeRed",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF8A75": {
|
||||
"label": "Cyan",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "RebeccaPurple",
|
||||
"pos": "4x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "CoolWhite",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Cool White",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Orange",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "Turquoise",
|
||||
"pos": "5x2",
|
||||
"cmd": "FP=5&CL=h40E0D0&C2=h40A0E0&C3=h4E9381"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "Purple",
|
||||
"pos": "5x3",
|
||||
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "MedGray",
|
||||
"pos": "5x4",
|
||||
"cmnt": "Cycle palette +",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=h7FFF00&C3=hA89539"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "DarkCyan",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=h8B8B&C2=h458B&C3=h1F5B51"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Plum",
|
||||
"pos": "6x3",
|
||||
"cmnt": "Magenta",
|
||||
"cmd": "FP=40"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "DarkGray",
|
||||
"pos": "6x4",
|
||||
"cmnt": "Cycle palette -",
|
||||
"cmd": "FP=~-"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "W +",
|
||||
"pos": "7x1"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "W -",
|
||||
"pos": "7x2"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "W On",
|
||||
"pos": "7x3"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "W Off",
|
||||
"pos": "7x4"
|
||||
},
|
||||
"0xFF08F7": {
|
||||
"label": "W25",
|
||||
"pos": "8x1"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "W50",
|
||||
"pos": "8x2"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "W75",
|
||||
"pos": "8x3"
|
||||
},
|
||||
"0xFFC837": {
|
||||
"label": "W100",
|
||||
"pos": "8x4"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Jump3",
|
||||
"pos": "9x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "CY=0&FX=74"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Fade3",
|
||||
"pos": "9x2",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "Jump7",
|
||||
"pos": "9x3",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Quick",
|
||||
"pos": "9x4",
|
||||
"cmnt": "Fx speed +16",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Fade",
|
||||
"pos": "10x1",
|
||||
"cmnt": "Lighthouse",
|
||||
"cmd": "CY=0&FX=41"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "Flash",
|
||||
"pos": "10x2",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Auto",
|
||||
"pos": "10x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFFD02F": {
|
||||
"label": "Slow",
|
||||
"pos": "10x4",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
}
|
||||
}
|
||||
241
usermods/JSON_IR_remote/44-key_ir.json
Normal file
241
usermods/JSON_IR_remote/44-key_ir.json
Normal file
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"desc": "44-key",
|
||||
"0xFF3AC5": {
|
||||
"label": "Bright +",
|
||||
"pos": "1x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFBA45": {
|
||||
"label": "Bright -",
|
||||
"pos": "1x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF827D": {
|
||||
"label": "Off",
|
||||
"pos": "1x3",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "On",
|
||||
"pos": "1x4",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF1AE5": {
|
||||
"label": "Red",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9A65": {
|
||||
"label": "Green",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFA25D": {
|
||||
"label": "Blue",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "White",
|
||||
"pos": "2x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Tomato",
|
||||
"pos": "3x1",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFFAA55": {
|
||||
"label": "LightGreen",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF926D": {
|
||||
"label": "DeepBlue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "Warmwhite2",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Warm White",
|
||||
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
|
||||
},
|
||||
"0xFF0AF5": {
|
||||
"label": "Orange",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF8A75": {
|
||||
"label": "Turquoise",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "Purple",
|
||||
"pos": "4x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "WarmWhite",
|
||||
"pos": "4x4",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Yellowish",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "Cyan",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "Magenta",
|
||||
"pos": "5x3",
|
||||
"cmd": "FP=5&CL=hFF00FF&C2=hFF007F&C3=h9539A8"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "ColdWhite",
|
||||
"pos": "5x4",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Aqua",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=hFFFF&C2=h7FFF&C3=h39A895"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Pink",
|
||||
"pos": "6x3",
|
||||
"cmd": "FP=5&CL=hFFC0CB&C2=hFFD4C0&C3=hA88C96"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "ColdWhite2",
|
||||
"pos": "6x4",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "Red +",
|
||||
"pos": "7x1",
|
||||
"cmd": "FP=5&R=~16"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "Green +",
|
||||
"pos": "7x2",
|
||||
"cmd": "FP=5&G=~16"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "Blue +",
|
||||
"pos": "7x3",
|
||||
"cmd": "FP=5&B=~16"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "Quick",
|
||||
"pos": "7x4",
|
||||
"cmnt": "Fx speed +16",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF08F7": {
|
||||
"label": "Red -",
|
||||
"pos": "8x1",
|
||||
"cmd": "FP=5&R=~-16"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "Green -",
|
||||
"pos": "8x2",
|
||||
"cmd": "FP=5&G=~-16"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "Blue -",
|
||||
"pos": "8x3",
|
||||
"cmd": "FP=5&B=~-16"
|
||||
},
|
||||
"0xFFC837": {
|
||||
"label": "Slow",
|
||||
"pos": "8x4",
|
||||
"cmnt": "FX speed -16",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Diy1",
|
||||
"pos": "9x1",
|
||||
"cmd": "CY=0&PL=1"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Diy2",
|
||||
"pos": "9x2",
|
||||
"cmd": "CY=0&PL=2"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "Diy3",
|
||||
"pos": "9x3",
|
||||
"cmd": "CY=0&PL=3"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Auto",
|
||||
"pos": "9x4",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Diy4",
|
||||
"pos": "10x1",
|
||||
"cmd": "CY=0&PL=4"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "Diy5",
|
||||
"pos": "10x2",
|
||||
"cmd": "CY=0&PL=5"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Diy6",
|
||||
"pos": "10x3",
|
||||
"cmd": "CY=0&PL=6"
|
||||
},
|
||||
"0xFFD02F": {
|
||||
"label": "Flash",
|
||||
"pos": "10x4",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFF20DF": {
|
||||
"label": "Jump3",
|
||||
"pos": "11x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "CY=0&FX=74"
|
||||
},
|
||||
"0xFFA05F": {
|
||||
"label": "Jump7",
|
||||
"pos": "11x2",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFF609F": {
|
||||
"label": "Fade3",
|
||||
"pos": "11x3",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFFE01F": {
|
||||
"label": "Fade7",
|
||||
"pos": "11x4",
|
||||
"cmnt": "Lighthouse",
|
||||
"cmd": "CY=0&FX=41"
|
||||
}
|
||||
}
|
||||
38
usermods/JSON_IR_remote/6-key_ir.json
Normal file
38
usermods/JSON_IR_remote/6-key_ir.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"desc": "6-key",
|
||||
"0xFF0FF0": {
|
||||
"label": "Power",
|
||||
"pos": "1x1",
|
||||
"cmd": "T=2"
|
||||
},
|
||||
"0xFF8F70": {
|
||||
"label": "Channel +",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Cycle palette up",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xFF4FB0": {
|
||||
"label": "Channel -",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Cycle palette down",
|
||||
"cmd": "FP=~-"
|
||||
},
|
||||
"0xFFCF30": {
|
||||
"label": "Volume +",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Brighten",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFF2FD0": {
|
||||
"label": "Volume -",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Dim",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFFAF50": {
|
||||
"label": "Mute",
|
||||
"pos": "6x1",
|
||||
"cmnt": "Cycle effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
}
|
||||
}
|
||||
47
usermods/JSON_IR_remote/9-key_ir.json
Normal file
47
usermods/JSON_IR_remote/9-key_ir.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"desc": "9-key",
|
||||
"0xFF629D": {
|
||||
"label": "Power",
|
||||
"cmd": "T=2"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "A",
|
||||
"cmnt": "Preset 1",
|
||||
"cmd": "PL=1"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "B",
|
||||
"cmnt": "Preset 2",
|
||||
"cmd": "PL=2"
|
||||
},
|
||||
"0xFFC23D": {
|
||||
"label": "C",
|
||||
"cmnt": "Preset 3",
|
||||
"cmd": "PL=3"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Left",
|
||||
"cmnt": "Speed -",
|
||||
"cmd": "SI=~-16"
|
||||
},
|
||||
"0xFF7A85": {
|
||||
"label": "Right",
|
||||
"cmnt": "Speed +",
|
||||
"cmd": "SI=~16"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Up",
|
||||
"cmnt": "Bright +",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Down",
|
||||
"cmnt": "Bright -",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Select",
|
||||
"cmnt": "Cycle effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
}
|
||||
}
|
||||
BIN
usermods/JSON_IR_remote/IR_Remote_Codes.xlsx
Normal file
BIN
usermods/JSON_IR_remote/IR_Remote_Codes.xlsx
Normal file
Binary file not shown.
108
usermods/JSON_IR_remote/ir_json_maker.py
Normal file
108
usermods/JSON_IR_remote/ir_json_maker.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import colorsys
|
||||
import json
|
||||
import openpyxl
|
||||
|
||||
named_colors = {'AliceBlue': '0xF0F8FF', 'AntiqueWhite': '0xFAEBD7', 'Aqua': '0x00FFFF',
|
||||
'Aquamarine': '0x7FFFD4', 'Azure': '0xF0FFFF', 'Beige': '0xF5F5DC', 'Bisque': '0xFFE4C4',
|
||||
'Black': '0x000000', 'BlanchedAlmond': '0xFFEBCD', 'Blue': '0x0000FF',
|
||||
'BlueViolet': '0x8A2BE2', 'Brown': '0xA52A2A', 'BurlyWood': '0xDEB887',
|
||||
'CadetBlue': '0x5F9EA0', 'Chartreuse': '0x7FFF00', 'Chocolate': '0xD2691E',
|
||||
'Coral': '0xFF7F50', 'CornflowerBlue': '0x6495ED', 'Cornsilk': '0xFFF8DC',
|
||||
'Crimson': '0xDC143C', 'Cyan': '0x00FFFF', 'DarkBlue': '0x00008B', 'DarkCyan': '0x008B8B',
|
||||
'DarkGoldenRod': '0xB8860B', 'DarkGray': '0xA9A9A9', 'DarkGrey': '0xA9A9A9',
|
||||
'DarkGreen': '0x006400', 'DarkKhaki': '0xBDB76B', 'DarkMagenta': '0x8B008B',
|
||||
'DarkOliveGreen': '0x556B2F', 'DarkOrange': '0xFF8C00', 'DarkOrchid': '0x9932CC',
|
||||
'DarkRed': '0x8B0000', 'DarkSalmon': '0xE9967A', 'DarkSeaGreen': '0x8FBC8F',
|
||||
'DarkSlateBlue': '0x483D8B', 'DarkSlateGray': '0x2F4F4F', 'DarkSlateGrey': '0x2F4F4F',
|
||||
'DarkTurquoise': '0x00CED1', 'DarkViolet': '0x9400D3', 'DeepPink': '0xFF1493',
|
||||
'DeepSkyBlue': '0x00BFFF', 'DimGray': '0x696969', 'DimGrey': '0x696969',
|
||||
'DodgerBlue': '0x1E90FF', 'FireBrick': '0xB22222', 'FloralWhite': '0xFFFAF0',
|
||||
'ForestGreen': '0x228B22', 'Fuchsia': '0xFF00FF', 'Gainsboro': '0xDCDCDC',
|
||||
'GhostWhite': '0xF8F8FF', 'Gold': '0xFFD700', 'GoldenRod': '0xDAA520', 'Gray': '0x808080',
|
||||
'Grey': '0x808080', 'Green': '0x008000', 'GreenYellow': '0xADFF2F', 'HoneyDew': '0xF0FFF0',
|
||||
'HotPink': '0xFF69B4', 'IndianRed': '0xCD5C5C', 'Indigo': '0x4B0082', 'Ivory': '0xFFFFF0',
|
||||
'Khaki': '0xF0E68C', 'Lavender': '0xE6E6FA', 'LavenderBlush': '0xFFF0F5',
|
||||
'LawnGreen': '0x7CFC00', 'LemonChiffon': '0xFFFACD', 'LightBlue': '0xADD8E6',
|
||||
'LightCoral': '0xF08080', 'LightCyan': '0xE0FFFF', 'LightGoldenRodYellow': '0xFAFAD2',
|
||||
'LightGray': '0xD3D3D3', 'LightGrey': '0xD3D3D3', 'LightGreen': '0x90EE90',
|
||||
'LightPink': '0xFFB6C1', 'LightSalmon': '0xFFA07A', 'LightSeaGreen': '0x20B2AA',
|
||||
'LightSkyBlue': '0x87CEFA', 'LightSlateGray': '0x778899', 'LightSlateGrey': '0x778899',
|
||||
'LightSteelBlue': '0xB0C4DE', 'LightYellow': '0xFFFFE0', 'Lime': '0x00FF00',
|
||||
'LimeGreen': '0x32CD32', 'Linen': '0xFAF0E6', 'Magenta': '0xFF00FF', 'Maroon': '0x800000',
|
||||
'MediumAquaMarine': '0x66CDAA', 'MediumBlue': '0x0000CD', 'MediumOrchid': '0xBA55D3',
|
||||
'MediumPurple': '0x9370DB', 'MediumSeaGreen': '0x3CB371', 'MediumSlateBlue': '0x7B68EE',
|
||||
'MediumSpringGreen': '0x00FA9A', 'MediumTurquoise': '0x48D1CC', 'MediumVioletRed': '0xC71585',
|
||||
'MidnightBlue': '0x191970', 'MintCream': '0xF5FFFA', 'MistyRose': '0xFFE4E1',
|
||||
'Moccasin': '0xFFE4B5', 'NavajoWhite': '0xFFDEAD', 'Navy': '0x000080', 'OldLace': '0xFDF5E6',
|
||||
'Olive': '0x808000', 'OliveDrab': '0x6B8E23', 'Orange': '0xFFA500', 'OrangeRed': '0xFF4500',
|
||||
'Orchid': '0xDA70D6', 'PaleGoldenRod': '0xEEE8AA', 'PaleGreen': '0x98FB98',
|
||||
'PaleTurquoise': '0xAFEEEE', 'PaleVioletRed': '0xDB7093', 'PapayaWhip': '0xFFEFD5',
|
||||
'PeachPuff': '0xFFDAB9', 'Peru': '0xCD853F', 'Pink': '0xFFC0CB', 'Plum': '0xDDA0DD',
|
||||
'PowderBlue': '0xB0E0E6', 'Purple': '0x800080', 'RebeccaPurple': '0x663399', 'Red': '0xFF0000',
|
||||
'RosyBrown': '0xBC8F8F', 'RoyalBlue': '0x4169E1', 'SaddleBrown': '0x8B4513', 'Salmon': '0xFA8072',
|
||||
'SandyBrown': '0xF4A460', 'SeaGreen': '0x2E8B57', 'SeaShell': '0xFFF5EE', 'Sienna': '0xA0522D',
|
||||
'Silver': '0xC0C0C0', 'SkyBlue': '0x87CEEB', 'SlateBlue': '0x6A5ACD', 'SlateGray': '0x708090',
|
||||
'SlateGrey': '0x708090', 'Snow': '0xFFFAFA', 'SpringGreen': '0x00FF7F', 'SteelBlue': '0x4682B4',
|
||||
'Tan': '0xD2B48C', 'Teal': '0x008080', 'Thistle': '0xD8BFD8', 'Tomato': '0xFF6347',
|
||||
'Turquoise': '0x40E0D0', 'Violet': '0xEE82EE', 'Wheat': '0xF5DEB3', 'White': '0xFFFFFF',
|
||||
'WhiteSmoke': '0xF5F5F5', 'Yellow': '0xFFFF00', 'YellowGreen': '0x9ACD32'}
|
||||
|
||||
def shift_color(col, shift=30, sat=1.0, val=1.0):
|
||||
r = (col & (255 << 16)) >> 16
|
||||
g = (col & (255 << 8)) >> 8
|
||||
b = col & 255
|
||||
hsv = colorsys.rgb_to_hsv(r, g, b)
|
||||
h = (((hsv[0] * 360) + shift) % 360) / 360
|
||||
rgb = colorsys.hsv_to_rgb(h, hsv[1] * sat, hsv[2] * val)
|
||||
return (int(rgb[0]) << 16) + (int(rgb[1]) << 8) + int(rgb[2])
|
||||
|
||||
def parse_sheet(ws):
|
||||
print(f'Parsing worksheet {ws.title}')
|
||||
ir = {"desc": ws.title}
|
||||
rows = ws.rows
|
||||
keys = [col.value.lower() for col in next(rows)]
|
||||
for row in rows:
|
||||
rec = dict(zip(keys, [col.value for col in row]))
|
||||
if rec.get('code') is None:
|
||||
continue
|
||||
cd = {"label": rec.get('label')}
|
||||
if rec.get('row'):
|
||||
cd['pos'] = f'{rec["row"]}x{rec["col"]}'
|
||||
if rec.get('comment'):
|
||||
cd['cmnt'] = rec.get('comment')
|
||||
if rec.get('rpt'):
|
||||
cd['rpt'] = bool(rec['rpt'])
|
||||
|
||||
if rec.get('cmd'):
|
||||
cd['cmd'] = rec['cmd']
|
||||
elif all((rec.get('primary'), rec.get('secondary'), rec.get('tertiary'))):
|
||||
c1 = int(rec.get('primary'), 16)
|
||||
c2 = int(rec.get('secondary'), 16)
|
||||
c3 = int(rec.get('tertiary'), 16)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
elif all((rec.get('primary'), rec.get('secondary'))):
|
||||
c1 = int(rec.get('primary'), 16)
|
||||
c2 = int(rec.get('secondary'), 16)
|
||||
c3 = shift_color(c1, -1, sat=0.66, val=0.66)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
elif rec.get('primary'):
|
||||
c1 = int(rec.get('primary'), 16)
|
||||
c2 = shift_color(c1, 30)
|
||||
c3 = shift_color(c1, -10, sat=0.66, val=0.66)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
elif rec.get('label') in named_colors:
|
||||
c1 = int(named_colors[rec.get('label')], 16)
|
||||
c2 = shift_color(c1, 30)
|
||||
c3 = shift_color(c1, -10, sat=0.66, val=0.66)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
else:
|
||||
print(f'Did not find a command or color for {rec["label"]}. Hint use named CSS colors as labels')
|
||||
ir[rec['code']] = cd
|
||||
|
||||
with open(f'{ws.title}_ir.json', 'w') as fp:
|
||||
json.dump(ir, fp, indent=2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
wb = openpyxl.load_workbook('IR_Remote_Codes.xlsx')
|
||||
for ws in wb.worksheets:
|
||||
parse_sheet(ws)
|
||||
33
usermods/JSON_IR_remote/readme.md
Normal file
33
usermods/JSON_IR_remote/readme.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# JSON IR remote
|
||||
|
||||
## Purpose
|
||||
|
||||
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
|
||||
It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
|
||||
map buttons from any remote to any HTTP request API or JSON API command.
|
||||
|
||||
## Usage
|
||||
|
||||
* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
|
||||
* On the config > LED settings page, set the correct IR pin.
|
||||
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
|
||||
|
||||
## Modification
|
||||
|
||||
* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
|
||||
* In the ir.json file, each key will be the hex encoded IR code.
|
||||
* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
|
||||
* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
|
||||
* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
|
||||
* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
|
||||
* Other properties are ignored, but having a label property may help when editing.
|
||||
|
||||
|
||||
Sample:
|
||||
{
|
||||
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
|
||||
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
|
||||
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
|
||||
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
|
||||
"label": "Preset 1 or fallback to Saw - Party"}, // c function
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class PIRsensorSwitch : public Usermod {
|
||||
// PIR sensor pin
|
||||
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
|
||||
// notification mode for colorUpdated()
|
||||
const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE
|
||||
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
|
||||
// 1 min delay before switch off after the sensor state goes LOW
|
||||
uint32_t m_switchOffDelay = 60000;
|
||||
// off timer start time
|
||||
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
// PIR sensor pin
|
||||
int8_t PIRsensorPin = PIR_SENSOR_PIN;
|
||||
// notification mode for colorUpdated()
|
||||
const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE
|
||||
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
|
||||
// delay before switch off after the sensor state goes LOW
|
||||
uint32_t m_switchOffDelay = 600000; // 10min
|
||||
// off timer start time
|
||||
@@ -76,6 +76,9 @@ private:
|
||||
bool m_nightTimeOnly = false;
|
||||
// flag to send MQTT message only (assuming it is enabled)
|
||||
bool m_mqttOnly = false;
|
||||
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
|
||||
bool m_offOnly = false;
|
||||
bool PIRtriggered = false;
|
||||
|
||||
unsigned long lastLoop = 0;
|
||||
|
||||
@@ -87,6 +90,7 @@ private:
|
||||
static const char _offPreset[];
|
||||
static const char _nightTime[];
|
||||
static const char _mqttOnly[];
|
||||
static const char _offOnly[];
|
||||
|
||||
/**
|
||||
* check if it is daytime
|
||||
@@ -118,6 +122,8 @@ private:
|
||||
*/
|
||||
void switchStrip(bool switchOn)
|
||||
{
|
||||
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
|
||||
PIRtriggered = switchOn;
|
||||
if (switchOn && m_onPreset) {
|
||||
applyPreset(m_onPreset);
|
||||
} else if (!switchOn && m_offPreset) {
|
||||
@@ -193,16 +199,18 @@ public:
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
// 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) {
|
||||
if (enabled) {
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
// PIR Sensor mode INPUT_PULLUP
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
} else {
|
||||
if (PIRsensorPin >= 0) {
|
||||
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
|
||||
}
|
||||
PIRsensorPin = -1; // allocation failed
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
initDone = true;
|
||||
@@ -221,8 +229,8 @@ public:
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
// only check sensors 10x/s
|
||||
if (millis() - lastLoop < 100 || strip.isUpdating()) return;
|
||||
// only check sensors 4x/s
|
||||
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
|
||||
lastLoop = millis();
|
||||
|
||||
if (!updatePIRsensorState()) {
|
||||
@@ -313,6 +321,7 @@ public:
|
||||
top[FPSTR(_offPreset)] = m_offPreset;
|
||||
top[FPSTR(_nightTime)] = m_nightTimeOnly;
|
||||
top[FPSTR(_mqttOnly)] = m_mqttOnly;
|
||||
top[FPSTR(_offOnly)] = m_offOnly;
|
||||
DEBUG_PRINTLN(F("PIR config saved."));
|
||||
}
|
||||
|
||||
@@ -348,6 +357,7 @@ public:
|
||||
|
||||
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
|
||||
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
|
||||
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
@@ -359,8 +369,8 @@ public:
|
||||
if (oldPin != PIRsensorPin && oldPin >= 0) {
|
||||
// if we are changing pin in settings page
|
||||
// deallocate old pin
|
||||
pinManager.deallocatePin(oldPin);
|
||||
if (pinManager.allocatePin(PIRsensorPin,false)) {
|
||||
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
|
||||
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
} else {
|
||||
// allocation failed
|
||||
@@ -375,7 +385,7 @@ public:
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
return !top[FPSTR(_offOnly)].isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,3 +406,4 @@ 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";
|
||||
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
|
||||
|
||||
36
usermods/PWM_fan/readme.md
Normal file
36
usermods/PWM_fan/readme.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# PWM fan
|
||||
|
||||
v2 Usermod to to control PWM fan with RPM feedback and temperature control
|
||||
|
||||
This usermod requires Dallas Temperature usermod to obtain temperature information. If this is not available the fan will always run at 100% speed.
|
||||
If the fan does not have _tacho_ (RPM) output you can set the _tacho-pin_ to -1 to not use that feature.
|
||||
|
||||
You can also set the thershold temperature at which fan runs at lowest speed. If the actual temperature measured will be 3°C greater than threshold temperature the fan will run at 100%.
|
||||
|
||||
If the _tacho_ is supported the current speed (in RPM) will be repored in WLED Info page.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the compile-time option `-D USERMOD_PWM_FAN` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_PWM_FAN` in `myconfig.h`.
|
||||
You will also need `-D USERMOD_DALLASTEMPERATURE`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
All of the parameters are configured during run-time using Usermods settings page.
|
||||
This includes:
|
||||
|
||||
* PWM output pin
|
||||
* tacho input pin
|
||||
* sampling frequency in seconds
|
||||
* threshold temperature in degees C
|
||||
|
||||
_NOTE:_ You may also need to tweak Dallas Temperature usermod sampling frequency to match PWM fan sampling frequency.
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-10
|
||||
* First public release
|
||||
332
usermods/PWM_fan/usermod_PWM_fan.h
Normal file
332
usermods/PWM_fan/usermod_PWM_fan.h
Normal file
@@ -0,0 +1,332 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef USERMOD_DALLASTEMPERATURE
|
||||
#error The "PWM fan" usermod requires "Dallas Temeprature" usermod to function properly.
|
||||
#endif
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
// PWM & tacho code curtesy of @KlausMu
|
||||
// https://github.com/KlausMu/esp32-fan-controller/tree/main/src
|
||||
// adapted for WLED usermod by @blazoncek
|
||||
|
||||
|
||||
// tacho counter
|
||||
static volatile unsigned long counter_rpm = 0;
|
||||
// Interrupt counting every rotation of the fan
|
||||
// https://desire.giesecke.tk/index.php/2018/01/30/change-global-variables-from-isr/
|
||||
static void IRAM_ATTR rpm_fan() {
|
||||
counter_rpm++;
|
||||
}
|
||||
|
||||
|
||||
class PWMFanUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
unsigned long msLastTachoMeasurement = 0;
|
||||
uint16_t last_rpm = 0;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint8_t pwmChannel = 255;
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
UsermodTemperature* tempUM;
|
||||
#endif
|
||||
|
||||
// configurable parameters
|
||||
int8_t tachoPin = -1;
|
||||
int8_t pwmPin = -1;
|
||||
uint8_t tachoUpdateSec = 30;
|
||||
float targetTemperature = 25.0;
|
||||
uint8_t minPWMValuePct = 50;
|
||||
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _tachoPin[];
|
||||
static const char _pwmPin[];
|
||||
static const char _temperature[];
|
||||
static const char _tachoUpdateSec[];
|
||||
static const char _minPWMValuePct[];
|
||||
static const char _IRQperRotation[];
|
||||
|
||||
void initTacho(void) {
|
||||
if (tachoPin < 0 || !pinManager.allocatePin(tachoPin, false, PinOwner::UM_Unspecified)){
|
||||
tachoPin = -1;
|
||||
return;
|
||||
}
|
||||
pinMode(tachoPin, INPUT);
|
||||
digitalWrite(tachoPin, HIGH);
|
||||
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
|
||||
DEBUG_PRINTLN(F("Tacho sucessfully initialized."));
|
||||
}
|
||||
|
||||
void deinitTacho(void) {
|
||||
if (tachoPin < 0) return;
|
||||
detachInterrupt(digitalPinToInterrupt(tachoPin));
|
||||
pinManager.deallocatePin(tachoPin, PinOwner::UM_Unspecified);
|
||||
tachoPin = -1;
|
||||
}
|
||||
|
||||
void updateTacho(void) {
|
||||
if (tachoPin < 0) return;
|
||||
|
||||
// start of tacho measurement
|
||||
// detach interrupt while calculating rpm
|
||||
detachInterrupt(digitalPinToInterrupt(tachoPin));
|
||||
// calculate rpm
|
||||
last_rpm = (counter_rpm * 60) / numberOfInterrupsInOneSingleRotation;
|
||||
last_rpm /= tachoUpdateSec;
|
||||
// reset counter
|
||||
counter_rpm = 0;
|
||||
// store milliseconds when tacho was measured the last time
|
||||
msLastTachoMeasurement = millis();
|
||||
// attach interrupt again
|
||||
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
|
||||
}
|
||||
|
||||
// https://randomnerdtutorials.com/esp32-pwm-arduino-ide/
|
||||
void initPWMfan(void) {
|
||||
if (pwmPin < 0 || !pinManager.allocatePin(pwmPin, true, PinOwner::UM_Unspecified)) {
|
||||
pwmPin = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
analogWriteRange(255);
|
||||
analogWriteFreq(WLED_PWM_FREQ);
|
||||
#else
|
||||
pwmChannel = pinManager.allocateLedc(1);
|
||||
if (pwmChannel == 255) { //no more free LEDC channels
|
||||
deinitPWMfan(); return;
|
||||
}
|
||||
// configure LED PWM functionalitites
|
||||
ledcSetup(pwmChannel, 25000, 8);
|
||||
// attach the channel to the GPIO to be controlled
|
||||
ledcAttachPin(pwmPin, pwmChannel);
|
||||
#endif
|
||||
DEBUG_PRINTLN(F("Fan PWM sucessfully initialized."));
|
||||
}
|
||||
|
||||
void deinitPWMfan(void) {
|
||||
if (pwmPin < 0) return;
|
||||
|
||||
pinManager.deallocatePin(pwmPin, PinOwner::UM_Unspecified);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
pinManager.deallocateLedc(pwmChannel, 1);
|
||||
#endif
|
||||
pwmPin = -1;
|
||||
}
|
||||
|
||||
void updateFanSpeed(uint8_t pwmValue){
|
||||
if (pwmPin < 0) return;
|
||||
|
||||
#ifdef ESP8266
|
||||
analogWrite(pwmPin, pwmValue);
|
||||
#else
|
||||
ledcWrite(pwmChannel, pwmValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
float getActualTemperature(void) {
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
if (tempUM != nullptr)
|
||||
return tempUM->getTemperatureC();
|
||||
#endif
|
||||
return -127.0f;
|
||||
}
|
||||
|
||||
void setFanPWMbasedOnTemperature(void) {
|
||||
float temp = getActualTemperature();
|
||||
float difftemp = temp - targetTemperature;
|
||||
// Default to run fan at full speed.
|
||||
int newPWMvalue = 255;
|
||||
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
|
||||
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
|
||||
|
||||
if ((temp == NAN) || (temp <= 0.0)) {
|
||||
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
|
||||
} else if (difftemp <= 0.0) {
|
||||
// Temperature is below target temperature. Run fan at minimum speed.
|
||||
newPWMvalue = pwmMinimumValue;
|
||||
} else if (difftemp <= 0.5) {
|
||||
newPWMvalue = pwmMinimumValue + pwmStep;
|
||||
} else if (difftemp <= 1.0) {
|
||||
newPWMvalue = pwmMinimumValue + 2*pwmStep;
|
||||
} else if (difftemp <= 1.5) {
|
||||
newPWMvalue = pwmMinimumValue + 3*pwmStep;
|
||||
} else if (difftemp <= 2.0) {
|
||||
newPWMvalue = pwmMinimumValue + 4*pwmStep;
|
||||
} else if (difftemp <= 2.5) {
|
||||
newPWMvalue = pwmMinimumValue + 5*pwmStep;
|
||||
} else if (difftemp <= 3.0) {
|
||||
newPWMvalue = pwmMinimumValue + 6*pwmStep;
|
||||
}
|
||||
updateFanSpeed(newPWMvalue);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
// This Usermod requires Temperature usermod
|
||||
tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE);
|
||||
#endif
|
||||
initTacho();
|
||||
initPWMfan();
|
||||
updateFanSpeed((minPWMValuePct * 255) / 100); // inital fan speed
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/*
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
|
||||
unsigned long now = millis();
|
||||
if ((now - msLastTachoMeasurement) < (tachoUpdateSec * 1000)) return;
|
||||
|
||||
updateTacho();
|
||||
setFanPWMbasedOnTemperature();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
if (tachoPin < 0) return;
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
JsonArray data = user.createNestedArray(FPSTR(_name));
|
||||
data.add(last_rpm);
|
||||
data.add(F("rpm"));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_pwmPin)] = pwmPin;
|
||||
top[FPSTR(_tachoPin)] = tachoPin;
|
||||
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
|
||||
top[FPSTR(_temperature)] = targetTemperature;
|
||||
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
|
||||
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
|
||||
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) {
|
||||
int8_t newTachoPin = tachoPin;
|
||||
int8_t newPwmPin = pwmPin;
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newTachoPin = top[FPSTR(_tachoPin)] | newTachoPin;
|
||||
newPwmPin = top[FPSTR(_pwmPin)] | newPwmPin;
|
||||
tachoUpdateSec = top[FPSTR(_tachoUpdateSec)] | tachoUpdateSec;
|
||||
tachoUpdateSec = (uint8_t) max(1,(int)tachoUpdateSec); // bounds checking
|
||||
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
|
||||
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
|
||||
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
|
||||
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
|
||||
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
|
||||
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
tachoPin = newTachoPin;
|
||||
pwmPin = newPwmPin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing paramters from settings page
|
||||
if (tachoPin != newTachoPin || pwmPin != newPwmPin) {
|
||||
DEBUG_PRINTLN(F("Re-init pins."));
|
||||
// deallocate pin and release interrupts
|
||||
deinitTacho();
|
||||
deinitPWMfan();
|
||||
tachoPin = newTachoPin;
|
||||
pwmPin = newPwmPin;
|
||||
// initialise
|
||||
setup();
|
||||
}
|
||||
}
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_IRQperRotation)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_PWM_FAN;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char PWMFanUsermod::_name[] PROGMEM = "PWM-fan";
|
||||
const char PWMFanUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char PWMFanUsermod::_tachoPin[] PROGMEM = "tacho-pin";
|
||||
const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
|
||||
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
|
||||
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
|
||||
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
|
||||
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";
|
||||
72
usermods/ST7789_display/README.md
Normal file
72
usermods/ST7789_display/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# ST7789 TFT IPS Color display 240x240pxwith ESP32 boards
|
||||
|
||||
This usermod allow to use 240x240 display to display following:
|
||||
|
||||
* Network SSID;
|
||||
* IP address;
|
||||
* Brightness;
|
||||
* Chosen effect;
|
||||
* Chosen palette;
|
||||
* Estimated current in mA;
|
||||
|
||||
## Hardware
|
||||
|
||||
***
|
||||

|
||||
|
||||
## Library used
|
||||
|
||||
[Bodmer/TFT_eSPI](https://github.com/Bodmer/TFT_eSPI)
|
||||
|
||||
## Setup
|
||||
|
||||
***
|
||||
|
||||
### Platformio.ini changes
|
||||
|
||||
In the `platformio.ini` file, uncomment the `TFT_eSPI` line within the [common] section, under `lib_deps`:
|
||||
|
||||
```ini
|
||||
# platformio.ini
|
||||
...
|
||||
[common]
|
||||
...
|
||||
lib_deps =
|
||||
...
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
...
|
||||
```
|
||||
|
||||
Also, while in the `platformio.ini` file, you must change the environment setup to build for just the esp32dev platform as follows:
|
||||
|
||||
Add lines to section:
|
||||
|
||||
```ini
|
||||
default_envs = esp32dev
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D USERMOD_ST7789_DISPLAY
|
||||
|
||||
```
|
||||
|
||||
Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step.
|
||||
|
||||
### TFT_eSPI Library Adjustments
|
||||
|
||||
We need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI` folder.
|
||||
|
||||
Modify the `User_Setup_Select.h` file as follows:
|
||||
|
||||
* Comment out the following line (which is the 'default' setup file):
|
||||
|
||||
```ini
|
||||
//#include <User_Setup.h> // Default setup is root library folder
|
||||
```
|
||||
|
||||
* Add following line:
|
||||
|
||||
```ini
|
||||
#include <User_Setups/Setup_ST7789_Display.h> // Setup file for ESP32 ST7789V SPI bus TFT
|
||||
```
|
||||
|
||||
* Copy file `"Setup_ST7789_Display.h"` from usermod folder to `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups`
|
||||
350
usermods/ST7789_display/ST7789_display.h
Normal file
350
usermods/ST7789_display/ST7789_display.h
Normal file
@@ -0,0 +1,350 @@
|
||||
// Credits to @mrVanboy, @gwaland and my dearest friend @westward
|
||||
// Also for @spiff72 for usermod TTGO-T-Display
|
||||
// 210217
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <TFT_eSPI.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#define USERMOD_ST7789_DISPLAY 97
|
||||
|
||||
#ifndef TFT_DISPOFF
|
||||
#define TFT_DISPOFF 0x28
|
||||
#endif
|
||||
|
||||
#ifndef TFT_SLPIN
|
||||
#define TFT_SLPIN 0x10
|
||||
#endif
|
||||
|
||||
#define TFT_MOSI 21
|
||||
#define TFT_SCLK 22
|
||||
#define TFT_DC 18
|
||||
#define TFT_RST 5
|
||||
#define TFT_BL 26 // Display backlight control pin
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library
|
||||
|
||||
// How often we are redrawing screen
|
||||
#define USER_LOOP_REFRESH_RATE_MS 1000
|
||||
|
||||
|
||||
//class name. Use something descriptive and leave the ": public Usermod" part :)
|
||||
class St7789DisplayUsermod : public Usermod {
|
||||
private:
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
bool displayTurnedOff = false;
|
||||
long lastRedraw = 0;
|
||||
// needRedraw marks if redraw is required to prevent often redrawing.
|
||||
bool needRedraw = true;
|
||||
// Next variables hold the previous known values to determine if redraw is required.
|
||||
String knownSsid = "";
|
||||
IPAddress knownIp;
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
|
||||
long lastUpdate = 0;
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
tft.init();
|
||||
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setTextColor(TFT_RED);
|
||||
tft.setCursor(60, 100);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.setTextSize(2);
|
||||
tft.print("Loading...");
|
||||
if (TFT_BL > 0)
|
||||
{ // TFT_BL has been set in the TFT_eSPI library
|
||||
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
|
||||
digitalWrite(TFT_BL, HIGH); // Turn backlight on.
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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() {
|
||||
// Check if we time interval for redrawing passes.
|
||||
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lastUpdate = millis();
|
||||
|
||||
// Turn off display after 5 minutes with no change.
|
||||
if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
|
||||
{
|
||||
digitalWrite(TFT_BL, LOW); // Turn backlight off.
|
||||
displayTurnedOff = true;
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid)
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP()))
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
else if (knownBrightness != bri)
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
else if (knownMode != strip.getMode())
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
else if (knownPalette != strip.getSegment(0).palette)
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
if (!needRedraw)
|
||||
{
|
||||
return;
|
||||
}
|
||||
needRedraw = false;
|
||||
|
||||
if (displayTurnedOff)
|
||||
{
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on.
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
lastRedraw = millis();
|
||||
|
||||
// Update last known values.
|
||||
#if defined(ESP8266)
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
#else
|
||||
knownSsid = WiFi.SSID();
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setTextSize(2);
|
||||
// First row with Wifi name
|
||||
tft.setTextColor(TFT_SILVER);
|
||||
tft.setCursor(3, 40);
|
||||
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 AP IP and Password or IP
|
||||
tft.setTextColor(TFT_GREEN);
|
||||
tft.setTextSize(2);
|
||||
tft.setCursor(3, 64);
|
||||
// Print AP IP and password in AP mode or knownIP if AP not active.
|
||||
|
||||
if (apActive)
|
||||
{
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.print("AP IP: ");
|
||||
tft.print(knownIp);
|
||||
tft.setCursor(3,86);
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.print("AP Pass:");
|
||||
tft.print(apPass);
|
||||
}
|
||||
else
|
||||
{
|
||||
tft.setTextColor(TFT_GREEN);
|
||||
tft.print("IP: ");
|
||||
tft.print(knownIp);
|
||||
tft.setCursor(3,86);
|
||||
//tft.print("Signal Strength: ");
|
||||
//tft.print(i.wifi.signal);
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.print("Bri: ");
|
||||
tft.print(((float(bri)/255)*100),0);
|
||||
tft.print("%");
|
||||
}
|
||||
|
||||
// Third row with mode name
|
||||
tft.setCursor(3, 108);
|
||||
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(JSON_mode_names); i++)
|
||||
{
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
|
||||
switch (singleJsonSymbol)
|
||||
{
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownMode))
|
||||
break;
|
||||
tft.setTextColor(TFT_MAGENTA);
|
||||
tft.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
|
||||
break;
|
||||
}
|
||||
// Fourth row with palette name
|
||||
tft.setTextColor(TFT_YELLOW);
|
||||
tft.setCursor(3, 130);
|
||||
qComma = 0;
|
||||
insideQuotes = false;
|
||||
printedChars = 0;
|
||||
// Looking for palette name in JSON.
|
||||
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
|
||||
{
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
|
||||
switch (singleJsonSymbol)
|
||||
{
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownPalette))
|
||||
break;
|
||||
tft.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
|
||||
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
|
||||
break;
|
||||
}
|
||||
// Fifth row with estimated mA usage
|
||||
tft.setTextColor(TFT_SILVER);
|
||||
tft.setCursor(3, 152);
|
||||
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
|
||||
tft.print("Current: ");
|
||||
tft.print(strip.currentMilliamps);
|
||||
tft.print("mA");
|
||||
}
|
||||
/*
|
||||
* 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!"));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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("exampleUsermod");
|
||||
top["great"] = userVar0; //save this var persistently whenever settings are 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 :)
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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_ST7789_DISPLAY;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
||||
39
usermods/ST7789_display/Setup_ST7789_Display.h
Normal file
39
usermods/ST7789_display/Setup_ST7789_Display.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Setup for the ESP32 board with 1.5" 240x240 display
|
||||
|
||||
// See SetupX_Template.h for all options available
|
||||
|
||||
#define ST7789_DRIVER
|
||||
#define TFT_SDA_READ // Display has a bidirectionsl SDA pin
|
||||
|
||||
#define TFT_WIDTH 240
|
||||
#define TFT_HEIGHT 240
|
||||
|
||||
#define CGRAM_OFFSET // Library will add offsets required
|
||||
|
||||
//#define TFT_MISO -1
|
||||
|
||||
#define TFT_MOSI 21
|
||||
#define TFT_SCLK 22
|
||||
//#define TFT_CS 5
|
||||
#define TFT_DC 18
|
||||
#define TFT_RST 5
|
||||
|
||||
#define TFT_BL 26 // Display backlight control pin
|
||||
|
||||
#define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options
|
||||
|
||||
#define LOAD_GLCD
|
||||
#define LOAD_FONT2
|
||||
#define LOAD_FONT4
|
||||
#define LOAD_FONT6
|
||||
#define LOAD_FONT7
|
||||
#define LOAD_FONT8
|
||||
#define LOAD_GFXFF
|
||||
|
||||
//#define SMOOTH_FONT
|
||||
|
||||
//#define SPI_FREQUENCY 27000000
|
||||
#define SPI_FREQUENCY 40000000 // Maximum for ILI9341
|
||||
|
||||
|
||||
#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V
|
||||
BIN
usermods/ST7789_display/images/ST7789_Guide.jpg
Normal file
BIN
usermods/ST7789_display/images/ST7789_Guide.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 175 KiB |
@@ -42,6 +42,8 @@ class UsermodTemperature : public Usermod {
|
||||
bool waitingForConversion = 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 sensorFound = false;
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
@@ -79,7 +81,9 @@ class UsermodTemperature : public Usermod {
|
||||
temperature = readDallas();
|
||||
lastMeasurement = millis();
|
||||
waitingForConversion = false;
|
||||
DEBUG_PRINTF("Read temperature %2.1f.\n", temperature);
|
||||
//DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
|
||||
DEBUG_PRINT(F("Read temperature "));
|
||||
DEBUG_PRINTLN(temperature);
|
||||
}
|
||||
|
||||
bool findSensor() {
|
||||
@@ -87,7 +91,9 @@ class UsermodTemperature : public Usermod {
|
||||
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
|
||||
// find out if we have DS18xxx sensor attached
|
||||
oneWire->reset_search();
|
||||
delay(10);
|
||||
while (oneWire->search(deviceAddress)) {
|
||||
DEBUG_PRINTLN(F("Found something..."));
|
||||
if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) {
|
||||
switch (deviceAddress[0]) {
|
||||
case 0x10: // DS18S20
|
||||
@@ -107,21 +113,28 @@ class UsermodTemperature : public Usermod {
|
||||
|
||||
void setup() {
|
||||
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 {
|
||||
if (enabled) {
|
||||
// config says we are enabled
|
||||
if (enabled) {
|
||||
// config says we are enabled
|
||||
DEBUG_PRINTLN(F("Allocating temperature pin..."));
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
|
||||
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
|
||||
if (!oneWire->reset()) {
|
||||
sensorFound = false; // resetting 1-Wire bus yielded an error
|
||||
} else {
|
||||
while ((sensorFound=findSensor()) && retries--) {
|
||||
delay(25); // try to find sensor
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (temperaturePin >= 0) {
|
||||
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
|
||||
}
|
||||
temperaturePin = -1; // allocation failed
|
||||
sensorFound = false;
|
||||
}
|
||||
}
|
||||
lastMeasurement = millis() - readingInterval + 10000;
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
@@ -189,7 +202,7 @@ class UsermodTemperature : public Usermod {
|
||||
JsonArray temp = user.createNestedArray(FPSTR(_name));
|
||||
//temp.add(F("Loaded."));
|
||||
|
||||
if (temperature <= -100) {
|
||||
if (temperature <= -100.0 || (!sensorFound && temperature == -1.0)) {
|
||||
temp.add(0);
|
||||
temp.add(F(" Sensor Error!"));
|
||||
return;
|
||||
@@ -249,6 +262,7 @@ class UsermodTemperature : public Usermod {
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newTemperaturePin = top["pin"] | newTemperaturePin;
|
||||
// newTemperaturePin = min(33,max(-1,(int)newTemperaturePin)); // bounds check
|
||||
degC = top["degC"] | degC;
|
||||
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
|
||||
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
|
||||
@@ -260,16 +274,17 @@ class UsermodTemperature : public Usermod {
|
||||
temperaturePin = newTemperaturePin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing parameters from settings page
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing paramters from settings page
|
||||
if (newTemperaturePin != temperaturePin) {
|
||||
DEBUG_PRINTLN(F("Re-init temperature."));
|
||||
// deallocate pin and release memory
|
||||
delete oneWire;
|
||||
pinManager.deallocatePin(temperaturePin);
|
||||
pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature);
|
||||
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();
|
||||
|
||||
@@ -54,46 +54,46 @@ void userLoop()
|
||||
switch (myKey) {
|
||||
case '1':
|
||||
applyPreset(1);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '2':
|
||||
applyPreset(2);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '3':
|
||||
applyPreset(3);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '4':
|
||||
applyPreset(4);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '5':
|
||||
applyPreset(5);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '6':
|
||||
applyPreset(6);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case 'A':
|
||||
applyPreset(7);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case 'B':
|
||||
applyPreset(8);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
|
||||
case '7':
|
||||
effectCurrent += 1;
|
||||
if (effectCurrent >= MODE_COUNT) effectCurrent = 0;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '*':
|
||||
effectCurrent -= 1;
|
||||
if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
|
||||
case '8':
|
||||
@@ -102,7 +102,7 @@ void userLoop()
|
||||
} else if (effectSpeed < 255) {
|
||||
effectSpeed += 1;
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '0':
|
||||
if (effectSpeed > 15) {
|
||||
@@ -110,7 +110,7 @@ void userLoop()
|
||||
} else if (effectSpeed > 0) {
|
||||
effectSpeed -= 1;
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
|
||||
case '9':
|
||||
@@ -119,7 +119,7 @@ void userLoop()
|
||||
} else if (effectIntensity < 255) {
|
||||
effectIntensity += 1;
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '#':
|
||||
if (effectIntensity > 15) {
|
||||
@@ -127,18 +127,18 @@ void userLoop()
|
||||
} else if (effectIntensity > 0) {
|
||||
effectIntensity -= 1;
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
effectPalette += 1;
|
||||
if (effectPalette >= 50) effectPalette = 0;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case 'D':
|
||||
effectPalette -= 1;
|
||||
if (effectPalette <= 0) effectPalette = 50;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
BIN
usermods/battery_status_basic/assets/battery_info_screen.png
Normal file
BIN
usermods/battery_status_basic/assets/battery_info_screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
69
usermods/battery_status_basic/readme.md
Normal file
69
usermods/battery_status_basic/readme.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# :battery: Battery status/level Usermod :battery:
|
||||
|
||||
This Usermod allows you to monitor the battery level of your battery powered project.
|
||||
|
||||
You can see the battery level and voltage in the `info modal`.
|
||||
|
||||
For this to work the positive side of the (18650) battery must be connected to pin `A0` of the d1mini/esp8266 with a 100k ohm resistor (see [Useful Links](#useful-links)).
|
||||
|
||||
If you have a esp32 board it is best to connect the positive side of the battery to ADC1 (GPIO32 - GPIO39)
|
||||
|
||||
<p align="center">
|
||||
<img width="300" src="assets/battery_info_screen.png">
|
||||
</p>
|
||||
|
||||
## Installation
|
||||
|
||||
define `USERMOD_BATTERY_STATUS_BASIC` in `my_config.h`
|
||||
|
||||
### Basic wiring diagram
|
||||
<p align="center">
|
||||
<img width="300" src="assets/battery_connection_schematic_01.png">
|
||||
</p>
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_BATTERY_STATUS_BASIC` - define this (in `my_config.h`) to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_BATTERY_MEASUREMENT_PIN` - defaults to A0 on esp8266 and GPIO32 on esp32
|
||||
* `USERMOD_BATTERY_MEASUREMENT_INTERVAL` - the frequency to check the battery, defaults to 30 seconds
|
||||
* `USERMOD_BATTERY_MIN_VOLTAGE` - minimum voltage of the Battery used, default is 2.6 (18650 battery standard)
|
||||
* `USERMOD_BATTERY_MAX_VOLTAGE` - maximum voltage of the Battery used, default is 4.2 (18650 battery standard)
|
||||
|
||||
All parameters can be configured at runtime using Usermods settings page.
|
||||
|
||||
## Important :warning:
|
||||
* Make sure you know your battery specification ! not every battery is the same !
|
||||
* Example:
|
||||
|
||||
| Your battery specification table | | Options you can define |
|
||||
| :-------------------------------- |:--------------- | :---------------------------- |
|
||||
| Capacity | 3500mAh 12,5 Wh | |
|
||||
| Minimum capacity | 3350mAh 11,9 Wh | |
|
||||
| Rated voltage | 3.6V - 3.7V | |
|
||||
| **Charging end voltage** | **4,2V ± 0,05** | `USERMOD_BATTERY_MAX_VOLTAGE` |
|
||||
| **Discharge voltage** | **2,5V** | `USERMOD_BATTERY_MIN_VOLTAGE` |
|
||||
| Max. discharge current (constant) | 10A (10000mA) | |
|
||||
| max. charging current | 1.7A (1700mA) | |
|
||||
| ... | ... | ... |
|
||||
| .. | .. | .. |
|
||||
|
||||
Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833)
|
||||
|
||||
## Useful Links
|
||||
* https://lazyzero.de/elektronik/esp8266/wemos_d1_mini_a0/start
|
||||
* https://arduinodiy.wordpress.com/2016/12/25/monitoring-lipo-battery-voltage-with-wemos-d1-minibattery-shield-and-thingspeak/
|
||||
|
||||
## Change Log
|
||||
2021-09-02
|
||||
* added "Battery voltage" to info
|
||||
* added circuit diagram to readme
|
||||
* added MQTT support, sending battery voltage
|
||||
* minor fixes
|
||||
|
||||
2021-08-15
|
||||
* changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries
|
||||
* Updated readme, added specification table
|
||||
|
||||
2021-08-10
|
||||
* Created
|
||||
|
||||
398
usermods/battery_status_basic/usermod_v2_battery_status_basic.h
Normal file
398
usermods/battery_status_basic/usermod_v2_battery_status_basic.h
Normal file
@@ -0,0 +1,398 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// pin defaults
|
||||
// for the esp32 it is best to use the ADC1: GPIO32 - GPIO39
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
|
||||
#ifndef USERMOD_BATTERY_MEASUREMENT_PIN
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define USERMOD_BATTERY_MEASUREMENT_PIN 32
|
||||
#else //ESP8266 boards
|
||||
#define USERMOD_BATTERY_MEASUREMENT_PIN A0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// esp32 has a 12bit adc resolution
|
||||
// esp8266 only 10bit
|
||||
#ifndef USERMOD_BATTERY_ADC_PRECISION
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// 12 bits
|
||||
#define USERMOD_BATTERY_ADC_PRECISION 4095.0
|
||||
#else
|
||||
// 10 bits
|
||||
#define USERMOD_BATTERY_ADC_PRECISION 1024.0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// the frequency to check the battery, 30 sec
|
||||
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
|
||||
#endif
|
||||
|
||||
|
||||
// default for 18650 battery
|
||||
// https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop
|
||||
// Discharge voltage: 2.5 volt + .1 for personal safety
|
||||
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2
|
||||
#endif
|
||||
|
||||
class UsermodBatteryBasic : public Usermod
|
||||
{
|
||||
private:
|
||||
// battery pin can be defined in my_config.h
|
||||
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN;
|
||||
// how often to read the battery voltage
|
||||
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
|
||||
unsigned long nextReadTime = 0;
|
||||
unsigned long lastReadTime = 0;
|
||||
// battery min. voltage
|
||||
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
|
||||
// battery max. voltage
|
||||
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
|
||||
// 0 - 1024 for esp8266 (10-bit resolution)
|
||||
// 0 - 4095 for esp32 (Default is 12-bit resolution)
|
||||
float adcPrecision = USERMOD_BATTERY_ADC_PRECISION;
|
||||
// raw analog reading
|
||||
float rawValue = 0.0;
|
||||
// calculated voltage
|
||||
float voltage = 0.0;
|
||||
// mapped battery level based on voltage
|
||||
long batteryLevel = 0;
|
||||
bool initDone = false;
|
||||
bool initializing = true;
|
||||
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _readInterval[];
|
||||
|
||||
|
||||
// custom map function
|
||||
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
|
||||
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
|
||||
{
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
float truncate(float val, byte dec)
|
||||
{
|
||||
float x = val * pow(10, dec);
|
||||
float y = round(x);
|
||||
float z = x - y;
|
||||
if ((int)z == 5)
|
||||
{
|
||||
y++;
|
||||
}
|
||||
x = y / pow(10, dec);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
DEBUG_PRINTLN(F("Allocating battery pin..."));
|
||||
if (batteryPin >= 0 && pinManager.allocatePin(batteryPin, false))
|
||||
{
|
||||
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
|
||||
} else {
|
||||
if (batteryPin >= 0) DEBUG_PRINTLN(F("Battery pin allocation failed."));
|
||||
batteryPin = -1; // allocation failed
|
||||
}
|
||||
#else //ESP8266 boards have only one analog input pin A0
|
||||
|
||||
pinMode(batteryPin, INPUT);
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + readingInterval;
|
||||
lastReadTime = millis();
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
if(strip.isUpdating()) return;
|
||||
|
||||
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
|
||||
if (millis() < nextReadTime) return;
|
||||
|
||||
|
||||
nextReadTime = millis() + readingInterval;
|
||||
lastReadTime = millis();
|
||||
initializing = false;
|
||||
|
||||
// read battery raw input
|
||||
rawValue = analogRead(batteryPin);
|
||||
|
||||
// calculate the voltage
|
||||
voltage = (rawValue / adcPrecision) * maxBatteryVoltage ;
|
||||
// check if voltage is within specified voltage range
|
||||
voltage = voltage<minBatteryVoltage||voltage>maxBatteryVoltage?-1.0f:voltage;
|
||||
|
||||
// translate battery voltage into percentage
|
||||
/*
|
||||
the standard "map" function doesn't work
|
||||
https://www.arduino.cc/reference/en/language/functions/math/map/ notes and warnings at the bottom
|
||||
*/
|
||||
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100);
|
||||
|
||||
|
||||
// SmartHome stuff
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/voltage"));
|
||||
mqtt->publish(subuf, 0, false, String(voltage).c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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");
|
||||
|
||||
// info modal display names
|
||||
JsonArray batteryPercentage = user.createNestedArray("Battery level");
|
||||
JsonArray batteryVoltage = user.createNestedArray("Battery voltage");
|
||||
|
||||
if (initializing) {
|
||||
batteryPercentage.add((nextReadTime - millis()) / 1000);
|
||||
batteryPercentage.add(" sec");
|
||||
batteryVoltage.add((nextReadTime - millis()) / 1000);
|
||||
batteryVoltage.add(" sec");
|
||||
return;
|
||||
}
|
||||
|
||||
if(batteryLevel < 0) {
|
||||
batteryPercentage.add(F("invalid"));
|
||||
} else {
|
||||
batteryPercentage.add(batteryLevel);
|
||||
}
|
||||
batteryPercentage.add(F(" %"));
|
||||
|
||||
if(voltage < 0) {
|
||||
batteryVoltage.add(F("invalid"));
|
||||
} else {
|
||||
batteryVoltage.add(truncate(voltage, 2));
|
||||
}
|
||||
batteryVoltage.add(F(" V"));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 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 make your settings editable through the Usermod Settings page automatically.
|
||||
*
|
||||
* Usermod Settings Overview:
|
||||
* - Numeric values are treated as floats in the browser.
|
||||
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
|
||||
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
|
||||
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
|
||||
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
|
||||
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
|
||||
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
|
||||
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
|
||||
* used in the Usermod when reading the value from ArduinoJson.
|
||||
* - Pin values can be treated differently from an integer value by using the key name "pin"
|
||||
* - "pin" can contain a single or array of integer values
|
||||
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
|
||||
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
|
||||
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
|
||||
*
|
||||
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
|
||||
*
|
||||
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
|
||||
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
// created JSON object:
|
||||
/*
|
||||
{
|
||||
"Battery-Level": {
|
||||
"pin": "A0", <--- only when using esp32 boards
|
||||
"minBatteryVoltage": 2.6,
|
||||
"maxBatteryVoltage": 4.2,
|
||||
"read-interval-ms": 30000
|
||||
}
|
||||
}
|
||||
*/
|
||||
JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
battery["pin"] = batteryPin; // usermodparam
|
||||
#endif
|
||||
battery["minBatteryVoltage"] = minBatteryVoltage; // usermodparam
|
||||
battery["maxBatteryVoltage"] = maxBatteryVoltage; // usermodparam
|
||||
battery[FPSTR(_readInterval)] = readingInterval;
|
||||
|
||||
DEBUG_PRINTLN(F("Battery 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 immediately after boot, or after saving on the Usermod Settings page)
|
||||
*
|
||||
* 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 the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
|
||||
*
|
||||
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
|
||||
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
|
||||
*
|
||||
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// looking for JSON object:
|
||||
/*
|
||||
{
|
||||
"BatteryLevel": {
|
||||
"pin": "A0", <--- only when using esp32 boards
|
||||
"minBatteryVoltage": 2.6,
|
||||
"maxBatteryVoltage": 4.2,
|
||||
"read-interval-ms": 30000
|
||||
}
|
||||
}
|
||||
*/
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
int8_t newBatteryPin = batteryPin;
|
||||
#endif
|
||||
|
||||
JsonObject battery = root[FPSTR(_name)];
|
||||
if (battery.isNull())
|
||||
{
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
newBatteryPin = battery["pin"] | newBatteryPin;
|
||||
#endif
|
||||
minBatteryVoltage = battery["minBatteryVoltage"] | minBatteryVoltage;
|
||||
//minBatteryVoltage = min(12.0f, (int)readingInterval);
|
||||
maxBatteryVoltage = battery["maxBatteryVoltage"] | maxBatteryVoltage;
|
||||
//maxBatteryVoltage = min(14.4f, max(3.3f,(int)readingInterval));
|
||||
readingInterval = battery["read-interval-ms"] | readingInterval;
|
||||
readingInterval = max(3000, (int)readingInterval); // minimum repetition is >5000ms (5s)
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!initDone)
|
||||
{
|
||||
// first run: reading from cfg.json
|
||||
newBatteryPin = batteryPin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// changing paramters from settings page
|
||||
if (newBatteryPin != batteryPin)
|
||||
{
|
||||
// deallocate pin
|
||||
pinManager.deallocatePin(batteryPin);
|
||||
batteryPin = newBatteryPin;
|
||||
// initialise
|
||||
setup();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return !battery[FPSTR(_readInterval)].isNull();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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_BATTERY_STATUS_BASIC;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char UsermodBatteryBasic::_name[] PROGMEM = "Battery-level";
|
||||
const char UsermodBatteryBasic::_readInterval[] PROGMEM = "read-interval-ms";
|
||||
@@ -16,6 +16,12 @@ 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`
|
||||
|
||||
## JSON API
|
||||
You can switch relay state using the following JSON object transmitted to: `http://[device-ip]/json`
|
||||
|
||||
Switch relay 0 on: `{"MultiRelay":{"relay":0,"on":true}}`
|
||||
Switch relay4 3 & 4 off: `{"MultiRelay":[{"relay":2,"on":false},{"relay":3,"on":false}]}`
|
||||
|
||||
## MQTT API
|
||||
|
||||
wled/deviceMAC/relay/0/command on|off|toggle
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#define MULTI_RELAY_MAX_RELAYS 4
|
||||
#endif
|
||||
|
||||
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
||||
|
||||
#define ON true
|
||||
#define OFF false
|
||||
|
||||
@@ -23,6 +25,7 @@ typedef struct relay_t {
|
||||
bool state;
|
||||
bool external;
|
||||
uint16_t delay;
|
||||
int8_t button;
|
||||
} Relay;
|
||||
|
||||
|
||||
@@ -35,7 +38,7 @@ class MultiRelay : public Usermod {
|
||||
// switch timer start time
|
||||
uint32_t _switchTimerStart = 0;
|
||||
// old brightness
|
||||
bool _oldBrightness = 0;
|
||||
bool _oldMode;
|
||||
|
||||
// usermod enabled
|
||||
bool enabled = false; // needs to be configured (no default config)
|
||||
@@ -49,6 +52,7 @@ class MultiRelay : public Usermod {
|
||||
static const char _delay_str[];
|
||||
static const char _activeHigh[];
|
||||
static const char _external[];
|
||||
static const char _button[];
|
||||
|
||||
|
||||
void publishMqtt(const char* state, int relay) {
|
||||
@@ -170,6 +174,7 @@ class MultiRelay : public Usermod {
|
||||
_relay[i].active = false;
|
||||
_relay[i].state = false;
|
||||
_relay[i].external = false;
|
||||
_relay[i].button = -1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -258,14 +263,15 @@ class MultiRelay : public Usermod {
|
||||
// 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)) {
|
||||
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
|
||||
_relay[i].pin = -1; // allocation failed
|
||||
} else {
|
||||
switchRelay(i, _relay[i].state = (bool)bri);
|
||||
if (!_relay[i].external) _relay[i].state = offMode;
|
||||
switchRelay(i, _relay[i].state);
|
||||
_relay[i].active = false;
|
||||
}
|
||||
}
|
||||
_oldBrightness = (bool)bri;
|
||||
_oldMode = offMode;
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
@@ -281,24 +287,110 @@ class MultiRelay : public Usermod {
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop() {
|
||||
yield();
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
|
||||
static unsigned long lastUpdate = 0;
|
||||
if (millis() - lastUpdate < 200) return; // update only 5 times/s
|
||||
if (millis() - lastUpdate < 100) return; // update only 10 times/s
|
||||
lastUpdate = millis();
|
||||
|
||||
//set relay when LEDs turn on
|
||||
if (_oldBrightness != (bool)bri) {
|
||||
_oldBrightness = (bool)bri;
|
||||
if (_oldMode != offMode) {
|
||||
_oldMode = offMode;
|
||||
_switchTimerStart = millis();
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin>=0) _relay[i].active = true;
|
||||
if (_relay[i].pin>=0 && !_relay[i].external) _relay[i].active = true;
|
||||
}
|
||||
}
|
||||
|
||||
handleOffTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
* handleButton() can be used to override default button behaviour. Returning true
|
||||
* will prevent button working in a default way.
|
||||
* Replicating button.cpp
|
||||
*/
|
||||
bool handleButton(uint8_t b) {
|
||||
yield();
|
||||
if (buttonType[b] == BTN_TYPE_NONE || buttonType[b] == BTN_TYPE_RESERVED || buttonType[b] == BTN_TYPE_PIR_SENSOR || buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].button == b) {
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
if (!handled) return false;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
//button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
||||
if (buttonType[b] == BTN_TYPE_SWITCH) {
|
||||
//handleSwitch(b);
|
||||
if (buttonPressedBefore[b] != isButtonPressed(b)) {
|
||||
buttonPressedTime[b] = now;
|
||||
buttonPressedBefore[b] = !buttonPressedBefore[b];
|
||||
}
|
||||
|
||||
if (buttonLongPressed[b] == buttonPressedBefore[b]) return handled;
|
||||
|
||||
if (now - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin>=0 && _relay[i].button == b) {
|
||||
switchRelay(i, buttonPressedBefore[b]);
|
||||
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
|
||||
}
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
//momentary button logic
|
||||
if (isButtonPressed(b)) { //pressed
|
||||
|
||||
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
|
||||
buttonPressedBefore[b] = true;
|
||||
|
||||
if (now - buttonPressedTime[b] > 600) { //long press
|
||||
buttonLongPressed[b] = true;
|
||||
}
|
||||
|
||||
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
|
||||
|
||||
long dur = now - buttonPressedTime[b];
|
||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {
|
||||
buttonPressedBefore[b] = false;
|
||||
return handled;
|
||||
} //too short "press", debounce
|
||||
bool doublePress = buttonWaitTime[b]; //did we have short press before?
|
||||
buttonWaitTime[b] = 0;
|
||||
|
||||
if (!buttonLongPressed[b]) { //short press
|
||||
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
||||
if (doublePress) {
|
||||
//doublePressAction(b);
|
||||
} else {
|
||||
buttonWaitTime[b] = now;
|
||||
}
|
||||
}
|
||||
buttonPressedBefore[b] = false;
|
||||
buttonLongPressed[b] = false;
|
||||
}
|
||||
// if 450ms elapsed since last press/release it is a short press
|
||||
if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
|
||||
buttonWaitTime[b] = 0;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin>=0 && _relay[i].button == b) {
|
||||
toggleRelay(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
*/
|
||||
@@ -310,6 +402,26 @@ class MultiRelay : public Usermod {
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
|
||||
infoArr.add(String(getActiveRelayCount()));
|
||||
|
||||
String uiDomString;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin<0 || !_relay[i].external) continue;
|
||||
uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_relay_str);
|
||||
uiDomString += F(":");
|
||||
uiDomString += i;
|
||||
uiDomString += F(",on:");
|
||||
uiDomString += _relay[i].state ? "false" : "true";
|
||||
uiDomString += F("}});\">");
|
||||
uiDomString += F("Relay ");
|
||||
uiDomString += i;
|
||||
uiDomString += F(" <i class=\"icons\"></i></button>");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
|
||||
infoArr.add(_relay[i].state ? "on" : "off");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +430,23 @@ class MultiRelay : public Usermod {
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject &root) {
|
||||
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
|
||||
JsonObject multiRelay = root[FPSTR(_name)];
|
||||
if (multiRelay.isNull()) {
|
||||
multiRelay = root.createNestedObject(FPSTR(_name));
|
||||
}
|
||||
#if MULTI_RELAY_MAX_RELAYS > 1
|
||||
JsonArray rel_arr = multiRelay.createNestedArray(F("relays"));
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin < 0) continue;
|
||||
JsonObject relay = rel_arr.createNestedObject();
|
||||
relay[FPSTR(_relay_str)] = i;
|
||||
relay[F("state")] = _relay[i].state;
|
||||
}
|
||||
#else
|
||||
multiRelay[FPSTR(_relay_str)] = 0;
|
||||
multiRelay[F("state")] = _relay[0].state;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,6 +454,20 @@ class MultiRelay : public Usermod {
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject &root) {
|
||||
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
|
||||
JsonObject usermod = root[FPSTR(_name)];
|
||||
if (!usermod.isNull()) {
|
||||
if (usermod["on"].is<bool>() && usermod[FPSTR(_relay_str)].is<int>() && usermod[FPSTR(_relay_str)].as<int>()>=0) {
|
||||
switchRelay(usermod[FPSTR(_relay_str)].as<int>(), usermod["on"].as<bool>());
|
||||
}
|
||||
} else if (root[FPSTR(_name)].is<JsonArray>()) {
|
||||
JsonArray relays = root[FPSTR(_name)].as<JsonArray>();
|
||||
for (JsonVariant r : relays) {
|
||||
if (r["on"].is<bool>() && r[FPSTR(_relay_str)].is<int>() && r[FPSTR(_relay_str)].as<int>()>=0) {
|
||||
switchRelay(r[FPSTR(_relay_str)].as<int>(), r["on"].as<bool>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,11 +478,13 @@ class MultiRelay : public Usermod {
|
||||
|
||||
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;
|
||||
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
|
||||
JsonObject relay = top.createNestedObject(parName);
|
||||
relay["pin"] = _relay[i].pin;
|
||||
relay[FPSTR(_activeHigh)] = _relay[i].mode;
|
||||
relay[FPSTR(_delay_str)] = _relay[i].delay;
|
||||
relay[FPSTR(_external)] = _relay[i].external;
|
||||
relay[FPSTR(_button)] = _relay[i].button;
|
||||
}
|
||||
DEBUG_PRINTLN(F("MultiRelay config saved."));
|
||||
}
|
||||
@@ -363,12 +508,20 @@ class MultiRelay : public Usermod {
|
||||
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 += "-";
|
||||
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
|
||||
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].button = top[parName][FPSTR(_button)] | _relay[i].button;
|
||||
// begin backwards compatibility (beta) remove when 0.13 is released
|
||||
parName += '-';
|
||||
_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;
|
||||
// end compatibility
|
||||
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
|
||||
}
|
||||
|
||||
@@ -380,12 +533,14 @@ class MultiRelay : public Usermod {
|
||||
// deallocate all pins 1st
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++)
|
||||
if (oldPin[i]>=0) {
|
||||
pinManager.deallocatePin(oldPin[i]);
|
||||
pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay);
|
||||
}
|
||||
// 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);
|
||||
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
|
||||
if (!_relay[i].external) {
|
||||
switchRelay(i, offMode);
|
||||
}
|
||||
} else {
|
||||
_relay[i].pin = -1;
|
||||
}
|
||||
@@ -394,7 +549,7 @@ class MultiRelay : public Usermod {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
return !top[F("relay-0")][FPSTR(_button)].isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,3 +569,4 @@ 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";
|
||||
const char MultiRelay::_button[] PROGMEM = "button";
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* I've had good results with settings around 5 (20 fps).
|
||||
*
|
||||
*/
|
||||
#include "wled.h"
|
||||
|
||||
const uint8_t PCARS_dimcolor = 20;
|
||||
WiFiUDP UDP;
|
||||
const unsigned int PCARS_localUdpPort = 5606; // local port to listen on
|
||||
@@ -49,11 +51,12 @@ void PCARS_readValues() {
|
||||
void PCARS_buildcolorbars() {
|
||||
boolean activated = false;
|
||||
float ledratio = 0;
|
||||
uint16_t totalLen = strip.getLengthTotal();
|
||||
|
||||
for (uint16_t i = 0; i < ledCount; i++) {
|
||||
for (uint16_t i = 0; i < totalLen; i++) {
|
||||
if (PCARS_rpmRatio < .95 || (millis() % 100 > 70 )) {
|
||||
|
||||
ledratio = (float)i / (float)ledCount;
|
||||
ledratio = (float)i / (float)totalLen;
|
||||
if (ledratio < PCARS_rpmRatio) {
|
||||
activated = true;
|
||||
} else {
|
||||
|
||||
755
usermods/quinled-an-penta/quinled-an-penta.h
Normal file
755
usermods/quinled-an-penta/quinled-an-penta.h
Normal file
@@ -0,0 +1,755 @@
|
||||
#pragma once
|
||||
|
||||
#include "U8g2lib.h"
|
||||
#include "SHT85.h"
|
||||
#include "Wire.h"
|
||||
#include "wled.h"
|
||||
|
||||
class QuinLEDAnPentaUsermod : public Usermod
|
||||
{
|
||||
private:
|
||||
bool enabled = false;
|
||||
bool firstRunDone = false;
|
||||
bool initDone = false;
|
||||
U8G2 *oledDisplay = nullptr;
|
||||
SHT *sht30TempHumidSensor;
|
||||
|
||||
// Network info vars
|
||||
bool networkHasChanged = false;
|
||||
bool lastKnownNetworkConnected;
|
||||
IPAddress lastKnownIp;
|
||||
bool lastKnownWiFiConnected;
|
||||
String lastKnownSsid;
|
||||
bool lastKnownApActive;
|
||||
char *lastKnownApSsid;
|
||||
char *lastKnownApPass;
|
||||
byte lastKnownApChannel;
|
||||
int lastKnownEthType;
|
||||
bool lastKnownEthLinkUp;
|
||||
|
||||
// Brightness / LEDC vars
|
||||
byte lastKnownBri = 0;
|
||||
int8_t currentBussesNumPins[5] = {0, 0, 0, 0, 0};
|
||||
int8_t currentLedPins[5] = {0, 0, 0, 0, 0};
|
||||
uint8_t currentLedcReads[5] = {0, 0, 0, 0, 0};
|
||||
uint8_t lastKnownLedcReads[5] = {0, 0, 0, 0, 0};
|
||||
|
||||
// OLED vars
|
||||
bool oledEnabled = false;
|
||||
bool oledInitDone = false;
|
||||
bool oledUseProgressBars = false;
|
||||
bool oledFlipScreen = false;
|
||||
bool oledFixBuggedScreen = false;
|
||||
byte oledMaxPage = 3;
|
||||
byte oledCurrentPage = 3; // Start with the network page to help identifying the IP
|
||||
byte oledSecondsPerPage = 10;
|
||||
unsigned long oledLogoDrawn = 0;
|
||||
unsigned long oledLastTimeUpdated = 0;
|
||||
unsigned long oledLastTimePageChange = 0;
|
||||
unsigned long oledLastTimeFixBuggedScreen = 0;
|
||||
|
||||
// SHT30 vars
|
||||
bool shtEnabled = false;
|
||||
bool shtInitDone = false;
|
||||
bool shtReadDataSuccess = false;
|
||||
byte shtI2cAddress = 0x44;
|
||||
unsigned long shtLastTimeUpdated = 0;
|
||||
bool shtDataRequested = false;
|
||||
float shtCurrentTemp = 0;
|
||||
float shtLastKnownTemp = 0;
|
||||
float shtCurrentHumidity = 0;
|
||||
float shtLastKnownHumidity = 0;
|
||||
|
||||
// Pin/IO vars
|
||||
const int8_t anPentaPins[5] = {14, 13, 12, 4, 2};
|
||||
int8_t oledSpiClk = 15;
|
||||
int8_t oledSpiData = 16;
|
||||
int8_t oledSpiCs = 0;
|
||||
int8_t oledSpiDc = 32;
|
||||
int8_t oledSpiRst = 33;
|
||||
int8_t shtSda = 1;
|
||||
int8_t shtScl = 3;
|
||||
|
||||
|
||||
bool isAnPentaLedPin(int8_t pin)
|
||||
{
|
||||
for(int8_t i = 0; i <= 4; i++)
|
||||
{
|
||||
if(anPentaPins[i] == pin)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void getCurrentUsedLedPins()
|
||||
{
|
||||
for (int8_t lp = 0; lp <= 4; lp++) currentLedPins[lp] = 0;
|
||||
byte numBusses = busses.getNumBusses();
|
||||
byte numUsedPins = 0;
|
||||
|
||||
for (int8_t b = 0; b < numBusses; b++) {
|
||||
Bus* curBus = busses.getBus(b);
|
||||
if (curBus != nullptr) {
|
||||
uint8_t pins[5] = {0, 0, 0, 0, 0};
|
||||
currentBussesNumPins[b] = curBus->getPins(pins);
|
||||
for (int8_t p = 0; p < currentBussesNumPins[b]; p++) {
|
||||
if (isAnPentaLedPin(pins[p])) {
|
||||
currentLedPins[numUsedPins] = pins[p];
|
||||
numUsedPins++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getCurrentLedcValues()
|
||||
{
|
||||
byte numBusses = busses.getNumBusses();
|
||||
byte numLedc = 0;
|
||||
|
||||
for (int8_t b = 0; b < numBusses; b++) {
|
||||
Bus* curBus = busses.getBus(b);
|
||||
if (curBus != nullptr) {
|
||||
uint32_t curPixColor = curBus->getPixelColor(0);
|
||||
uint8_t _data[5] = {255, 255, 255, 255, 255};
|
||||
_data[3] = curPixColor >> 24;
|
||||
_data[0] = curPixColor >> 16;
|
||||
_data[1] = curPixColor >> 8;
|
||||
_data[2] = curPixColor;
|
||||
|
||||
for (uint8_t i = 0; i < currentBussesNumPins[b]; i++) {
|
||||
currentLedcReads[numLedc] = (_data[i] * bri) / 255;
|
||||
numLedc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void initOledDisplay()
|
||||
{
|
||||
PinManagerPinType pins[5] = { { oledSpiClk, true }, { oledSpiData, true }, { oledSpiCs, true }, { oledSpiDc, true }, { oledSpiRst, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_QuinLEDAnPenta)) {
|
||||
DEBUG_PRINTF("[%s] OLED pin allocation failed!\n", _name);
|
||||
oledEnabled = oledInitDone = false;
|
||||
return;
|
||||
}
|
||||
|
||||
oledDisplay = (U8G2 *) new U8G2_SSD1306_128X64_NONAME_2_4W_SW_SPI(U8G2_R0, oledSpiClk, oledSpiData, oledSpiCs, oledSpiDc, oledSpiRst);
|
||||
if (oledDisplay == nullptr) {
|
||||
DEBUG_PRINTF("[%s] OLED init failed!\n", _name);
|
||||
oledEnabled = oledInitDone = false;
|
||||
return;
|
||||
}
|
||||
|
||||
oledDisplay->begin();
|
||||
oledDisplay->setBusClock(40 * 1000 * 1000);
|
||||
oledDisplay->setContrast(10);
|
||||
oledDisplay->setPowerSave(0);
|
||||
oledDisplay->setFont(u8g2_font_6x10_tf);
|
||||
oledDisplay->setFlipMode(oledFlipScreen);
|
||||
|
||||
oledDisplay->firstPage();
|
||||
do {
|
||||
oledDisplay->drawXBMP(0, 16, 128, 36, quinLedLogo);
|
||||
} while (oledDisplay->nextPage());
|
||||
oledLogoDrawn = millis();
|
||||
|
||||
oledInitDone = true;
|
||||
}
|
||||
|
||||
void cleanupOledDisplay()
|
||||
{
|
||||
if (oledInitDone) {
|
||||
oledDisplay->clear();
|
||||
}
|
||||
|
||||
pinManager.deallocatePin(oledSpiClk, PinOwner::UM_QuinLEDAnPenta);
|
||||
pinManager.deallocatePin(oledSpiData, PinOwner::UM_QuinLEDAnPenta);
|
||||
pinManager.deallocatePin(oledSpiCs, PinOwner::UM_QuinLEDAnPenta);
|
||||
pinManager.deallocatePin(oledSpiDc, PinOwner::UM_QuinLEDAnPenta);
|
||||
pinManager.deallocatePin(oledSpiRst, PinOwner::UM_QuinLEDAnPenta);
|
||||
|
||||
delete oledDisplay;
|
||||
|
||||
oledEnabled = false;
|
||||
oledInitDone = false;
|
||||
}
|
||||
|
||||
bool isOledReady()
|
||||
{
|
||||
return oledEnabled && oledInitDone;
|
||||
}
|
||||
|
||||
void initSht30TempHumiditySensor()
|
||||
{
|
||||
PinManagerPinType pins[2] = { { shtSda, true }, { shtScl, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_QuinLEDAnPenta)) {
|
||||
DEBUG_PRINTF("[%s] SHT30 pin allocation failed!\n", _name);
|
||||
shtEnabled = shtInitDone = false;
|
||||
return;
|
||||
}
|
||||
|
||||
TwoWire *wire = new TwoWire(1);
|
||||
wire->setClock(400000);
|
||||
|
||||
sht30TempHumidSensor = (SHT *) new SHT30();
|
||||
sht30TempHumidSensor->begin(shtI2cAddress, wire);
|
||||
// The SHT lib calls wire.begin() again without the SDA and SCL pins... So call it again here...
|
||||
wire->begin(shtSda, shtScl);
|
||||
if (sht30TempHumidSensor->readStatus() == 0xFFFF) {
|
||||
DEBUG_PRINTF("[%s] SHT30 init failed!\n", _name);
|
||||
shtEnabled = shtInitDone = false;
|
||||
return;
|
||||
}
|
||||
|
||||
shtInitDone = true;
|
||||
}
|
||||
|
||||
void cleanupSht30TempHumiditySensor()
|
||||
{
|
||||
if (shtInitDone) {
|
||||
sht30TempHumidSensor->reset();
|
||||
}
|
||||
|
||||
pinManager.deallocatePin(shtSda, PinOwner::UM_QuinLEDAnPenta);
|
||||
pinManager.deallocatePin(shtScl, PinOwner::UM_QuinLEDAnPenta);
|
||||
|
||||
delete sht30TempHumidSensor;
|
||||
|
||||
shtEnabled = false;
|
||||
shtInitDone = false;
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
if (isOledReady()) {
|
||||
cleanupOledDisplay();
|
||||
}
|
||||
|
||||
if (isShtReady()) {
|
||||
cleanupSht30TempHumiditySensor();
|
||||
}
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool oledCheckForNetworkChanges()
|
||||
{
|
||||
if (lastKnownNetworkConnected != Network.isConnected() || lastKnownIp != Network.localIP()
|
||||
|| lastKnownWiFiConnected != WiFi.isConnected() || lastKnownSsid != WiFi.SSID()
|
||||
|| lastKnownApActive != apActive || lastKnownApSsid != apSSID || lastKnownApPass != apPass || lastKnownApChannel != apChannel) {
|
||||
lastKnownNetworkConnected = Network.isConnected();
|
||||
lastKnownIp = Network.localIP();
|
||||
lastKnownWiFiConnected = WiFi.isConnected();
|
||||
lastKnownSsid = WiFi.SSID();
|
||||
lastKnownApActive = apActive;
|
||||
lastKnownApSsid = apSSID;
|
||||
lastKnownApPass = apPass;
|
||||
lastKnownApChannel = apChannel;
|
||||
|
||||
return networkHasChanged = true;
|
||||
}
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
if (lastKnownEthType != ethernetType || lastKnownEthLinkUp != ETH.linkUp()) {
|
||||
lastKnownEthType = ethernetType;
|
||||
lastKnownEthLinkUp = ETH.linkUp();
|
||||
|
||||
return networkHasChanged = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return networkHasChanged = false;
|
||||
}
|
||||
|
||||
byte oledGetNextPage()
|
||||
{
|
||||
return oledCurrentPage + 1 <= oledMaxPage ? oledCurrentPage + 1 : 1;
|
||||
}
|
||||
|
||||
void oledShowPage(byte page, bool updateLastTimePageChange = false)
|
||||
{
|
||||
oledCurrentPage = page;
|
||||
updateOledDisplay();
|
||||
oledLastTimeUpdated = millis();
|
||||
if (updateLastTimePageChange) oledLastTimePageChange = oledLastTimeUpdated;
|
||||
}
|
||||
|
||||
/*
|
||||
* Page 1: Overall brightness and LED outputs
|
||||
* Page 2: General info like temp, humidity and others
|
||||
* Page 3: Network info
|
||||
*/
|
||||
void updateOledDisplay()
|
||||
{
|
||||
if (!isOledReady()) return;
|
||||
|
||||
oledDisplay->firstPage();
|
||||
do {
|
||||
oledDisplay->setFont(u8g2_font_chroma48medium8_8r);
|
||||
oledDisplay->drawStr(0, 8, serverDescription);
|
||||
oledDisplay->drawHLine(0, 13, 127);
|
||||
oledDisplay->setFont(u8g2_font_6x10_tf);
|
||||
|
||||
byte charPerRow = 21;
|
||||
byte oledRow = 23;
|
||||
switch (oledCurrentPage) {
|
||||
// LED Outputs
|
||||
case 1:
|
||||
{
|
||||
char charCurrentBrightness[charPerRow+1] = "Brightness:";
|
||||
if (oledUseProgressBars) {
|
||||
oledDisplay->drawStr(0, oledRow, charCurrentBrightness);
|
||||
// There is no method to draw a filled box with rounded corners. So draw the rounded frame first, then fill that frame accordingly to LED percentage
|
||||
oledDisplay->drawRFrame(68, oledRow - 6, 60, 7, 2);
|
||||
oledDisplay->drawBox(69, oledRow - 5, int(round(58*getPercentageForBrightness(bri)) / 100), 5);
|
||||
}
|
||||
else {
|
||||
sprintf(charCurrentBrightness, "%s %d%%", charCurrentBrightness, getPercentageForBrightness(bri));
|
||||
oledDisplay->drawStr(0, oledRow, charCurrentBrightness);
|
||||
}
|
||||
oledRow += 8;
|
||||
|
||||
byte drawnLines = 0;
|
||||
for (int8_t app = 0; app <= 4; app++) {
|
||||
for (int8_t clp = 0; clp <= 4; clp++) {
|
||||
if (anPentaPins[app] == currentLedPins[clp]) {
|
||||
char charCurrentLedcReads[17];
|
||||
sprintf(charCurrentLedcReads, "LED %d:", app+1);
|
||||
if (oledUseProgressBars) {
|
||||
oledDisplay->drawStr(0, oledRow+(drawnLines*8), charCurrentLedcReads);
|
||||
oledDisplay->drawRFrame(38, oledRow - 6 + (drawnLines * 8), 90, 7, 2);
|
||||
oledDisplay->drawBox(39, oledRow - 5 + (drawnLines * 8), int(round(88*getPercentageForBrightness(currentLedcReads[clp])) / 100), 5);
|
||||
}
|
||||
else {
|
||||
sprintf(charCurrentLedcReads, "%s %d%%", charCurrentLedcReads, getPercentageForBrightness(currentLedcReads[clp]));
|
||||
oledDisplay->drawStr(0, oledRow+(drawnLines*8), charCurrentLedcReads);
|
||||
}
|
||||
|
||||
drawnLines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Various info
|
||||
case 2:
|
||||
{
|
||||
if (isShtReady() && shtReadDataSuccess) {
|
||||
char charShtCurrentTemp[charPerRow+4]; // Reserve 3 more bytes than usual as we gonna have one UTF8 char which can be up to 4 bytes.
|
||||
sprintf(charShtCurrentTemp, "Temperature: %.02f°C", shtCurrentTemp);
|
||||
char charShtCurrentHumidity[charPerRow+1];
|
||||
sprintf(charShtCurrentHumidity, "Humidity: %.02f RH", shtCurrentHumidity);
|
||||
|
||||
oledDisplay->drawUTF8(0, oledRow, charShtCurrentTemp);
|
||||
oledDisplay->drawStr(0, oledRow + 10, charShtCurrentHumidity);
|
||||
oledRow += 20;
|
||||
}
|
||||
|
||||
if (mqttEnabled && mqttServer[0] != 0) {
|
||||
char charMqttStatus[charPerRow+1];
|
||||
sprintf(charMqttStatus, "MQTT: %s", (WLED_MQTT_CONNECTED ? "Connected" : "Disconnected"));
|
||||
oledDisplay->drawStr(0, oledRow, charMqttStatus);
|
||||
oledRow += 10;
|
||||
}
|
||||
|
||||
// Always draw these two on the bottom
|
||||
char charUptime[charPerRow+1];
|
||||
sprintf(charUptime, "Uptime: %ds", int(millis()/1000 + rolloverMillis*4294967)); // From json.cpp
|
||||
oledDisplay->drawStr(0, 53, charUptime);
|
||||
|
||||
char charWledVersion[charPerRow+1];
|
||||
sprintf(charWledVersion, "WLED v%s", versionString);
|
||||
oledDisplay->drawStr(0, 63, charWledVersion);
|
||||
break;
|
||||
}
|
||||
|
||||
// Network Info
|
||||
case 3:
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
if (lastKnownEthType == WLED_ETH_NONE) {
|
||||
oledDisplay->drawStr(0, oledRow, "Ethernet: No board selected");
|
||||
oledRow += 10;
|
||||
}
|
||||
else if (!lastKnownEthLinkUp) {
|
||||
oledDisplay->drawStr(0, oledRow, "Ethernet: Link Down");
|
||||
oledRow += 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lastKnownNetworkConnected) {
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
if (lastKnownEthLinkUp) {
|
||||
oledDisplay->drawStr(0, oledRow, "Ethernet: Link Up");
|
||||
oledRow += 10;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
// Wi-Fi can be active with ETH being connected, but we don't mind...
|
||||
if (lastKnownWiFiConnected) {
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
if (!lastKnownEthLinkUp) {
|
||||
#endif
|
||||
|
||||
oledDisplay->drawStr(0, oledRow, "Wi-Fi: Connected");
|
||||
char currentSsidChar[lastKnownSsid.length() + 1];
|
||||
lastKnownSsid.toCharArray(currentSsidChar, lastKnownSsid.length() + 1);
|
||||
char charCurrentSsid[50];
|
||||
sprintf(charCurrentSsid, "SSID: %s", currentSsidChar);
|
||||
oledDisplay->drawStr(0, oledRow + 10, charCurrentSsid);
|
||||
oledRow += 20;
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
String currentIpStr = lastKnownIp.toString();
|
||||
char currentIpChar[currentIpStr.length() + 1];
|
||||
currentIpStr.toCharArray(currentIpChar, currentIpStr.length() + 1);
|
||||
char charCurrentIp[30];
|
||||
sprintf(charCurrentIp, "IP: %s", currentIpChar);
|
||||
oledDisplay->drawStr(0, oledRow, charCurrentIp);
|
||||
}
|
||||
// If WLED AP is active. Theoretically, it can even be active with ETH being connected, but we don't mind...
|
||||
else if (lastKnownApActive) {
|
||||
char charCurrentApStatus[charPerRow+1];
|
||||
sprintf(charCurrentApStatus, "WLED AP: %s (Ch: %d)", (lastKnownApActive ? "On" : "Off"), lastKnownApChannel);
|
||||
oledDisplay->drawStr(0, oledRow, charCurrentApStatus);
|
||||
|
||||
char charCurrentApSsid[charPerRow+1];
|
||||
sprintf(charCurrentApSsid, "SSID: %s", lastKnownApSsid);
|
||||
oledDisplay->drawStr(0, oledRow + 10, charCurrentApSsid);
|
||||
|
||||
char charCurrentApPass[charPerRow+1];
|
||||
sprintf(charCurrentApPass, "PW: %s", lastKnownApPass);
|
||||
oledDisplay->drawStr(0, oledRow + 20, charCurrentApPass);
|
||||
|
||||
// IP is hardcoded / no var exists in WLED at the time this mod was coded, so also hardcode it here
|
||||
oledDisplay->drawStr(0, oledRow + 30, "IP: 4.3.2.1");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} while (oledDisplay->nextPage());
|
||||
}
|
||||
|
||||
bool isShtReady()
|
||||
{
|
||||
return shtEnabled && shtInitDone;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _oledEnabled[];
|
||||
static const char _oledUseProgressBars[];
|
||||
static const char _oledFlipScreen[];
|
||||
static const char _oledSecondsPerPage[];
|
||||
static const char _oledFixBuggedScreen[];
|
||||
static const char _shtEnabled[];
|
||||
static const unsigned char quinLedLogo[];
|
||||
|
||||
|
||||
static int8_t getPercentageForBrightness(byte brightness)
|
||||
{
|
||||
return int(((float)brightness / (float)255) * 100);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
if (enabled) {
|
||||
lastKnownBri = bri;
|
||||
|
||||
if (oledEnabled) {
|
||||
initOledDisplay();
|
||||
}
|
||||
|
||||
if (shtEnabled) {
|
||||
initSht30TempHumiditySensor();
|
||||
}
|
||||
|
||||
getCurrentUsedLedPins();
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
firstRunDone = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
if (!enabled || !initDone || strip.isUpdating()) return;
|
||||
|
||||
if (isShtReady()) {
|
||||
if (millis() - shtLastTimeUpdated > 30000 && !shtDataRequested) {
|
||||
sht30TempHumidSensor->requestData();
|
||||
shtDataRequested = true;
|
||||
|
||||
shtLastTimeUpdated = millis();
|
||||
}
|
||||
|
||||
if (shtDataRequested) {
|
||||
if (sht30TempHumidSensor->dataReady()) {
|
||||
if (sht30TempHumidSensor->readData()) {
|
||||
shtCurrentTemp = sht30TempHumidSensor->getTemperature();
|
||||
shtCurrentHumidity = sht30TempHumidSensor->getHumidity();
|
||||
shtReadDataSuccess = true;
|
||||
}
|
||||
else {
|
||||
shtReadDataSuccess = false;
|
||||
}
|
||||
|
||||
shtDataRequested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isOledReady() && millis() - oledLogoDrawn > 3000) {
|
||||
// Check for changes on the current page and update the OLED if a change is detected
|
||||
if (millis() - oledLastTimeUpdated > 150) {
|
||||
// If there was a network change, force page 3 (network page)
|
||||
if (oledCheckForNetworkChanges()) {
|
||||
oledCurrentPage = 3;
|
||||
}
|
||||
// Only redraw a page if there was a change for that page
|
||||
switch (oledCurrentPage) {
|
||||
case 1:
|
||||
lastKnownBri = bri;
|
||||
// Probably causes lag to always do ledcRead(), so rather re-do the math, 'cause we can't easily get it...
|
||||
getCurrentLedcValues();
|
||||
|
||||
if (bri != lastKnownBri || lastKnownLedcReads[0] != currentLedcReads[0] || lastKnownLedcReads[1] != currentLedcReads[1] || lastKnownLedcReads[2] != currentLedcReads[2]
|
||||
|| lastKnownLedcReads[3] != currentLedcReads[3] || lastKnownLedcReads[4] != currentLedcReads[4]) {
|
||||
lastKnownLedcReads[0] = currentLedcReads[0]; lastKnownLedcReads[1] = currentLedcReads[1]; lastKnownLedcReads[2] = currentLedcReads[2]; lastKnownLedcReads[3] = currentLedcReads[3]; lastKnownLedcReads[4] = currentLedcReads[4];
|
||||
|
||||
oledShowPage(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (shtLastKnownTemp != shtCurrentTemp || shtLastKnownHumidity != shtCurrentHumidity) {
|
||||
shtLastKnownTemp = shtCurrentTemp;
|
||||
shtLastKnownHumidity = shtCurrentHumidity;
|
||||
|
||||
oledShowPage(2);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (networkHasChanged) {
|
||||
networkHasChanged = false;
|
||||
|
||||
oledShowPage(3, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cycle through OLED pages
|
||||
if (millis() - oledLastTimePageChange > oledSecondsPerPage * 1000) {
|
||||
// Periodically fixing a "bugged out" OLED. More details in the ReadMe
|
||||
if (oledFixBuggedScreen && millis() - oledLastTimeFixBuggedScreen > 60000) {
|
||||
oledDisplay->begin();
|
||||
oledLastTimeFixBuggedScreen = millis();
|
||||
}
|
||||
oledShowPage(oledGetNextPage(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_oledEnabled)] = oledEnabled;
|
||||
top[FPSTR(_oledUseProgressBars)] = oledUseProgressBars;
|
||||
top[FPSTR(_oledFlipScreen)] = oledFlipScreen;
|
||||
top[FPSTR(_oledSecondsPerPage)] = oledSecondsPerPage;
|
||||
top[FPSTR(_oledFixBuggedScreen)] = oledFixBuggedScreen;
|
||||
top[FPSTR(_shtEnabled)] = shtEnabled;
|
||||
|
||||
// Update LED pins on config save
|
||||
getCurrentUsedLedPins();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTF("[%s] No config found. (Using defaults.)\n", _name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool oldEnabled = enabled;
|
||||
bool oldOledEnabled = oledEnabled;
|
||||
bool oldOledFlipScreen = oledFlipScreen;
|
||||
bool oldShtEnabled = shtEnabled;
|
||||
|
||||
getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
getJsonValue(top[FPSTR(_oledEnabled)], oledEnabled);
|
||||
getJsonValue(top[FPSTR(_oledUseProgressBars)], oledUseProgressBars);
|
||||
getJsonValue(top[FPSTR(_oledFlipScreen)], oledFlipScreen);
|
||||
getJsonValue(top[FPSTR(_oledSecondsPerPage)], oledSecondsPerPage);
|
||||
getJsonValue(top[FPSTR(_oledFixBuggedScreen)], oledFixBuggedScreen);
|
||||
getJsonValue(top[FPSTR(_shtEnabled)], shtEnabled);
|
||||
|
||||
// First run: reading from cfg.json, nothing to do here, will be all done in setup()
|
||||
if (!firstRunDone) {
|
||||
DEBUG_PRINTF("[%s] First run, nothing to do\n", _name);
|
||||
}
|
||||
// Check if mod has been en-/disabled
|
||||
else if (enabled != oldEnabled) {
|
||||
enabled ? setup() : cleanup();
|
||||
DEBUG_PRINTF("[%s] Usermod has been en-/disabled\n", _name);
|
||||
}
|
||||
// Config has been changed, so adopt to changes
|
||||
else if (enabled) {
|
||||
if (oldOledEnabled != oledEnabled) {
|
||||
oledEnabled ? initOledDisplay() : cleanupOledDisplay();
|
||||
}
|
||||
else if (oledEnabled && oldOledFlipScreen != oledFlipScreen) {
|
||||
oledDisplay->clear();
|
||||
oledDisplay->setFlipMode(oledFlipScreen);
|
||||
oledShowPage(oledCurrentPage);
|
||||
}
|
||||
|
||||
if (oldShtEnabled != shtEnabled) {
|
||||
shtEnabled ? initSht30TempHumiditySensor() : cleanupSht30TempHumiditySensor();
|
||||
}
|
||||
|
||||
DEBUG_PRINTF("[%s] Config (re)loaded\n", _name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
if (!enabled && !isShtReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray jsonTemp = user.createNestedArray("Temperature");
|
||||
JsonArray jsonHumidity = user.createNestedArray("Humidity");
|
||||
|
||||
if (shtLastTimeUpdated == 0 || !shtReadDataSuccess) {
|
||||
jsonTemp.add(0);
|
||||
jsonHumidity.add(0);
|
||||
if (shtLastTimeUpdated == 0) {
|
||||
jsonTemp.add(" Not read yet");
|
||||
jsonHumidity.add(" Not read yet");
|
||||
}
|
||||
else {
|
||||
jsonTemp.add(" Error");
|
||||
jsonHumidity.add(" Error");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
jsonHumidity.add(shtCurrentHumidity);
|
||||
jsonHumidity.add(" RH");
|
||||
|
||||
jsonTemp.add(shtCurrentTemp);
|
||||
jsonTemp.add(" °C");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_QUINLED_AN_PENTA;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
// Config settings
|
||||
const char QuinLEDAnPentaUsermod::_name[] PROGMEM = "QuinLED-An-Penta";
|
||||
const char QuinLEDAnPentaUsermod::_enabled[] PROGMEM = "Enabled";
|
||||
const char QuinLEDAnPentaUsermod::_oledEnabled[] PROGMEM = "Enable-OLED";
|
||||
const char QuinLEDAnPentaUsermod::_oledUseProgressBars[] PROGMEM = "OLED-Use-Progress-Bars";
|
||||
const char QuinLEDAnPentaUsermod::_oledFlipScreen[] PROGMEM = "OLED-Flip-Screen-180";
|
||||
const char QuinLEDAnPentaUsermod::_oledSecondsPerPage[] PROGMEM = "OLED-Seconds-Per-Page";
|
||||
const char QuinLEDAnPentaUsermod::_oledFixBuggedScreen[] PROGMEM = "OLED-Fix-Bugged-Screen";
|
||||
const char QuinLEDAnPentaUsermod::_shtEnabled[] PROGMEM = "Enable-SHT30-Temp-Humidity-Sensor";
|
||||
// Other strings
|
||||
|
||||
const unsigned char QuinLEDAnPentaUsermod::quinLedLogo[] PROGMEM = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFD, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x80, 0xFF,
|
||||
0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0x07, 0xFE, 0xFF, 0xFF, 0x0F, 0xFC,
|
||||
0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0x0F, 0xFE,
|
||||
0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0xE3, 0xFF, 0xA5, 0xFF, 0xFF, 0xFF,
|
||||
0x0F, 0xFC, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xE1, 0xFF,
|
||||
0x00, 0xF0, 0xE3, 0xFF, 0x0F, 0xFE, 0x1F, 0xFE, 0xFF, 0xFF, 0x3F, 0xFF,
|
||||
0xFF, 0xFF, 0xE3, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0x07, 0xFE, 0x1F, 0xFC,
|
||||
0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0x00, 0xF0, 0x00, 0xFE,
|
||||
0x07, 0xFF, 0x1F, 0xFC, 0xF0, 0xC7, 0x3F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF,
|
||||
0xF1, 0xFF, 0x00, 0xFC, 0x07, 0xFF, 0x1F, 0xFE, 0xF0, 0xC3, 0x1F, 0xFE,
|
||||
0x00, 0xFF, 0xE1, 0xFF, 0xF1, 0xFF, 0x30, 0xF8, 0x07, 0xFF, 0x1F, 0xFE,
|
||||
0xF0, 0xC3, 0x1F, 0xFE, 0x00, 0xFC, 0xC3, 0xFF, 0xE1, 0xFF, 0xF0, 0xF0,
|
||||
0x03, 0xFF, 0x0F, 0x7E, 0xF0, 0xC3, 0x1F, 0x7E, 0x00, 0xF8, 0xE3, 0xFF,
|
||||
0xE1, 0xFF, 0xF1, 0xF1, 0x83, 0xFF, 0x0F, 0x7E, 0xF0, 0xC3, 0x1F, 0x7E,
|
||||
0x00, 0xF0, 0xC3, 0xFF, 0xE1, 0xFF, 0xF1, 0xE1, 0x83, 0xFF, 0x0F, 0xFE,
|
||||
0xF0, 0xC3, 0x1F, 0xFE, 0xF8, 0xF0, 0xC3, 0xFF, 0xA1, 0xFF, 0xF1, 0xE3,
|
||||
0x81, 0xFF, 0x0F, 0x7E, 0xF0, 0xC1, 0x1F, 0x7E, 0xF0, 0xF0, 0xC3, 0xFF,
|
||||
0x01, 0xF8, 0xE1, 0xC3, 0x83, 0xFF, 0x0F, 0x7F, 0xF8, 0xC3, 0x1F, 0x7E,
|
||||
0xF8, 0xF0, 0xC3, 0xFF, 0x03, 0xF8, 0xE1, 0xC7, 0x81, 0xE4, 0x0F, 0x7F,
|
||||
0xF0, 0xC3, 0x1F, 0xFE, 0xF8, 0xF0, 0xC3, 0xFF, 0x01, 0xF8, 0xE3, 0xC7,
|
||||
0x01, 0xC0, 0x07, 0x7F, 0xF8, 0xC1, 0x1F, 0x7E, 0xF0, 0xE1, 0xC3, 0xFF,
|
||||
0xC3, 0xFD, 0xE1, 0x87, 0x01, 0x00, 0x07, 0x7F, 0xF8, 0xC3, 0x1F, 0x7E,
|
||||
0xF8, 0xF0, 0xC3, 0xFF, 0xE3, 0xFF, 0xE3, 0x87, 0x01, 0x00, 0x82, 0x3F,
|
||||
0xF8, 0xE1, 0x1F, 0xFE, 0xF8, 0xE1, 0xC3, 0xFF, 0xC3, 0xFF, 0xC3, 0x87,
|
||||
0x01, 0x00, 0x80, 0x3F, 0xF8, 0xC1, 0x1F, 0x7E, 0xF0, 0xF1, 0xC3, 0xFF,
|
||||
0xC3, 0xFF, 0xC3, 0x87, 0x03, 0x0F, 0x80, 0x3F, 0xF8, 0xE1, 0x0F, 0x7E,
|
||||
0xF8, 0xE1, 0x87, 0xFF, 0xC3, 0xFF, 0xC7, 0x87, 0x03, 0x04, 0xC0, 0x7F,
|
||||
0xF0, 0xE1, 0x0F, 0xFF, 0xF8, 0xF1, 0x87, 0xFF, 0xC3, 0xFF, 0xC3, 0x87,
|
||||
0x07, 0x00, 0xE0, 0x7F, 0x00, 0xE0, 0x1F, 0x7E, 0xF0, 0xE0, 0xC3, 0xFF,
|
||||
0xC7, 0xFF, 0x87, 0x87, 0x0F, 0x00, 0xE0, 0x7F, 0x00, 0xE0, 0x0F, 0x7F,
|
||||
0xF8, 0xE1, 0x07, 0x80, 0x07, 0xEA, 0x87, 0xC1, 0x0F, 0x00, 0x80, 0xFF,
|
||||
0x00, 0xE0, 0x1F, 0x7E, 0xF0, 0xE1, 0x07, 0x00, 0x03, 0x80, 0x07, 0xC0,
|
||||
0x7F, 0x00, 0x00, 0xFF, 0x01, 0xE0, 0x1F, 0xFF, 0xF8, 0xE1, 0x07, 0x00,
|
||||
0x07, 0x00, 0x07, 0xE0, 0xFF, 0xF7, 0x01, 0xFF, 0x57, 0xF7, 0x9F, 0xFF,
|
||||
0xFC, 0xF1, 0x0F, 0x00, 0x07, 0x80, 0x0F, 0xE0, 0xFF, 0xFF, 0x03, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xBF, 0xFE,
|
||||
0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
69
usermods/quinled-an-penta/readme.md
Normal file
69
usermods/quinled-an-penta/readme.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# QuinLED-An-Penta
|
||||
The (un)official usermod to get the best out of the QuinLED-An-Penta, like using the OLED and the SHT30 temperature/humidity sensor.
|
||||
|
||||
## Requirements
|
||||
* "u8gs" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2
|
||||
* "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85
|
||||
|
||||
## Usermod installation
|
||||
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D QUINLED_AN_PENTA`.
|
||||
|
||||
ESP32 (**without** ethernet):
|
||||
```
|
||||
[env:custom_esp32dev_usermod_quinled_an_penta]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D QUINLED_AN_PENTA
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
olikraus/U8g2@~2.28.8
|
||||
robtillaart/SHT85@~0.2.0
|
||||
```
|
||||
|
||||
ESP32 (**with** ethernet):
|
||||
```
|
||||
[env:custom_esp32dev_usermod_quinled_an_penta]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D WLED_USE_ETHERNET -D QUINLED_AN_PENTA
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
olikraus/U8g2@~2.28.8
|
||||
robtillaart/SHT85@~0.2.0
|
||||
```
|
||||
|
||||
## Some words about the (optional) OLED
|
||||
This mod has been optimized for an SSD1306 driven 128x64 OLED. Using a smaller OLED or an OLED using a different driver will result in unexpected results.
|
||||
I highly recommend using these "two color monochromatic OLEDs", which have the first 16 pixels in a different color than the other 48, e.g. a yellow/blue OLED.
|
||||
Also note, you need to have an **SPI** driven OLED, **not i2c**!
|
||||
|
||||
### My OLED flickers after some time, what should I do?
|
||||
That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose it's settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display.
|
||||
If you're facing this issue, you can enable a setting I've added which will call the `begin()` roughly every 60 seconds between a page change. This will make the page change take ~500ms, but will fix the display.
|
||||
|
||||
## Configuration
|
||||
Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D QUINLED_AN_PENTA`, you will see the config for it there:
|
||||
* Enable-OLED:
|
||||
* What it does: Enabled the optional SPI driven OLED that can be mounted to the 7-pin female header
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
* OLED-Use-Progress-Bars:
|
||||
* What it does: Toggle between showing percentage numbers or a progress-bar-like visualization for overall brightness and each LED channels brightness level
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
* OLED-Flip-Screen-180:
|
||||
* What it does: Flips the screen 180° / upside-down
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
* OLED-Seconds-Per-Page:
|
||||
* What it does: Defines how long the OLED should stay on one page in seconds before changing to the next
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: 10
|
||||
* OLED-Fix-Bugged-Screen:
|
||||
* What it does: Enable this if your OLED flickers after some time. For more info read above under ["My OLED flickers after some time, what should I do?"](#My-OLED-flickers-after-some-time-what-should-I-do)
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
* Enable-SHT30-Temp-Humidity-Sensor:
|
||||
* What it does: Enabled the onboard SHT30 temperature and humidity sensor
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
|
||||
## Change log
|
||||
2021-10
|
||||
* First implementation.
|
||||
86
usermods/rgb-rotary-encoder/readme.md
Normal file
86
usermods/rgb-rotary-encoder/readme.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# RGB Encoder Board
|
||||
|
||||
This usermod-v2 adds support for the awesome RGB Rotary Encoder Board by Adam Zeloof / "Isotope Engineering" to control the overall brightness of your WLED instance: https://github.com/isotope-engineering/RGB-Encoder-Board. A great DIY rotary encoder with 20 tiny SK6805 / "NeoPixel Nano" LEDs.
|
||||
|
||||
https://user-images.githubusercontent.com/3090131/124680599-0180ab80-dec7-11eb-9065-a6d08ebe0287.mp4
|
||||
|
||||
## Credits
|
||||
The actual / original code that does the different LED modes is from Adam Zeloof. So I don't take credit for these. But I ported it to WLED, which involved replacing the LED library he used (because, guess what, WLED already has one; so no need to add another one, but use whatever WLED uses), plus the rotary encoder library, because that one was not compatible with ESP, only Arduino.
|
||||
So it was quite more work than I hoped, but I got there eventually :)
|
||||
|
||||
## Requirements
|
||||
* "ESP Rotary" by Lennart Hennigs, v1.5.0 or higher: https://github.com/LennartHennigs/ESPRotary
|
||||
|
||||
## Usermod installation
|
||||
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D RGB_ROTARY_ENCODER`.
|
||||
|
||||
ESP32:
|
||||
```
|
||||
[env:custom_esp32dev_usermod_rgb_encoder_board]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D RGB_ROTARY_ENCODER
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
lennarthennigs/ESP Rotary@^1.5.0
|
||||
```
|
||||
|
||||
ESP8266 / D1 Mini:
|
||||
```
|
||||
[env:custom_d1_mini_usermod_rgb_encoder_board]
|
||||
extends = env:d1_mini
|
||||
build_flags = ${common.build_flags_esp8266} -D RGB_ROTARY_ENCODER
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
lennarthennigs/ESP Rotary@^1.5.0
|
||||
```
|
||||
|
||||
## How to connect the board to your ESP
|
||||
We gonna need (minimum) three or (maximum) four GPIOs for the board:
|
||||
* "ea": Basically tells if the encoder goes into one or the other direction
|
||||
* "eb": Same thing, but the other direction
|
||||
* "di": LED data in. To actually control the LEDs
|
||||
* *(optional)* "sw": The integrated switch in the rotary encoder. Can be omitted for the bare functionality of just controlling the brightness
|
||||
|
||||
We also gonna need some power, so:
|
||||
|
||||
* "vdd": Needs to be connected to **+5V**.
|
||||
* "gnd": Well, it's GND.
|
||||
|
||||
You can freely pick the GPIOs, it doesn't matter. Those will be configured in the "Usermods" section in the WLED web panel:
|
||||
|
||||
## Configuration
|
||||
Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D RGB_ROTARY_ENCODER`, you will see the config for it there. The settings there are the GPIOs we mentioned before (*Note: The switch pin is not there, as this can just be configured the "normal" button on the "LED Preferences" page*), plus a few more:
|
||||
* LED pin:
|
||||
* Possible values: Any valid and available GPIO
|
||||
* Default: 3
|
||||
* What it does: Pin to control the LED ring
|
||||
* ea pin:
|
||||
* Possible values: Any valid and available GPIO
|
||||
* Default: 15
|
||||
* What it does: First of the two rotary encoder pins
|
||||
* eb pin:
|
||||
* Possible values: Any valid and available GPIO
|
||||
* Default: 32
|
||||
* What it does: Second of the two rotary encoder pins
|
||||
* LED Mode:
|
||||
* Possible values: 1-3
|
||||
* Default: 3
|
||||
* What it does: The usermod provides three different modes of how the LEDs can look like. Here's an example: https://github.com/isotope-engineering/RGB-Encoder-Board/blob/master/images/rgb-encoder-animations.gif
|
||||
* Up left is "1"
|
||||
* Up right is not supported / doesn't make sense for brightness control
|
||||
* Bottom left is "2"
|
||||
* Bottom right is "3"
|
||||
* LED Brightness:
|
||||
* Possible values: 1-255
|
||||
* Default: 64
|
||||
* What it does: Brightness of the LED ring
|
||||
* Steps per click:
|
||||
* Possible values: Any positive number
|
||||
* Default: 4
|
||||
* What it does: With each "click", a rotary encoder actually increments it's "steps". Most rotary encoder do four "steps" per "click". I know this sounds super weird, so just leave this the default value, unless your rotary encoder behaves weirdly, like with one click, it makes two LEDs light up, or you sometimes need two click for one LED. Then you should play around with this value or write a small sketch using the same "ESP Rotary" library and read out the steps it does.
|
||||
* Increment per click:
|
||||
* Possible values: Any positive number
|
||||
* Default: 5
|
||||
* What it does: Most rotary encoder have 20 "clicks", so basically 20 positions. This value should be set to 100 / `number of clicks`
|
||||
|
||||
## Change log
|
||||
2021-07
|
||||
* First implementation.
|
||||
343
usermods/rgb-rotary-encoder/rgb-rotary-encoder.h
Normal file
343
usermods/rgb-rotary-encoder/rgb-rotary-encoder.h
Normal file
@@ -0,0 +1,343 @@
|
||||
#pragma once
|
||||
|
||||
#include "ESPRotary.h"
|
||||
#include <math.h>
|
||||
#include "wled.h"
|
||||
|
||||
class RgbRotaryEncoderUsermod : public Usermod
|
||||
{
|
||||
private:
|
||||
bool enabled = false;
|
||||
bool initDone = false;
|
||||
bool isDirty = false;
|
||||
BusDigital *ledBus;
|
||||
/*
|
||||
* Green - eb - Q4 - 32
|
||||
* Red - ea - Q1 - 15
|
||||
* Black - sw - Q2 - 12
|
||||
*/
|
||||
ESPRotary *rotaryEncoder;
|
||||
int8_t ledIo = 3; // GPIO to control the LEDs
|
||||
int8_t eaIo = 15; // "ea" from RGB Encoder Board
|
||||
int8_t ebIo = 32; // "eb" from RGB Encoder Board
|
||||
byte stepsPerClick = 4; // How many "steps" your rotary encoder does per click. This varies per rotary encoder
|
||||
/* This could vary per rotary encoder: Usually rotary encoders have 20 "clicks".
|
||||
If yours has less/more, adjust this to: 100% = 20 LEDs * incrementPerClick */
|
||||
byte incrementPerClick = 5;
|
||||
byte ledMode = 3;
|
||||
byte ledBrightness = 64;
|
||||
|
||||
// This is all needed to calculate the brightness, rotary position, etc.
|
||||
const byte minPos = 5; // minPos is not zero, because if we want to turn the LEDs off, we use the built-in button ;)
|
||||
const byte maxPos = 100; // maxPos=100, like 100%
|
||||
const byte numLeds = 20;
|
||||
byte lastKnownPos = 0;
|
||||
|
||||
byte currentColors[3];
|
||||
byte lastKnownBri = 0;
|
||||
|
||||
|
||||
void initRotaryEncoder()
|
||||
{
|
||||
PinManagerPinType pins[2] = { { eaIo, false }, { ebIo, false } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_RGBRotaryEncoder)) {
|
||||
eaIo = -1;
|
||||
ebIo = -1;
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
// I don't know why, but setting the upper bound here does not work. It results into 1717922932 O_o
|
||||
rotaryEncoder = new ESPRotary(eaIo, ebIo, stepsPerClick, incrementPerClick, maxPos, currentPos, incrementPerClick);
|
||||
rotaryEncoder->setUpperBound(maxPos); // I have to again set it here and then it works / is actually 100...
|
||||
|
||||
rotaryEncoder->setChangedHandler(RgbRotaryEncoderUsermod::cbRotate);
|
||||
}
|
||||
|
||||
void initLedBus()
|
||||
{
|
||||
byte _pins[5] = {(byte)ledIo, 255, 255, 255, 255};
|
||||
BusConfig busCfg = BusConfig(TYPE_WS2812_RGB, _pins, 0, numLeds, COL_ORDER_GRB, false, 0);
|
||||
|
||||
ledBus = new BusDigital(busCfg, WLED_MAX_BUSSES - 1);
|
||||
if (!ledBus->isOk()) {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
ledBus->setBrightness(ledBrightness);
|
||||
}
|
||||
|
||||
void updateLeds()
|
||||
{
|
||||
switch (ledMode) {
|
||||
case 2:
|
||||
{
|
||||
currentColors[0] = 255; currentColors[1] = 0; currentColors[2] = 0;
|
||||
for (int i = 0; i < currentPos / incrementPerClick - 1; i++) {
|
||||
ledBus->setPixelColor(i, 0);
|
||||
}
|
||||
ledBus->setPixelColor(currentPos / incrementPerClick - 1, colorFromRgbw(currentColors));
|
||||
for (int i = currentPos / incrementPerClick; i < numLeds; i++) {
|
||||
ledBus->setPixelColor(i, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
case 1:
|
||||
case 3:
|
||||
// WLED orange (of course), which we will use in mode 1
|
||||
currentColors[0] = 255; currentColors[1] = 160; currentColors[2] = 0;
|
||||
for (int i = 0; i < currentPos / incrementPerClick; i++) {
|
||||
if (ledMode == 3) {
|
||||
hsv2rgb((i) / float(numLeds), 1, .25);
|
||||
}
|
||||
ledBus->setPixelColor(i, colorFromRgbw(currentColors));
|
||||
}
|
||||
for (int i = currentPos / incrementPerClick; i < numLeds; i++) {
|
||||
ledBus->setPixelColor(i, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
// Only deallocate pins if we allocated them ;)
|
||||
if (eaIo != -1) {
|
||||
pinManager.deallocatePin(eaIo, PinOwner::UM_RGBRotaryEncoder);
|
||||
eaIo = -1;
|
||||
}
|
||||
if (ebIo != -1) {
|
||||
pinManager.deallocatePin(ebIo, PinOwner::UM_RGBRotaryEncoder);
|
||||
ebIo = -1;
|
||||
}
|
||||
|
||||
delete rotaryEncoder;
|
||||
delete ledBus;
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
int getPositionForBrightness()
|
||||
{
|
||||
return int(((float)bri / (float)255) * 100);
|
||||
}
|
||||
|
||||
float fract(float x) { return x - int(x); }
|
||||
|
||||
float mix(float a, float b, float t) { return a + (b - a) * t; }
|
||||
|
||||
void hsv2rgb(float h, float s, float v) {
|
||||
currentColors[0] = int((v * mix(1.0, constrain(abs(fract(h + 1.0) * 6.0 - 3.0) - 1.0, 0.0, 1.0), s)) * 255);
|
||||
currentColors[1] = int((v * mix(1.0, constrain(abs(fract(h + 0.6666666) * 6.0 - 3.0) - 1.0, 0.0, 1.0), s)) * 255);
|
||||
currentColors[2] = int((v * mix(1.0, constrain(abs(fract(h + 0.3333333) * 6.0 - 3.0) - 1.0, 0.0, 1.0), s)) * 255);
|
||||
}
|
||||
|
||||
public:
|
||||
static byte currentPos;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _ledIo[];
|
||||
static const char _eaIo[];
|
||||
static const char _ebIo[];
|
||||
static const char _ledMode[];
|
||||
static const char _ledBrightness[];
|
||||
static const char _stepsPerClick[];
|
||||
static const char _incrementPerClick[];
|
||||
|
||||
|
||||
static void cbRotate(ESPRotary& r) {
|
||||
currentPos = r.getPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable the usermod
|
||||
*/
|
||||
// inline void enable(bool enable) { enabled = enable; }
|
||||
/**
|
||||
* Get usermod enabled/disabled state
|
||||
*/
|
||||
// inline bool isEnabled() { return enabled; }
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
if (enabled) {
|
||||
currentPos = getPositionForBrightness();
|
||||
lastKnownBri = bri;
|
||||
|
||||
initRotaryEncoder();
|
||||
initLedBus();
|
||||
|
||||
// No updating of LEDs here, as that's sometimes not working; loop() will take care of that
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
|
||||
rotaryEncoder->loop();
|
||||
|
||||
// If the rotary was changed
|
||||
if(lastKnownPos != currentPos) {
|
||||
lastKnownPos = currentPos;
|
||||
|
||||
bri = min(int(round((2.55 * currentPos))), 255);
|
||||
lastKnownBri = bri;
|
||||
|
||||
updateLeds();
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
// If the brightness is changed not with the rotary, update the rotary
|
||||
if (bri != lastKnownBri) {
|
||||
currentPos = lastKnownPos = getPositionForBrightness();
|
||||
lastKnownBri = bri;
|
||||
rotaryEncoder->resetPosition(currentPos);
|
||||
updateLeds();
|
||||
}
|
||||
|
||||
// Update LEDs here in loop to also validate that we can update/show
|
||||
if (isDirty && ledBus->canShow()) {
|
||||
isDirty = false;
|
||||
ledBus->show();
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_ledIo)] = ledIo;
|
||||
top[FPSTR(_eaIo)] = eaIo;
|
||||
top[FPSTR(_ebIo)] = ebIo;
|
||||
top[FPSTR(_ledMode)] = ledMode;
|
||||
top[FPSTR(_ledBrightness)] = ledBrightness;
|
||||
top[FPSTR(_stepsPerClick)] = stepsPerClick;
|
||||
top[FPSTR(_incrementPerClick)] = incrementPerClick;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTF("[%s] No config found. (Using defaults.)\n", _name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool oldEnabled = enabled;
|
||||
int8_t oldLedIo = ledIo;
|
||||
int8_t oldEaIo = eaIo;
|
||||
int8_t oldEbIo = ebIo;
|
||||
byte oldLedMode = ledMode;
|
||||
byte oldStepsPerClick = stepsPerClick;
|
||||
byte oldIncrementPerClick = incrementPerClick;
|
||||
byte oldLedBrightness = ledBrightness;
|
||||
|
||||
getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
getJsonValue(top[FPSTR(_ledIo)], ledIo);
|
||||
getJsonValue(top[FPSTR(_eaIo)], eaIo);
|
||||
getJsonValue(top[FPSTR(_ebIo)], ebIo);
|
||||
getJsonValue(top[FPSTR(_stepsPerClick)], stepsPerClick);
|
||||
getJsonValue(top[FPSTR(_incrementPerClick)], incrementPerClick);
|
||||
ledMode = top[FPSTR(_ledMode)] > 0 && top[FPSTR(_ledMode)] < 4 ? top[FPSTR(_ledMode)] : ledMode;
|
||||
ledBrightness = top[FPSTR(_ledBrightness)] > 0 && top[FPSTR(_ledBrightness)] <= 255 ? top[FPSTR(_ledBrightness)] : ledBrightness;
|
||||
|
||||
if (!initDone) {
|
||||
// First run: reading from cfg.json
|
||||
// Nothing to do here, will be all done in setup()
|
||||
}
|
||||
// Mod was disabled, so run setup()
|
||||
else if (enabled && enabled != oldEnabled) {
|
||||
DEBUG_PRINTF("[%s] Usermod has been re-enabled\n", _name);
|
||||
setup();
|
||||
}
|
||||
// Config has been changed, so adopt to changes
|
||||
else {
|
||||
if (!enabled) {
|
||||
DEBUG_PRINTF("[%s] Usermod has been disabled\n", _name);
|
||||
cleanup();
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTF("[%s] Usermod is enabled\n", _name);
|
||||
if (ledIo != oldLedIo) {
|
||||
delete ledBus;
|
||||
initLedBus();
|
||||
}
|
||||
|
||||
if (ledBrightness != oldLedBrightness) {
|
||||
ledBus->setBrightness(ledBrightness);
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
if (ledMode != oldLedMode) {
|
||||
updateLeds();
|
||||
}
|
||||
|
||||
if (eaIo != oldEaIo || ebIo != oldEbIo || stepsPerClick != oldStepsPerClick || incrementPerClick != oldIncrementPerClick) {
|
||||
pinManager.deallocatePin(oldEaIo, PinOwner::UM_RGBRotaryEncoder);
|
||||
pinManager.deallocatePin(oldEbIo, PinOwner::UM_RGBRotaryEncoder);
|
||||
|
||||
delete rotaryEncoder;
|
||||
initRotaryEncoder();
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_PRINTF("[%s] Config (re)loaded\n", _name);
|
||||
}
|
||||
|
||||
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_RGB_ROTARY_ENCODER;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
||||
|
||||
byte RgbRotaryEncoderUsermod::currentPos = 5;
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char RgbRotaryEncoderUsermod::_name[] PROGMEM = "RGB-Rotary-Encoder";
|
||||
const char RgbRotaryEncoderUsermod::_enabled[] PROGMEM = "Enabled";
|
||||
const char RgbRotaryEncoderUsermod::_ledIo[] PROGMEM = "LED-pin";
|
||||
const char RgbRotaryEncoderUsermod::_eaIo[] PROGMEM = "ea-pin";
|
||||
const char RgbRotaryEncoderUsermod::_ebIo[] PROGMEM = "eb-pin";
|
||||
const char RgbRotaryEncoderUsermod::_ledMode[] PROGMEM = "LED-Mode";
|
||||
const char RgbRotaryEncoderUsermod::_ledBrightness[] PROGMEM = "LED-Brightness";
|
||||
const char RgbRotaryEncoderUsermod::_stepsPerClick[] PROGMEM = "Steps-per-Click";
|
||||
const char RgbRotaryEncoderUsermod::_incrementPerClick[] PROGMEM = "Increment-per-Click";
|
||||
@@ -1,62 +0,0 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* This file allows you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
|
||||
* bytes 2400+ are currently ununsed, but might be used for future wled features
|
||||
*/
|
||||
|
||||
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
|
||||
|
||||
/*
|
||||
** Rotary Encoder Example
|
||||
** Use the Sparkfun Rotary Encoder to vary brightness of LED
|
||||
**
|
||||
** Sample the encoder at 500Hz using the millis() function
|
||||
*/
|
||||
|
||||
int fadeAmount = 5; // how many points to fade the Neopixel with each step
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
const int pinA = D6; // DT from encoder
|
||||
const int pinB = D7; // CLK from encoder
|
||||
|
||||
unsigned char Enc_A;
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
//gets called once at boot. Do all initialization that doesn't depend on network here
|
||||
void userSetup() {
|
||||
pinMode(pinA, INPUT_PULLUP);
|
||||
pinMode(pinB, INPUT_PULLUP);
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
}
|
||||
|
||||
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
|
||||
void userConnected() {
|
||||
}
|
||||
|
||||
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
|
||||
void userLoop() {
|
||||
currentTime = millis(); // get the current elapsed time
|
||||
if(currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||
{
|
||||
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
|
||||
if(bri + fadeAmount <= 255) bri += fadeAmount; // increase the brightness, dont go over 255
|
||||
|
||||
} else if (Enc_B == LOW) { // B is low so counter-clockwise
|
||||
if(bri - fadeAmount >= 0) bri -= fadeAmount; // decrease the brightness, dont go below 0
|
||||
}
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
|
||||
//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(6);
|
||||
}
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//v2 usermod that allows to change brightness and color using a rotary encoder,
|
||||
//change between modes by pressing a button (many encoder have one included)
|
||||
class RotaryEncoderSet : public Usermod
|
||||
{
|
||||
private:
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
/*
|
||||
** Rotary Encoder Example
|
||||
** Use the Sparkfun Rotary Encoder to vary brightness of LED
|
||||
**
|
||||
** Sample the encoder at 500Hz using the millis() function
|
||||
*/
|
||||
|
||||
int fadeAmount = 5; // how many points to fade the Neopixel with each step
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
const int pinA = 5; // DT from encoder
|
||||
const int pinB = 18; // CLK from encoder
|
||||
const int pinC = 23; // SW from encoder
|
||||
unsigned char select_state = 0; // 0 = brightness 1 = color
|
||||
unsigned char button_state = HIGH;
|
||||
unsigned char prev_button_state = HIGH;
|
||||
CRGB fastled_col;
|
||||
CHSV prim_hsv;
|
||||
int16_t new_val;
|
||||
|
||||
unsigned char Enc_A;
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
//Serial.println("Hello from my usermod!");
|
||||
pinMode(pinA, INPUT_PULLUP);
|
||||
pinMode(pinB, INPUT_PULLUP);
|
||||
pinMode(pinC, INPUT_PULLUP);
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
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)
|
||||
{
|
||||
if (select_state == 1)
|
||||
{
|
||||
select_state = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
select_state = 1;
|
||||
}
|
||||
prev_button_state = button_state;
|
||||
}
|
||||
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
|
||||
if (select_state == 0)
|
||||
{
|
||||
if (bri + fadeAmount <= 255)
|
||||
bri += fadeAmount; // increase the brightness, dont go over 255
|
||||
}
|
||||
else
|
||||
{
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
new_val = (int16_t)prim_hsv.h + fadeAmount;
|
||||
if (new_val > 255)
|
||||
new_val -= 255; // roll-over if bigger than 255
|
||||
if (new_val < 0)
|
||||
new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == LOW)
|
||||
{ // B is low so counter-clockwise
|
||||
if (select_state == 0)
|
||||
{
|
||||
if (bri - fadeAmount >= 0)
|
||||
bri -= fadeAmount; // decrease the brightness, dont go below 0
|
||||
}
|
||||
else
|
||||
{
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
new_val = (int16_t)prim_hsv.h - fadeAmount;
|
||||
if (new_val > 255)
|
||||
new_val -= 255; // roll-over if bigger than 255
|
||||
if (new_val < 0)
|
||||
new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
}
|
||||
}
|
||||
//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);
|
||||
updateInterfaces()
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 0xABCD;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
||||
@@ -39,7 +39,7 @@ void userLoop() {
|
||||
|
||||
//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_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
lastTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
55
usermods/seven_segment_display/readme.md
Normal file
55
usermods/seven_segment_display/readme.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Seven Segment Display
|
||||
|
||||
Usermod that uses the overlay feature to create a configurable seven segment display.
|
||||
This has only been tested on a single configuration. Colon support is entirely untested.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the compile-time option `-D USERMOD_SEVEN_SEGMENT` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SEVEN_SEGMENT` in `my_config.h`.
|
||||
|
||||
## Settings
|
||||
Settings can be controlled through both the usermod setting page and through MQTT with a raw payload.
|
||||
##### Example
|
||||
Topic ```<mqttDeviceTopic||mqttGroupTopic>/sevenSeg/perSegment/set```
|
||||
Payload ```3```
|
||||
#### perSegment -- ssLEDPerSegment
|
||||
The number of individual LEDs per segment. There are 7 segments per digit.
|
||||
#### perPeriod -- ssLEDPerPeriod
|
||||
The number of individual LEDs per period. A ':' has 2x periods.
|
||||
#### startIdx -- ssStartLED
|
||||
Index of the LED that the display starts at. Allows a seven segment display to be in the middle of a string.
|
||||
#### timeEnable -- ssTimeEnabled
|
||||
When true, when displayMask is configured for a time output and no message is set the time will be displayed.
|
||||
#### scrollSpd -- ssScrollSpeed
|
||||
Time, in milliseconds, between message shifts when the length of displayMsg exceeds the length of the displayMask.
|
||||
#### displayMask -- ssDisplayMask
|
||||
This should represent the configuration of the physical display.
|
||||
<pre>
|
||||
HH - 0-23. hh - 1-12, kk - 1-24 hours
|
||||
MM or mm - 0-59 minutes
|
||||
SS or ss = 0-59 seconds
|
||||
: for a colon
|
||||
All others for alpha numeric, (will be blank when displaying time)
|
||||
</pre>
|
||||
##### Example
|
||||
```HHMMSS ```
|
||||
```hh:MM:SS ```
|
||||
#### displayMsg -- ssDisplayMessage
|
||||
Message to be displayed across the display. If the length exceeds the length of the displayMask the message will scroll at scrollSpd. To 'remove' a message or revert back to time, if timeEnabled is true, set the message to '~'.
|
||||
#### displayCfg -- ssDisplayConfig
|
||||
The order that your LEDs are configured. All seven segments in the display need to be wired the same way.
|
||||
<pre>
|
||||
-------
|
||||
/ A / 0 - EDCGFAB
|
||||
/ F / B 1 - EDCBAFG
|
||||
/ / 2 - GCDEFAB
|
||||
------- 3 - GBAFEDC
|
||||
/ G / 4 - FABGEDC
|
||||
/ E / C 5 - FABCDEG
|
||||
/ /
|
||||
-------
|
||||
D
|
||||
</pre>
|
||||
|
||||
## Version
|
||||
20211009 - Initial release
|
||||
@@ -0,0 +1,497 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class SevenSegmentDisplay : public Usermod
|
||||
{
|
||||
|
||||
#define WLED_SS_BUFFLEN 6
|
||||
#define REFRESHTIME 497
|
||||
private:
|
||||
//Runtime variables.
|
||||
unsigned long lastRefresh = 0;
|
||||
unsigned long lastCharacterStep = 0;
|
||||
String ssDisplayBuffer = "";
|
||||
char ssCharacterMask[36] = {0x77, 0x11, 0x6B, 0x3B, 0x1D, 0x3E, 0x7E, 0x13, 0x7F, 0x1F, 0x5F, 0x7C, 0x66, 0x79, 0x6E, 0x4E, 0x76, 0x5D, 0x44, 0x71, 0x5E, 0x64, 0x27, 0x58, 0x77, 0x4F, 0x1F, 0x48, 0x3E, 0x6C, 0x75, 0x25, 0x7D, 0x2A, 0x3D, 0x6B};
|
||||
int ssDisplayMessageIdx = 0; //Position of the start of the message to be physically displayed.
|
||||
bool ssDoDisplayTime = true;
|
||||
int ssVirtualDisplayMessageIdxStart = 0;
|
||||
int ssVirtualDisplayMessageIdxEnd = 0;
|
||||
unsigned long resfreshTime = 497;
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
int ssLEDPerSegment = 1; //The number of LEDs in each segment of the 7 seg (total per digit is 7 * ssLedPerSegment)
|
||||
int ssLEDPerPeriod = 1; //A Period will have 1x and a Colon will have 2x
|
||||
int ssStartLED = 0; //The pixel that the display starts at.
|
||||
/* HH - 0-23. hh - 1-12, kk - 1-24 hours
|
||||
// MM or mm - 0-59 minutes
|
||||
// SS or ss = 0-59 seconds
|
||||
// : for a colon
|
||||
// All others for alpha numeric, (will be blank when displaying time)
|
||||
*/
|
||||
String ssDisplayMask = "HHMMSS"; //Physical Display Mask, this should reflect physical equipment.
|
||||
/* ssDisplayConfig
|
||||
// -------
|
||||
// / A / 0 - EDCGFAB
|
||||
// / F / B 1 - EDCBAFG
|
||||
// / / 2 - GCDEFAB
|
||||
// ------- 3 - GBAFEDC
|
||||
// / G / 4 - FABGEDC
|
||||
// / E / C 5 - FABCDEG
|
||||
// / /
|
||||
// -------
|
||||
// D
|
||||
*/
|
||||
int ssDisplayConfig = 5; //Physical configuration of the Seven segment display
|
||||
String ssDisplayMessage = "~";
|
||||
bool ssTimeEnabled = true; //If not, display message.
|
||||
unsigned int ssScrollSpeed = 1000; //Time between advancement of extended message scrolling, in milliseconds.
|
||||
|
||||
//String to reduce flash memory usage
|
||||
static const char _str_perSegment[];
|
||||
static const char _str_perPeriod[];
|
||||
static const char _str_startIdx[];
|
||||
static const char _str_displayCfg[];
|
||||
static const char _str_timeEnabled[];
|
||||
static const char _str_scrollSpd[];
|
||||
static const char _str_displayMask[];
|
||||
static const char _str_displayMsg[];
|
||||
static const char _str_sevenSeg[];
|
||||
static const char _str_subFormat[];
|
||||
static const char _str_topicFormat[];
|
||||
|
||||
unsigned long _overlaySevenSegmentProcess()
|
||||
{
|
||||
//Do time for now.
|
||||
if (ssDoDisplayTime)
|
||||
{
|
||||
//Format the ssDisplayBuffer based on ssDisplayMask
|
||||
int displayMaskLen = static_cast<int>(ssDisplayMask.length());
|
||||
for (int index = 0; index < displayMaskLen; index++)
|
||||
{
|
||||
//Only look for time formatting if there are at least 2 characters left in the buffer.
|
||||
if ((index < displayMaskLen - 1) && (ssDisplayMask[index] == ssDisplayMask[index + 1]))
|
||||
{
|
||||
int timeVar = 0;
|
||||
switch (ssDisplayMask[index])
|
||||
{
|
||||
case 'h':
|
||||
timeVar = hourFormat12(localTime);
|
||||
break;
|
||||
case 'H':
|
||||
timeVar = hour(localTime);
|
||||
break;
|
||||
case 'k':
|
||||
timeVar = hour(localTime) + 1;
|
||||
break;
|
||||
case 'M':
|
||||
case 'm':
|
||||
timeVar = minute(localTime);
|
||||
break;
|
||||
case 'S':
|
||||
case 's':
|
||||
timeVar = second(localTime);
|
||||
break;
|
||||
}
|
||||
|
||||
//Only want to leave a blank in the hour formatting.
|
||||
if ((ssDisplayMask[index] == 'h' || ssDisplayMask[index] == 'H' || ssDisplayMask[index] == 'k') && timeVar < 10)
|
||||
ssDisplayBuffer[index] = ' ';
|
||||
else
|
||||
ssDisplayBuffer[index] = 0x30 + (timeVar / 10);
|
||||
ssDisplayBuffer[index + 1] = 0x30 + (timeVar % 10);
|
||||
|
||||
//Need to increment the index because of the second digit.
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ssDisplayBuffer[index] = (ssDisplayMask[index] == ':' ? ':' : ' ');
|
||||
}
|
||||
}
|
||||
return REFRESHTIME;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This will handle displaying a message and the scrolling of the message if its longer than the buffer length */
|
||||
|
||||
//Check to see if the message has scrolled completely
|
||||
int len = static_cast<int>(ssDisplayMessage.length());
|
||||
if (ssDisplayMessageIdx > len)
|
||||
{
|
||||
//If it has scrolled the whole message, reset it.
|
||||
setSevenSegmentMessage(ssDisplayMessage);
|
||||
return REFRESHTIME;
|
||||
}
|
||||
//Display message
|
||||
int displayMaskLen = static_cast<int>(ssDisplayMask.length());
|
||||
for (int index = 0; index < displayMaskLen; index++)
|
||||
{
|
||||
if (ssDisplayMessageIdx + index < len && ssDisplayMessageIdx + index >= 0)
|
||||
ssDisplayBuffer[index] = ssDisplayMessage[ssDisplayMessageIdx + index];
|
||||
else
|
||||
ssDisplayBuffer[index] = ' ';
|
||||
}
|
||||
|
||||
//Increase the displayed message index to progress it one character if the length exceeds the display length.
|
||||
if (len > displayMaskLen)
|
||||
ssDisplayMessageIdx++;
|
||||
|
||||
return ssScrollSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
void _overlaySevenSegmentDraw()
|
||||
{
|
||||
|
||||
//Start pixels at ssStartLED, Use ssLEDPerSegment, ssLEDPerPeriod, ssDisplayBuffer
|
||||
int indexLED = ssStartLED;
|
||||
int displayMaskLen = static_cast<int>(ssDisplayMask.length());
|
||||
for (int indexBuffer = 0; indexBuffer < displayMaskLen; indexBuffer++)
|
||||
{
|
||||
if (ssDisplayBuffer[indexBuffer] == 0)
|
||||
break;
|
||||
else if (ssDisplayBuffer[indexBuffer] == '.')
|
||||
{
|
||||
//Won't ever turn off LED lights for a period. (or will we?)
|
||||
indexLED += ssLEDPerPeriod;
|
||||
continue;
|
||||
}
|
||||
else if (ssDisplayBuffer[indexBuffer] == ':')
|
||||
{
|
||||
//Turn off colon if odd second?
|
||||
indexLED += ssLEDPerPeriod * 2;
|
||||
}
|
||||
else if (ssDisplayBuffer[indexBuffer] == ' ')
|
||||
{
|
||||
//Turn off all 7 segments.
|
||||
_overlaySevenSegmentLEDOutput(0, indexLED);
|
||||
indexLED += ssLEDPerSegment * 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Turn off correct segments.
|
||||
_overlaySevenSegmentLEDOutput(_overlaySevenSegmentGetCharMask(ssDisplayBuffer[indexBuffer]), indexLED);
|
||||
indexLED += ssLEDPerSegment * 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _overlaySevenSegmentLEDOutput(char mask, int indexLED)
|
||||
{
|
||||
for (char index = 0; index < 7; index++)
|
||||
{
|
||||
if ((mask & (0x40 >> index)) != (0x40 >> index))
|
||||
{
|
||||
for (int numPerSeg = 0; numPerSeg < ssLEDPerSegment; numPerSeg++)
|
||||
{
|
||||
strip.setPixelColor(indexLED + numPerSeg, 0x000000);
|
||||
}
|
||||
}
|
||||
indexLED += ssLEDPerSegment;
|
||||
}
|
||||
}
|
||||
|
||||
char _overlaySevenSegmentGetCharMask(char var)
|
||||
{
|
||||
if (var >= 0x30 && var <= 0x39)
|
||||
{ /*If its a number, shift to index 0.*/
|
||||
var -= 0x30;
|
||||
}
|
||||
else if (var >= 0x41 && var <= 0x5a)
|
||||
{ /*If its an Upper case, shift to index 0xA.*/
|
||||
var -= 0x37;
|
||||
}
|
||||
else if (var >= 0x61 && var <= 0x7A)
|
||||
{ /*If its a lower case, shift to index 0xA.*/
|
||||
var -= 0x57;
|
||||
}
|
||||
else
|
||||
{ /* Else unsupported, return 0; */
|
||||
return 0;
|
||||
}
|
||||
char mask = ssCharacterMask[static_cast<int>(var)];
|
||||
/*
|
||||
0 - EDCGFAB
|
||||
1 - EDCBAFG
|
||||
2 - GCDEFAB
|
||||
3 - GBAFEDC
|
||||
4 - FABGEDC
|
||||
5 - FABCDEG
|
||||
*/
|
||||
switch (ssDisplayConfig)
|
||||
{
|
||||
case 1:
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 0, 3, 1);
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 1, 2, 1);
|
||||
break;
|
||||
case 2:
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 3, 6, 1);
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 4, 5, 1);
|
||||
break;
|
||||
case 3:
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 0, 4, 3);
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 3, 6, 1);
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 4, 5, 1);
|
||||
break;
|
||||
case 4:
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 0, 4, 3);
|
||||
break;
|
||||
case 5:
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 0, 4, 3);
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 0, 3, 1);
|
||||
mask = _overlaySevenSegmentSwapBits(mask, 1, 2, 1);
|
||||
break;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
char _overlaySevenSegmentSwapBits(char x, char p1, char p2, char n)
|
||||
{
|
||||
/* Move all bits of first set to rightmost side */
|
||||
char set1 = (x >> p1) & ((1U << n) - 1);
|
||||
|
||||
/* Move all bits of second set to rightmost side */
|
||||
char set2 = (x >> p2) & ((1U << n) - 1);
|
||||
|
||||
/* Xor the two sets */
|
||||
char Xor = (set1 ^ set2);
|
||||
|
||||
/* Put the Xor bits back to their original positions */
|
||||
Xor = (Xor << p1) | (Xor << p2);
|
||||
|
||||
/* Xor the 'Xor' with the original number so that the
|
||||
two sets are swapped */
|
||||
char result = x ^ Xor;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _publishMQTTint_P(const char *subTopic, int value)
|
||||
{
|
||||
if(mqtt == NULL) return;
|
||||
|
||||
char buffer[64];
|
||||
char valBuffer[12];
|
||||
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_sevenSeg, subTopic);
|
||||
sprintf_P(valBuffer, PSTR("%d"), value);
|
||||
mqtt->publish(buffer, 2, true, valBuffer);
|
||||
}
|
||||
|
||||
void _publishMQTTstr_P(const char *subTopic, String Value)
|
||||
{
|
||||
if(mqtt == NULL) return;
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_sevenSeg, subTopic);
|
||||
mqtt->publish(buffer, 2, true, Value.c_str(), Value.length());
|
||||
}
|
||||
|
||||
void _updateMQTT()
|
||||
{
|
||||
_publishMQTTint_P(_str_perSegment, ssLEDPerSegment);
|
||||
_publishMQTTint_P(_str_perPeriod, ssLEDPerPeriod);
|
||||
_publishMQTTint_P(_str_startIdx, ssStartLED);
|
||||
_publishMQTTint_P(_str_displayCfg, ssDisplayConfig);
|
||||
_publishMQTTint_P(_str_timeEnabled, ssTimeEnabled);
|
||||
_publishMQTTint_P(_str_scrollSpd, ssScrollSpeed);
|
||||
|
||||
_publishMQTTstr_P(_str_displayMask, ssDisplayMask);
|
||||
_publishMQTTstr_P(_str_displayMsg, ssDisplayMessage);
|
||||
}
|
||||
|
||||
bool _cmpIntSetting_P(char *topic, char *payload, const char *setting, void *value)
|
||||
{
|
||||
if (strcmp_P(topic, setting) == 0)
|
||||
{
|
||||
*((int *)value) = strtol(payload, NULL, 10);
|
||||
_publishMQTTint_P(setting, *((int *)value));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _handleSetting(char *topic, char *payload)
|
||||
{
|
||||
if (_cmpIntSetting_P(topic, payload, _str_perSegment, &ssLEDPerSegment))
|
||||
return true;
|
||||
if (_cmpIntSetting_P(topic, payload, _str_perPeriod, &ssLEDPerPeriod))
|
||||
return true;
|
||||
if (_cmpIntSetting_P(topic, payload, _str_startIdx, &ssStartLED))
|
||||
return true;
|
||||
if (_cmpIntSetting_P(topic, payload, _str_displayCfg, &ssDisplayConfig))
|
||||
return true;
|
||||
if (_cmpIntSetting_P(topic, payload, _str_timeEnabled, &ssTimeEnabled))
|
||||
return true;
|
||||
if (_cmpIntSetting_P(topic, payload, _str_scrollSpd, &ssScrollSpeed))
|
||||
return true;
|
||||
if (strcmp_P(topic, _str_displayMask) == 0)
|
||||
{
|
||||
ssDisplayMask = String(payload);
|
||||
ssDisplayBuffer = ssDisplayMask;
|
||||
_publishMQTTstr_P(_str_displayMask, ssDisplayMask);
|
||||
return true;
|
||||
}
|
||||
if (strcmp_P(topic, _str_displayMsg) == 0)
|
||||
{
|
||||
setSevenSegmentMessage(String(payload));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
void setSevenSegmentMessage(String message)
|
||||
{
|
||||
//If the message isn't blank display it otherwise show time, if enabled.
|
||||
if (message.length() < 1 || message == "~")
|
||||
ssDoDisplayTime = ssTimeEnabled;
|
||||
else
|
||||
ssDoDisplayTime = false;
|
||||
|
||||
//Determine is the message is longer than the display, if it is configure it to scroll the message.
|
||||
if (message.length() > ssDisplayMask.length())
|
||||
ssDisplayMessageIdx = -ssDisplayMask.length();
|
||||
else
|
||||
ssDisplayMessageIdx = 0;
|
||||
|
||||
//If the message isn't the same, update runtime/mqtt (most calls will be resetting message scroll)
|
||||
if (!ssDisplayMessage.equals(message))
|
||||
{
|
||||
_publishMQTTstr_P(_str_displayMsg, message);
|
||||
ssDisplayMessage = message;
|
||||
}
|
||||
}
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
ssDisplayBuffer = ssDisplayMask;
|
||||
}
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
if (millis() - lastRefresh > resfreshTime)
|
||||
{
|
||||
//In theory overlaySevenSegmentProcess should return the amount of time until it changes next.
|
||||
//So we should be okay to trigger the stripi on every process loop.
|
||||
resfreshTime = _overlaySevenSegmentProcess();
|
||||
lastRefresh = millis();
|
||||
strip.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverlayDraw()
|
||||
{
|
||||
_overlaySevenSegmentDraw();
|
||||
}
|
||||
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
char subBuffer[48];
|
||||
if (mqttDeviceTopic[0] != 0)
|
||||
{
|
||||
_updateMQTT();
|
||||
//subscribe for sevenseg messages on the device topic
|
||||
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttDeviceTopic, _str_sevenSeg);
|
||||
mqtt->subscribe(subBuffer, 2);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0)
|
||||
{
|
||||
//subcribe for sevenseg messages on the group topic
|
||||
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_sevenSeg);
|
||||
mqtt->subscribe(subBuffer, 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool onMqttMessage(char *topic, char *payload)
|
||||
{
|
||||
//If topic beings iwth sevenSeg cut it off, otherwise not our message.
|
||||
size_t topicPrefixLen = strlen_P(PSTR("/sevenSeg/"));
|
||||
if (strncmp_P(topic, PSTR("/sevenSeg/"), topicPrefixLen) == 0)
|
||||
topic += topicPrefixLen;
|
||||
else
|
||||
return false;
|
||||
//We only care if the topic ends with /set
|
||||
size_t topicLen = strlen(topic);
|
||||
if (topicLen > 4 &&
|
||||
topic[topicLen - 4] == '/' &&
|
||||
topic[topicLen - 3] == 's' &&
|
||||
topic[topicLen - 2] == 'e' &&
|
||||
topic[topicLen - 1] == 't')
|
||||
{
|
||||
//Trim /set and handle it
|
||||
topic[topicLen - 4] = '\0';
|
||||
_handleSetting(topic, payload);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_str_sevenSeg)];
|
||||
if (top.isNull())
|
||||
{
|
||||
top = root.createNestedObject(FPSTR(_str_sevenSeg));
|
||||
}
|
||||
top[FPSTR(_str_perSegment)] = ssLEDPerSegment;
|
||||
top[FPSTR(_str_perPeriod)] = ssLEDPerPeriod;
|
||||
top[FPSTR(_str_startIdx)] = ssStartLED;
|
||||
top[FPSTR(_str_displayMask)] = ssDisplayMask;
|
||||
top[FPSTR(_str_displayCfg)] = ssDisplayConfig;
|
||||
top[FPSTR(_str_displayMsg)] = ssDisplayMessage;
|
||||
top[FPSTR(_str_timeEnabled)] = ssTimeEnabled;
|
||||
top[FPSTR(_str_scrollSpd)] = ssScrollSpeed;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_str_sevenSeg)];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
//if sevenseg section doesn't exist return
|
||||
if (!configComplete)
|
||||
return configComplete;
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_perSegment)], ssLEDPerSegment);
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_perPeriod)], ssLEDPerPeriod);
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_startIdx)], ssStartLED);
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_displayMask)], ssDisplayMask);
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_displayCfg)], ssDisplayConfig);
|
||||
|
||||
String newDisplayMessage;
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_displayMsg)], newDisplayMessage);
|
||||
setSevenSegmentMessage(newDisplayMessage);
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_timeEnabled)], ssTimeEnabled);
|
||||
configComplete &= getJsonValue(top[FPSTR(_str_scrollSpd)], ssScrollSpeed);
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_SEVEN_SEGMENT_DISPLAY;
|
||||
}
|
||||
};
|
||||
|
||||
const char SevenSegmentDisplay::_str_perSegment[] PROGMEM = "perSegment";
|
||||
const char SevenSegmentDisplay::_str_perPeriod[] PROGMEM = "perPeriod";
|
||||
const char SevenSegmentDisplay::_str_startIdx[] PROGMEM = "startIdx";
|
||||
const char SevenSegmentDisplay::_str_displayCfg[] PROGMEM = "displayCfg";
|
||||
const char SevenSegmentDisplay::_str_timeEnabled[] PROGMEM = "timeEnabled";
|
||||
const char SevenSegmentDisplay::_str_scrollSpd[] PROGMEM = "scrollSpd";
|
||||
const char SevenSegmentDisplay::_str_displayMask[] PROGMEM = "displayMask";
|
||||
const char SevenSegmentDisplay::_str_displayMsg[] PROGMEM = "displayMsg";
|
||||
const char SevenSegmentDisplay::_str_sevenSeg[] PROGMEM = "sevenSeg";
|
||||
@@ -42,7 +42,7 @@ class StairwayWipeUsermod : public Usermod {
|
||||
if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete
|
||||
effectCurrent = FX_MODE_STATIC;
|
||||
timeStaticStart = millis();
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 2;
|
||||
}
|
||||
} else if (wipeState == 2) { //static
|
||||
@@ -54,7 +54,7 @@ class StairwayWipeUsermod : public Usermod {
|
||||
#ifdef STAIRCASE_WIPE_OFF
|
||||
effectCurrent = FX_MODE_COLOR_WIPE;
|
||||
strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 4;
|
||||
#else
|
||||
turnOff();
|
||||
@@ -100,7 +100,7 @@ class StairwayWipeUsermod : public Usermod {
|
||||
bool doReverse = (userVar0 == 2);
|
||||
seg.setOption(1, doReverse);
|
||||
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
}
|
||||
|
||||
void turnOff()
|
||||
@@ -111,7 +111,7 @@ class StairwayWipeUsermod : public Usermod {
|
||||
transitionDelayTemp = 4000; //fade out slowly
|
||||
#endif
|
||||
bri = 0;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 0;
|
||||
userVar0 = 0;
|
||||
previousUserVar0 = 0;
|
||||
|
||||
@@ -47,7 +47,7 @@ void userLoop()
|
||||
if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete
|
||||
effectCurrent = FX_MODE_STATIC;
|
||||
timeStaticStart = millis();
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 2;
|
||||
}
|
||||
} else if (wipeState == 2) { //static
|
||||
@@ -59,7 +59,7 @@ void userLoop()
|
||||
#ifdef STAIRCASE_WIPE_OFF
|
||||
effectCurrent = FX_MODE_COLOR_WIPE;
|
||||
strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 4;
|
||||
#else
|
||||
turnOff();
|
||||
@@ -93,7 +93,7 @@ void startWipe()
|
||||
bool doReverse = (userVar0 == 2);
|
||||
seg.setOption(1, doReverse);
|
||||
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
}
|
||||
|
||||
void turnOff()
|
||||
@@ -104,7 +104,7 @@ void turnOff()
|
||||
transitionDelayTemp = 4000; //fade out slowly
|
||||
#endif
|
||||
bri = 0;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 0;
|
||||
userVar0 = 0;
|
||||
previousUserVar0 = 0;
|
||||
|
||||
42
usermods/usermod_rotary_brightness_color/README.md
Normal file
42
usermods/usermod_rotary_brightness_color/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Rotary Encoder (Brightness and Color)
|
||||
|
||||
V2 usermod that allows changing brightness and color using a rotary encoder,
|
||||
change between modes by pressing a button (many encoders have one included)
|
||||
|
||||
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
|
||||
|
||||
define `USERMOD_ROTARY_ENCODER_BRIGHTNESS_COLOR` e.g.
|
||||
|
||||
`#define USERMOD_ROTARY_ENCODER_BRIGHTNESS_COLOR` in my_config.h
|
||||
|
||||
or add `-D USERMOD_ROTARY_ENCODER_BRIGHTNESS_COLOR` to `build_flags` in platformio_override.ini
|
||||
|
||||
### Define Your Options
|
||||
|
||||
Open Usermod Settings in WLED to change settings:
|
||||
|
||||
`fadeAmount` - how many points to fade the Neopixel with each step of the rotary encoder (default 5)
|
||||
`pin[3]` - pins to connect to the rotary encoder:
|
||||
- `pin[0]` is pin A on your rotary encoder
|
||||
- `pin[1]` is pin B on your rotary encoder
|
||||
- `pin[2]` is the button on your rotary encoder (optional, set to -1 to disable the button and the rotary encoder will control brightness only)
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-07
|
||||
* Upgraded to work with the latest WLED code, and make settings configurable in Usermod Settings
|
||||
@@ -0,0 +1,189 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//v2 usermod that allows to change brightness and color using a rotary encoder,
|
||||
//change between modes by pressing a button (many encoders have one included)
|
||||
class RotaryEncoderBrightnessColor : public Usermod
|
||||
{
|
||||
private:
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
|
||||
unsigned char select_state = 0; // 0 = brightness 1 = color
|
||||
unsigned char button_state = HIGH;
|
||||
unsigned char prev_button_state = HIGH;
|
||||
CRGB fastled_col;
|
||||
CHSV prim_hsv;
|
||||
int16_t new_val;
|
||||
|
||||
unsigned char Enc_A;
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
// private class memebers configurable by Usermod Settings (defaults set inside readFromConfig())
|
||||
int8_t pins[3]; // pins[0] = DT from encoder, pins[1] = CLK from encoder, pins[2] = CLK from encoder (optional)
|
||||
int fadeAmount; // how many points to fade the Neopixel with each step
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
//Serial.println("Hello from my usermod!");
|
||||
pinMode(pins[0], INPUT_PULLUP);
|
||||
pinMode(pins[1], INPUT_PULLUP);
|
||||
if(pins[2] >= 0) pinMode(pins[2], INPUT_PULLUP);
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||
{
|
||||
if(pins[2] >= 0) {
|
||||
button_state = digitalRead(pins[2]);
|
||||
if (prev_button_state != button_state)
|
||||
{
|
||||
if (button_state == LOW)
|
||||
{
|
||||
if (select_state == 1)
|
||||
{
|
||||
select_state = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
select_state = 1;
|
||||
}
|
||||
prev_button_state = button_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
int Enc_A = digitalRead(pins[0]); // Read encoder pins
|
||||
int Enc_B = digitalRead(pins[1]);
|
||||
if ((!Enc_A) && (Enc_A_prev))
|
||||
{ // A has gone from high to low
|
||||
if (Enc_B == HIGH)
|
||||
{ // B is high so clockwise
|
||||
if (select_state == 0)
|
||||
{
|
||||
if (bri + fadeAmount <= 255)
|
||||
bri += fadeAmount; // increase the brightness, dont go over 255
|
||||
}
|
||||
else
|
||||
{
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
new_val = (int16_t)prim_hsv.h + fadeAmount;
|
||||
if (new_val > 255)
|
||||
new_val -= 255; // roll-over if bigger than 255
|
||||
if (new_val < 0)
|
||||
new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == LOW)
|
||||
{ // B is low so counter-clockwise
|
||||
if (select_state == 0)
|
||||
{
|
||||
if (bri - fadeAmount >= 0)
|
||||
bri -= fadeAmount; // decrease the brightness, dont go below 0
|
||||
}
|
||||
else
|
||||
{
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
new_val = (int16_t)prim_hsv.h - fadeAmount;
|
||||
if (new_val > 255)
|
||||
new_val -= 255; // roll-over if bigger than 255
|
||||
if (new_val < 0)
|
||||
new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
}
|
||||
}
|
||||
//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(CALL_MODE_BUTTON);
|
||||
updateInterfaces(CALL_MODE_BUTTON);
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("rotEncBrightness");
|
||||
top["fadeAmount"] = fadeAmount;
|
||||
JsonArray pinArray = top.createNestedArray("pin");
|
||||
pinArray.add(pins[0]);
|
||||
pinArray.add(pins[1]);
|
||||
pinArray.add(pins[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* This example uses a more robust method of checking for missing values in the config, and setting back to defaults:
|
||||
* - The getJsonValue() function copies the value to the variable only if the key requested is present, returning false with no copy if the value isn't present
|
||||
* - configComplete is used to return false if any value is missing, not just if the main object is missing
|
||||
* - The defaults are loaded every time readFromConfig() is run, not just once after boot
|
||||
*
|
||||
* This ensures that missing values are added to the config, with their default values, in the rare but plauible cases of:
|
||||
* - a single value being missing at boot, e.g. if the Usermod was upgraded and a new setting was added
|
||||
* - a single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
*
|
||||
* If configComplete is false, the default values are already set, and by returning false, WLED now knows it needs to save the defaults by calling addToConfig()
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// set defaults here, they will be set before setup() is called, and if any values parsed from ArduinoJson below are missing, the default will be used instead
|
||||
fadeAmount = 5;
|
||||
pins[0] = -1;
|
||||
pins[1] = -1;
|
||||
pins[2] = -1;
|
||||
|
||||
JsonObject top = root["rotEncBrightness"];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
configComplete &= getJsonValue(top["fadeAmount"], fadeAmount);
|
||||
configComplete &= getJsonValue(top["pin"][0], pins[0]);
|
||||
configComplete &= getJsonValue(top["pin"][1], pins[1]);
|
||||
configComplete &= getJsonValue(top["pin"][2], pins[2]);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
};
|
||||
@@ -38,7 +38,7 @@ class AutoSaveUsermod : public Usermod {
|
||||
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;
|
||||
unsigned long autoSaveAfter = 0;
|
||||
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownEffectSpeed = 0;
|
||||
@@ -87,6 +87,12 @@ class AutoSaveUsermod : public Usermod {
|
||||
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
|
||||
#endif
|
||||
initDone = true;
|
||||
if (enabled && applyAutoSaveOnBoot) applyPreset(autoSavePreset);
|
||||
knownBrightness = bri;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
@@ -97,21 +103,11 @@ class AutoSaveUsermod : public Usermod {
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (!autoSaveAfterSec || !enabled || strip.isUpdating()) return; // setting 0 as autosave seconds disables autosave
|
||||
if (!autoSaveAfterSec || !enabled || strip.isUpdating() || currentPreset>0) 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) {
|
||||
|
||||
@@ -31,6 +31,21 @@
|
||||
#ifndef FLD_PIN_SDA
|
||||
#define FLD_PIN_SDA 21
|
||||
#endif
|
||||
#ifndef FLD_PIN_CLOCKSPI
|
||||
#define FLD_PIN_CLOCKSPI 18
|
||||
#endif
|
||||
#ifndef FLD_PIN_DATASPI
|
||||
#define FLD_PIN_DATASPI 23
|
||||
#endif
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 19
|
||||
#endif
|
||||
#ifndef FLD_PIN_CS
|
||||
#define FLD_PIN_CS 5
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 26
|
||||
#endif
|
||||
#else
|
||||
#ifndef FLD_PIN_SCL
|
||||
#define FLD_PIN_SCL 5
|
||||
@@ -38,6 +53,21 @@
|
||||
#ifndef FLD_PIN_SDA
|
||||
#define FLD_PIN_SDA 4
|
||||
#endif
|
||||
#ifndef FLD_PIN_CLOCKSPI
|
||||
#define FLD_PIN_CLOCKSPI 14
|
||||
#endif
|
||||
#ifndef FLD_PIN_DATASPI
|
||||
#define FLD_PIN_DATASPI 13
|
||||
#endif
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 12
|
||||
#endif
|
||||
#ifndef FLD_PIN_CS
|
||||
#define FLD_PIN_CS 15
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 16
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// When to time out to the clock or blank the screen
|
||||
@@ -64,11 +94,13 @@ typedef enum {
|
||||
|
||||
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
|
||||
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
|
||||
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
||||
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
||||
} DisplayType;
|
||||
|
||||
class FourLineDisplayUsermod : public Usermod {
|
||||
@@ -80,8 +112,14 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
// 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()
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
|
||||
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
|
||||
DisplayType type = SSD1306; // display type
|
||||
#else
|
||||
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
|
||||
DisplayType type = SSD1306_SPI; // display type
|
||||
#endif
|
||||
bool flip = false; // flip display 180°
|
||||
uint8_t contrast = 10; // screen contrast
|
||||
uint8_t lineHeight = 1; // 1 row or 2 rows
|
||||
@@ -118,6 +156,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
static const char _flip[];
|
||||
static const char _sleepMode[];
|
||||
static const char _clockMode[];
|
||||
static const char _busClkFrequency[];
|
||||
|
||||
// If display does not work or looks corrupted check the
|
||||
// constructor reference:
|
||||
@@ -131,65 +170,92 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// 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; }
|
||||
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
|
||||
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }};
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||
} else {
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||
}
|
||||
DEBUG_PRINTLN(F("Allocating display."));
|
||||
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
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // 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
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // 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
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // 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
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // 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
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_SPI:
|
||||
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1306_SPI64:
|
||||
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 2;
|
||||
break;
|
||||
default:
|
||||
u8x8 = nullptr;
|
||||
}
|
||||
if (nullptr == u8x8) {
|
||||
DEBUG_PRINTLN(F("Display init failed."));
|
||||
for (byte i=0; i<5 && ioPin[i]>=0; i++) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||
type = NONE;
|
||||
return;
|
||||
}
|
||||
(static_cast<U8X8*>(u8x8))->begin();
|
||||
|
||||
initDone = true;
|
||||
DEBUG_PRINTLN(F("Starting display."));
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
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
|
||||
@@ -211,40 +277,46 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
*/
|
||||
void setFlipMode(uint8_t mode) {
|
||||
if (type==NONE) return;
|
||||
(static_cast<U8X8*>(u8x8))->setFlipMode(mode);
|
||||
u8x8->setFlipMode(mode);
|
||||
}
|
||||
void setContrast(uint8_t contrast) {
|
||||
if (type==NONE) return;
|
||||
(static_cast<U8X8*>(u8x8))->setContrast(contrast);
|
||||
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);
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
|
||||
else 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);
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
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);
|
||||
u8x8->setFont(font);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
|
||||
else u8x8->drawGlyph(col, row, glyph);
|
||||
}
|
||||
uint8_t getCols() {
|
||||
if (type==NONE) return 0;
|
||||
return (static_cast<U8X8*>(u8x8))->getCols();
|
||||
return u8x8->getCols();
|
||||
}
|
||||
void clear() {
|
||||
if (type==NONE) return;
|
||||
(static_cast<U8X8*>(u8x8))->clear();
|
||||
u8x8->clear();
|
||||
}
|
||||
void setPowerSave(uint8_t save) {
|
||||
if (type==NONE) return;
|
||||
(static_cast<U8X8*>(u8x8))->setPowerSave(save);
|
||||
u8x8->setPowerSave(save);
|
||||
}
|
||||
|
||||
void center(String &line, uint8_t width) {
|
||||
int len = line.length();
|
||||
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
|
||||
for (byte i=line.length(); i<width; i++) line += ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,8 +349,8 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
(knownEffectIntensity != effectIntensity) ||
|
||||
(knownMode != strip.getMode()) ||
|
||||
(knownPalette != strip.getSegment(0).palette)) {
|
||||
knownHour = 99; // force time update
|
||||
clear();
|
||||
knownHour = 99; // force time update
|
||||
lastRedraw = now; // update lastRedraw marker
|
||||
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
|
||||
// change line every 5s
|
||||
showName = !showName;
|
||||
@@ -303,13 +375,13 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
break;
|
||||
}
|
||||
knownHour = 99; // force time update
|
||||
// do not update lastRedraw marker if just switching row contenet
|
||||
} 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();
|
||||
@@ -317,9 +389,6 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
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);
|
||||
|
||||
@@ -333,13 +402,14 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
knownEffectIntensity = effectIntensity;
|
||||
|
||||
// Do the actual drawing
|
||||
|
||||
String line;
|
||||
// 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());
|
||||
line = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
|
||||
center(line, getCols()-2);
|
||||
drawString(1, 0, line.c_str());
|
||||
// Print `~` char to indicate that SSID is longer, than our display
|
||||
if (knownSsid.length() > getCols()) {
|
||||
if (knownSsid.length() > (int)getCols()-1) {
|
||||
drawString(getCols() - 1, 0, "~");
|
||||
}
|
||||
|
||||
@@ -350,12 +420,12 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
drawString(1, lineHeight, apPass);
|
||||
} else {
|
||||
// alternate IP address and server name
|
||||
String secondLine = knownIp.toString();
|
||||
line = knownIp.toString();
|
||||
if (showName && strcmp(serverDescription, "WLED") != 0) {
|
||||
secondLine = serverDescription;
|
||||
line = serverDescription;
|
||||
}
|
||||
for (uint8_t i=secondLine.length(); i<getCols()-1; i++) secondLine += ' ';
|
||||
drawString(1, lineHeight, secondLine.c_str());
|
||||
center(line, getCols()-1);
|
||||
drawString(1, lineHeight, line.c_str());
|
||||
}
|
||||
|
||||
// draw third and fourth row
|
||||
@@ -388,10 +458,8 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, line);
|
||||
break;
|
||||
case FLD_LINE_TIME:
|
||||
showTime(false);
|
||||
break;
|
||||
default:
|
||||
// unknown type, do nothing
|
||||
showTime(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -455,17 +523,30 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
*/
|
||||
void overlay(const char* line1, const char *line2, long showHowLong) {
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
// Turn the display back on (includes clear())
|
||||
sleepOrClock(false);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
// Print the overlay
|
||||
clear();
|
||||
if (line1) drawString(0, 1*lineHeight, line1);
|
||||
if (line2) drawString(0, 2*lineHeight, line2);
|
||||
if (line1) {
|
||||
String buf = line1;
|
||||
center(buf, getCols());
|
||||
drawString(0, 1*lineHeight, buf.c_str());
|
||||
}
|
||||
if (line2) {
|
||||
String buf = line2;
|
||||
center(buf, getCols());
|
||||
drawString(0, 2*lineHeight, buf.c_str());
|
||||
}
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
void setLineType(byte lT) {
|
||||
lineType = (Line4Type) lT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Line 3 or 4 (last two lines) can be marked with an
|
||||
* arrow in the first column. Pass 2 or 3 to this to
|
||||
@@ -485,6 +566,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* Enable sleep (turn the display off) or clock mode.
|
||||
*/
|
||||
void sleepOrClock(bool enabled) {
|
||||
clear();
|
||||
if (enabled) {
|
||||
if (clockMode) showTime();
|
||||
else setPowerSave(1);
|
||||
@@ -510,8 +592,6 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
|
||||
// Time hasn't changed.
|
||||
if (!fullScreen) return;
|
||||
} else {
|
||||
//if (fullScreen) clear();
|
||||
}
|
||||
knownMinute = minuteCurrent;
|
||||
knownHour = hourCurrent;
|
||||
@@ -595,10 +675,10 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* 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);
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
JsonArray io_pin = top.createNestedArray("pin");
|
||||
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||
top["help4PinTypes"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["type"] = type;
|
||||
top[FPSTR(_flip)] = (bool) flip;
|
||||
top[FPSTR(_contrast)] = contrast;
|
||||
@@ -606,6 +686,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
|
||||
top[FPSTR(_sleepMode)] = (bool) sleepMode;
|
||||
top[FPSTR(_clockMode)] = (bool) clockMode;
|
||||
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
|
||||
DEBUG_PRINTLN(F("4 Line Display config saved."));
|
||||
}
|
||||
|
||||
@@ -620,8 +701,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
bool needsRedraw = false;
|
||||
DisplayType newType = type;
|
||||
int8_t newScl = sclPin;
|
||||
int8_t newSda = sdaPin;
|
||||
int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
@@ -630,45 +710,47 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
return false;
|
||||
}
|
||||
|
||||
newScl = top["pin"][0] | newScl;
|
||||
newSda = top["pin"][1] | newSda;
|
||||
newType = top["type"] | newType;
|
||||
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
|
||||
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;
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
sclPin = newScl;
|
||||
sdaPin = newSda;
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
type = newType;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// 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) {
|
||||
bool pinsChanged = false;
|
||||
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
|
||||
if (pinsChanged || type!=newType) {
|
||||
if (type != NONE) delete u8x8;
|
||||
for (byte i=0; i<5; i++) {
|
||||
if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||
ioPin[i] = newPin[i];
|
||||
}
|
||||
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
|
||||
type = NONE;
|
||||
return true;
|
||||
} else type = newType;
|
||||
setup();
|
||||
needsRedraw |= true;
|
||||
}
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
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;
|
||||
return !(top[_busClkFrequency]).isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -681,10 +763,11 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
};
|
||||
|
||||
// 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";
|
||||
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";
|
||||
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
|
||||
|
||||
45
usermods/usermod_v2_four_line_display_ALT/readme.md
Normal file
45
usermods/usermod_v2_four_line_display_ALT/readme.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# I2C 4 Line Display Usermod ALT
|
||||
|
||||
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
|
||||
"usermod_v2_four_line_display"
|
||||
"usermod_v2_rotary_encoder_ui"
|
||||
|
||||
The core of these usermods are a copy of the originals. The main changes are done to the FourLineDisplay usermod.
|
||||
The display usermod UI has been completely changed.
|
||||
|
||||
|
||||
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
|
||||
Without the display it functions identical to the original.
|
||||
The original "usermod_v2_auto_save" will not work with the display just yet.
|
||||
|
||||
Press the encoder to cycle through the options:
|
||||
*Brightness
|
||||
*Speed
|
||||
*Intensity
|
||||
*Palette
|
||||
*Effect
|
||||
*Main Color (only if display is used)
|
||||
*Saturation (only if display is used)
|
||||
|
||||
Press and hold the encoder to display Network Info
|
||||
if AP is active then it will display AP ssid and Password
|
||||
|
||||
Also shows if the timer is enabled
|
||||
|
||||
[See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI)
|
||||
|
||||
## Installation
|
||||
|
||||
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions
|
||||
Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
|
||||
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
|
||||
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-10
|
||||
* First public release
|
||||
@@ -0,0 +1,970 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||
|
||||
//
|
||||
// Insired by the usermod_v2_four_line_display
|
||||
//
|
||||
// v2 usermod for using 128x32 or 128x64 i2c
|
||||
// OLED displays to provide a four line display
|
||||
// for WLED.
|
||||
//
|
||||
// Dependencies
|
||||
// * This usermod REQURES the ModeSortUsermod
|
||||
// * This Usermod works best, by far, when coupled
|
||||
// with RotaryEncoderUIUsermod.
|
||||
//
|
||||
// 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
|
||||
#ifndef FLD_PIN_CLOCKSPI
|
||||
#define FLD_PIN_CLOCKSPI 18
|
||||
#endif
|
||||
#ifndef FLD_PIN_DATASPI
|
||||
#define FLD_PIN_DATASPI 23
|
||||
#endif
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 19
|
||||
#endif
|
||||
#ifndef FLD_PIN_CS
|
||||
#define FLD_PIN_CS 5
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 26
|
||||
#endif
|
||||
#else
|
||||
#ifndef FLD_PIN_SCL
|
||||
#define FLD_PIN_SCL 5
|
||||
#endif
|
||||
#ifndef FLD_PIN_SDA
|
||||
#define FLD_PIN_SDA 4
|
||||
#endif
|
||||
#ifndef FLD_PIN_CLOCKSPI
|
||||
#define FLD_PIN_CLOCKSPI 14
|
||||
#endif
|
||||
#ifndef FLD_PIN_DATASPI
|
||||
#define FLD_PIN_DATASPI 13
|
||||
#endif
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 12
|
||||
#endif
|
||||
#ifndef FLD_PIN_CS
|
||||
#define FLD_PIN_CS 15
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 16
|
||||
#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 100
|
||||
|
||||
// Extra char (+1) for null
|
||||
#define LINE_BUFFER_SIZE 16+1
|
||||
#define MAX_JSON_CHARS 19+1
|
||||
#define MAX_MODE_LINE_SPACE 13+1
|
||||
|
||||
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
|
||||
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
||||
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
||||
} DisplayType;
|
||||
|
||||
/*
|
||||
Fontname: benji_custom_icons_1x
|
||||
Copyright:
|
||||
Glyphs: 1/1
|
||||
BBX Build Mode: 3
|
||||
* 4 = custom palette
|
||||
*/
|
||||
const uint8_t u8x8_font_benji_custom_icons_1x1[13] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_1x1") =
|
||||
"\4\4\1\1<n\372\377\275\277\26\34";
|
||||
|
||||
/*
|
||||
Fontname: benji_custom_icons_2x
|
||||
Copyright:
|
||||
Glyphs: 8/8
|
||||
BBX Build Mode: 3
|
||||
// all the icons uses are consolidated into a single library to simplify code
|
||||
// these are just the required icons stripped from the U8x8 libraries in addition to a few new custom icons
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = custom saturation
|
||||
*/
|
||||
const uint8_t u8x8_font_benji_custom_icons_2x2[261] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_2x2") =
|
||||
"\1\10\2\2\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\7\17\317\317\17\7\3"
|
||||
"\60\60\1\1\374\370\360\340\340\300\200\0\374\370\360\340\340\300\200\0\77\37\17\7\7\3\1\0\77\37\17\7"
|
||||
"\7\3\1\0\0\200\340\360\377\376\374\360\0\0\300\200\0\0\0\0\17\77\177\377\17\7\301\340\370\374\377\377"
|
||||
"\377|\0\0\360\370\234\236\376\363\363\377\377\363\363\376><\370\360\3\17\77yy\377\377\377\377\317\17\17"
|
||||
"\17\17\7\3\360\360\360\360\366\377\377\366\360\360\360\360\0\0\0\0\377\377\377\377\237\17\17\237\377\377\377\377"
|
||||
"\6\17\17\6\340\370\374\376\377\340\200\0\0\0\0\0\0\0\0\0\3\17\37\77\177\177\177\377\376|||"
|
||||
"\70\30\14\0\0\0\0\0\0\0\0``\360\370|<\36\7\2\0\300\360\376\377\177\77\36\0\1\1\0"
|
||||
"\0\0\0\0\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\4\10\310\310\10\4\3"
|
||||
"\60\60\1\1";
|
||||
|
||||
/*
|
||||
Fontname: benji_custom_icons_6x
|
||||
Copyright:
|
||||
Glyphs: 8/8
|
||||
BBX Build Mode: 3
|
||||
// 6x6 icons libraries take up a lot of memory thus all the icons uses are consolidated into a single library
|
||||
// these are just the required icons stripped from the U8x8 libraries in addition to a few new custom icons
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = custom saturation
|
||||
*/
|
||||
const uint8_t u8x8_font_benji_custom_icons_6x6[2308] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_6x6") =
|
||||
"\1\10\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0"
|
||||
"\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7"
|
||||
"\0\0\0\0\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
|
||||
"\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
|
||||
"\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
|
||||
"\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
|
||||
"\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
|
||||
"\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
|
||||
"\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
|
||||
"\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
|
||||
"\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
|
||||
"\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\376\374\374\370\360\340\300\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360\374"
|
||||
"\377\377\377\377\377\377\377\377\377\376\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
|
||||
"\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
|
||||
"\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
|
||||
"\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
|
||||
"\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
|
||||
"\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
|
||||
"\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
|
||||
"\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
|
||||
"\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
|
||||
"\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
|
||||
"\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
|
||||
"\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
|
||||
"\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377\377"
|
||||
"\377\377\377\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\177\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
|
||||
"\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
|
||||
"\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
|
||||
"\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
|
||||
"\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`px\374\376\377\377\377\377\377\377"
|
||||
"\177\177\177\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0"
|
||||
"\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\7\17\17\7\3"
|
||||
"\0\200\300\340\360\360\370\370\370\374\374\374\374\370\370\370\360\360\340\300\200\0\3\7\17\17\7\3\0\0\0\0"
|
||||
"\0\0\0\0\300\340\360\360\340\300\0\0\0\0\340\374\377\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177"
|
||||
"\177\177\177\177\177\377\374\340\0\0\0\0\300\340\360\360\340\300\0\0\0\1\3\3\1\0\0\0\0\0\1\17"
|
||||
"\77\177\370\340\300\200\200\0\0\0\0\0\0\0\0\200\200\300\340\370\177\77\17\1\0\0\0\0\0\1\3\3"
|
||||
"\1\0\0\0\0\0\0\0\0\0\60x\374\374x\60\0\0\0\1\3\3\7\7\7\16\16\16\16\7\7\7"
|
||||
"\3\3\1\0\0\0\60x\374\374x\60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0";
|
||||
|
||||
class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
|
||||
bool initDone = false;
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// HW interface & configuration
|
||||
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
|
||||
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
|
||||
DisplayType type = SSD1306_64; // display type
|
||||
#else
|
||||
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
|
||||
DisplayType type = SSD1306_SPI; // display type
|
||||
#endif
|
||||
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
|
||||
|
||||
// needRedraw marks if redraw is required to prevent often redrawing.
|
||||
bool needRedraw = true;
|
||||
|
||||
// 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;
|
||||
byte brightness100;
|
||||
byte fxspeed100;
|
||||
byte fxintensity100;
|
||||
bool knownnightlight = nightlightActive;
|
||||
bool wificonnected = interfacesInited;
|
||||
bool powerON = true;
|
||||
|
||||
bool displayTurnedOff = false;
|
||||
unsigned long lastUpdate = 0;
|
||||
unsigned long lastRedraw = 0;
|
||||
unsigned long overlayUntil = 0;
|
||||
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||
byte markLineNum = 0;
|
||||
byte markColNum = 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[];
|
||||
static const char _busClkFrequency[];
|
||||
|
||||
// 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 (type == SSD1306_SPI || type == SSD1306_SPI64) {
|
||||
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }};
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||
} else {
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||
}
|
||||
DEBUG_PRINTLN(F("Allocating display."));
|
||||
switch (type) {
|
||||
case SSD1306:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SH1106:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_64:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1305:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1305_64:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_SPI:
|
||||
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1306_SPI64:
|
||||
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 2;
|
||||
break;
|
||||
default:
|
||||
u8x8 = nullptr;
|
||||
}
|
||||
if (nullptr == u8x8) {
|
||||
DEBUG_PRINTLN(F("Display init failed."));
|
||||
for (byte i=0; i<5 && ioPin[i]>=0; i++) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||
type = NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
initDone = true;
|
||||
DEBUG_PRINTLN(F("Starting display."));
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
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...");
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/**
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (displayTurnedOff && millis() - lastUpdate < 1000) {
|
||||
return;
|
||||
}else if (millis() - lastUpdate < refreshRate){
|
||||
return;}
|
||||
redraw(false);
|
||||
lastUpdate = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrappers for screen drawing
|
||||
*/
|
||||
void setFlipMode(uint8_t mode) {
|
||||
if (type==NONE) return;
|
||||
u8x8->setFlipMode(mode);
|
||||
}
|
||||
void setContrast(uint8_t contrast) {
|
||||
if (type==NONE) return;
|
||||
u8x8->setContrast(contrast);
|
||||
}
|
||||
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
|
||||
if (type==NONE) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
|
||||
else u8x8->drawString(col, row, string);
|
||||
}
|
||||
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
|
||||
if (type==NONE) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
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;
|
||||
u8x8->setFont(font);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
|
||||
else u8x8->drawGlyph(col, row, glyph);
|
||||
}
|
||||
uint8_t getCols() {
|
||||
if (type==NONE) return 0;
|
||||
return u8x8->getCols();
|
||||
}
|
||||
void clear() {
|
||||
if (type==NONE) return;
|
||||
u8x8->clear();
|
||||
}
|
||||
void setPowerSave(uint8_t save) {
|
||||
if (type==NONE) return;
|
||||
u8x8->setPowerSave(save);
|
||||
}
|
||||
|
||||
void center(String &line, uint8_t width) {
|
||||
int len = line.length();
|
||||
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
|
||||
for (byte i=line.length(); i<width; i++) line += ' ';
|
||||
}
|
||||
|
||||
//function to update lastredraw
|
||||
void updateRedrawTime(){
|
||||
lastRedraw = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraw the screen (but only if things have changed
|
||||
* or if forceRedraw).
|
||||
*/
|
||||
void redraw(bool forceRedraw) {
|
||||
if (type==NONE) return;
|
||||
if (overlayUntil > 0) {
|
||||
if (millis() >= 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) {
|
||||
needRedraw = true;
|
||||
} else if ((bri == 0 && powerON) || (bri > 0 && !powerON)) { //trigger power icon
|
||||
powerON = !powerON;
|
||||
drawStatusIcons();
|
||||
lastRedraw = millis();
|
||||
} else if (knownnightlight != nightlightActive) { //trigger moon icon
|
||||
knownnightlight = nightlightActive;
|
||||
drawStatusIcons();
|
||||
if (knownnightlight) overlay(" Timer On", 1000, 6);
|
||||
lastRedraw = millis();
|
||||
}else if (wificonnected != interfacesInited){ //trigger wifi icon
|
||||
wificonnected = interfacesInited;
|
||||
drawStatusIcons();
|
||||
lastRedraw = millis();
|
||||
} else if (knownMode != effectCurrent) {
|
||||
knownMode = effectCurrent;
|
||||
if(displayTurnedOff)needRedraw = true;
|
||||
else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3);
|
||||
} else if (knownPalette != effectPalette) {
|
||||
knownPalette = effectPalette;
|
||||
if(displayTurnedOff)needRedraw = true;
|
||||
else showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2);
|
||||
} else if (knownBrightness != bri) {
|
||||
if(displayTurnedOff && nightlightActive){needRedraw = false; knownBrightness = bri;}
|
||||
else if(displayTurnedOff)needRedraw = true;
|
||||
else updateBrightness();
|
||||
} else if (knownEffectSpeed != effectSpeed) {
|
||||
if(displayTurnedOff)needRedraw = true;
|
||||
else updateSpeed();
|
||||
} else if (knownEffectIntensity != effectIntensity) {
|
||||
if(displayTurnedOff)needRedraw = true;
|
||||
else updateIntensity();
|
||||
}
|
||||
|
||||
|
||||
if (!needRedraw) {
|
||||
// Nothing to change.
|
||||
// Turn off display after 1 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.
|
||||
sleepOrClock(true);
|
||||
} else if (displayTurnedOff && clockMode) {
|
||||
showTime();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
needRedraw = false;
|
||||
lastRedraw = millis();
|
||||
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
}
|
||||
|
||||
// Update last known values.
|
||||
knownSsid = apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = effectCurrent;
|
||||
knownPalette = effectPalette;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
knownnightlight = nightlightActive;
|
||||
wificonnected = interfacesInited;
|
||||
|
||||
// Do the actual drawing
|
||||
// First row: Icons
|
||||
draw2x2GlyphIcons();
|
||||
drawArrow();
|
||||
drawStatusIcons();
|
||||
|
||||
// Second row
|
||||
updateBrightness();
|
||||
updateSpeed();
|
||||
updateIntensity();
|
||||
|
||||
// Third row
|
||||
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2); //Palette info
|
||||
|
||||
// Fourth row
|
||||
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); //Effect Mode info
|
||||
}
|
||||
|
||||
void updateBrightness(){
|
||||
knownBrightness = bri;
|
||||
if(overlayUntil == 0){
|
||||
brightness100 = (((float)(bri)/255)*100);
|
||||
char lineBuffer[4];
|
||||
sprintf_P(lineBuffer, PSTR("%-3d"), brightness100);
|
||||
drawString(1, lineHeight, lineBuffer);
|
||||
lastRedraw = millis();}
|
||||
}
|
||||
|
||||
void updateSpeed(){
|
||||
knownEffectSpeed = effectSpeed;
|
||||
if(overlayUntil == 0){
|
||||
fxspeed100 = (((float)(effectSpeed)/255)*100);
|
||||
char lineBuffer[4];
|
||||
sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100);
|
||||
drawString(5, lineHeight, lineBuffer);
|
||||
lastRedraw = millis();}
|
||||
}
|
||||
|
||||
void updateIntensity(){
|
||||
knownEffectIntensity = effectIntensity;
|
||||
if(overlayUntil == 0){
|
||||
fxintensity100 = (((float)(effectIntensity)/255)*100);
|
||||
char lineBuffer[4];
|
||||
sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100);
|
||||
drawString(9, lineHeight, lineBuffer);
|
||||
lastRedraw = millis();}
|
||||
}
|
||||
|
||||
void draw2x2GlyphIcons(){
|
||||
if(lineHeight == 2){
|
||||
drawGlyph(1, 0, 1, u8x8_font_benji_custom_icons_2x2, true);//brightness icon
|
||||
drawGlyph(5, 0, 2, u8x8_font_benji_custom_icons_2x2, true);//speed icon
|
||||
drawGlyph(9, 0, 3, u8x8_font_benji_custom_icons_2x2, true);//intensity icon
|
||||
drawGlyph(14, 2*lineHeight, 4, u8x8_font_benji_custom_icons_2x2, true);//palette icon
|
||||
drawGlyph(14, 3*lineHeight, 5, u8x8_font_benji_custom_icons_2x2, true);//effect icon
|
||||
}
|
||||
else{
|
||||
drawGlyph(2, 0, 69, u8x8_font_open_iconic_weather_1x1);//brightness icon
|
||||
drawGlyph(6, 0, 72, u8x8_font_open_iconic_play_1x1);//speed icon
|
||||
drawGlyph(10, 0, 78, u8x8_font_open_iconic_thing_1x1);//intensity icon
|
||||
drawGlyph(15, 2*lineHeight, 4, u8x8_font_benji_custom_icons_1x1);//palette icon
|
||||
drawGlyph(15, 3*lineHeight, 70, u8x8_font_open_iconic_thing_1x1);//effect icon
|
||||
}
|
||||
}
|
||||
|
||||
void drawStatusIcons(){
|
||||
drawGlyph(14, 0, 80 + (wificonnected?0:1), u8x8_font_open_iconic_embedded_1x1, true); // wifi icon
|
||||
drawGlyph(15, 0, 78 + (bri > 0 ? 0 : 3), u8x8_font_open_iconic_embedded_1x1, true); // power icon
|
||||
drawGlyph(13, 0, 66 + (nightlightActive?0:4), u8x8_font_open_iconic_weather_1x1, true); // moon icon for nighlight mode
|
||||
}
|
||||
|
||||
/**
|
||||
* marks the position of the arrow showing
|
||||
* the current setting being changed
|
||||
* pass line and colum info
|
||||
*/
|
||||
void setMarkLine(byte newMarkLineNum, byte newMarkColNum) {
|
||||
markLineNum = newMarkLineNum;
|
||||
markColNum = newMarkColNum;
|
||||
}
|
||||
|
||||
//Draw the arrow for the current setting beiong changed
|
||||
void drawArrow(){
|
||||
if(markColNum != 255 && markLineNum !=255)drawGlyph(markColNum, markLineNum*lineHeight, 69, u8x8_font_open_iconic_play_1x1);
|
||||
}
|
||||
|
||||
//Display the current effect or palette (desiredEntry)
|
||||
// on the appropriate line (row).
|
||||
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
|
||||
knownMode = effectCurrent;
|
||||
knownPalette = effectPalette;
|
||||
if(overlayUntil == 0){
|
||||
char lineBuffer[MAX_JSON_CHARS];
|
||||
char smallBuffer1[MAX_MODE_LINE_SPACE];
|
||||
char smallBuffer2[MAX_MODE_LINE_SPACE];
|
||||
char smallBuffer3[MAX_MODE_LINE_SPACE+1];
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
bool spaceHit = false;
|
||||
uint8_t printedChars = 0;
|
||||
uint8_t smallChars1 = 0;
|
||||
uint8_t smallChars2 = 0;
|
||||
uint8_t smallChars3 = 0;
|
||||
uint8_t totalCount = 0;
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < strlen_P(qstring); i++) { //find and get the full text for printing
|
||||
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 != inputEffPal)) break;
|
||||
lineBuffer[printedChars++] = singleJsonSymbol;
|
||||
totalCount++;
|
||||
}
|
||||
if ((qComma > inputEffPal)) break;
|
||||
}
|
||||
|
||||
if(lineHeight ==2){ // use this code for 8 line display
|
||||
if(printedChars < (MAX_MODE_LINE_SPACE)){ // use big font if the text fits
|
||||
for (;printedChars < (MAX_MODE_LINE_SPACE-1); printedChars++) {lineBuffer[printedChars]=' '; }
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(1, row*lineHeight, lineBuffer);
|
||||
lastRedraw = millis();
|
||||
}else{ // for long names divide the text into 2 lines and print them small
|
||||
for (uint8_t i = 0; i < printedChars; i++){
|
||||
switch (lineBuffer[i]){
|
||||
case ' ':
|
||||
if(i > 4 && !spaceHit) {
|
||||
spaceHit = true;
|
||||
break;}
|
||||
if(!spaceHit) smallBuffer1[smallChars1++] = lineBuffer[i];
|
||||
if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
|
||||
break;
|
||||
default:
|
||||
if(!spaceHit) smallBuffer1[smallChars1++] = lineBuffer[i];
|
||||
if (spaceHit) smallBuffer2[smallChars2++] = lineBuffer[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; smallChars1 < (MAX_MODE_LINE_SPACE-1); smallChars1++) smallBuffer1[smallChars1]=' ';
|
||||
smallBuffer1[smallChars1] = 0;
|
||||
drawString(1, row*lineHeight, smallBuffer1, true);
|
||||
for (; smallChars2 < (MAX_MODE_LINE_SPACE-1); smallChars2++) smallBuffer2[smallChars2]=' ';
|
||||
smallBuffer2[smallChars2] = 0;
|
||||
drawString(1, row*lineHeight+1, smallBuffer2, true);
|
||||
lastRedraw = millis();
|
||||
}
|
||||
}
|
||||
else{ // use this code for 4 ling displays
|
||||
if (printedChars > MAX_MODE_LINE_SPACE) printedChars = MAX_MODE_LINE_SPACE;
|
||||
for (uint8_t i = 0; i < printedChars; i++){
|
||||
smallBuffer3[smallChars3++] = lineBuffer[i];
|
||||
}
|
||||
|
||||
for (; smallChars3 < (MAX_MODE_LINE_SPACE); smallChars3++) smallBuffer3[smallChars3]=' ';
|
||||
smallBuffer3[smallChars3] = 0;
|
||||
drawString(1, row*lineHeight, smallBuffer3, true);
|
||||
lastRedraw = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 one line and a glyph as overlay for a
|
||||
* period of time.
|
||||
* Clears the screen and prints.
|
||||
*/
|
||||
void overlay(const char* line1, long showHowLong, byte glyphType) {
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
}
|
||||
|
||||
// Print the overlay
|
||||
clear();
|
||||
if (glyphType > 0){
|
||||
if ( lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_font_benji_custom_icons_6x6, true);
|
||||
else drawGlyph(7, lineHeight, glyphType, u8x8_font_benji_custom_icons_2x2, true);
|
||||
}
|
||||
if (line1) drawString(0, 3*lineHeight, line1);
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
void networkOverlay(const char* line1, long showHowLong) {
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
}
|
||||
// Print the overlay
|
||||
clear();
|
||||
// First row string
|
||||
if (line1) drawString(0, 0, line1);
|
||||
// Second row with Wifi name
|
||||
String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0); //
|
||||
drawString(0, lineHeight, ssidString.c_str());
|
||||
// Print `~` char to indicate that SSID is longer, than our display
|
||||
if (knownSsid.length() > getCols()) {
|
||||
drawString(getCols() - 1, 0, "~");
|
||||
}
|
||||
// Third row with IP and Psssword in AP Mode
|
||||
drawString(0, lineHeight*2, (knownIp.toString()).c_str());
|
||||
if (apActive) {
|
||||
String appassword = apPass;
|
||||
drawString(0, lineHeight*3, appassword.c_str());
|
||||
}
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable sleep (turn the display off) or clock mode.
|
||||
*/
|
||||
void sleepOrClock(bool enabled) {
|
||||
if (enabled) {
|
||||
if (clockMode) {
|
||||
clear();
|
||||
knownMinute = 99;
|
||||
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() {
|
||||
if(knownMinute != minute(localTime)){ //only redraw clock if it has changed
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
|
||||
//updateLocalTime();
|
||||
byte AmPmHour = hour(localTime);
|
||||
boolean isitAM = true;
|
||||
if (useAMPM) {
|
||||
if (AmPmHour > 11) AmPmHour -= 12;
|
||||
if (AmPmHour == 0) AmPmHour = 12;
|
||||
if (hour(localTime) > 11) isitAM = false;
|
||||
}
|
||||
clear();
|
||||
drawStatusIcons(); //icons power, wifi, timer, etc
|
||||
|
||||
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
|
||||
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
|
||||
|
||||
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hour(localTime)), minute(localTime));
|
||||
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
|
||||
|
||||
if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time
|
||||
knownMinute = minute(localTime);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 io_pin = top.createNestedArray("pin");
|
||||
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||
top["help4PinTypes"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["type"] = type;
|
||||
top[FPSTR(_flip)] = (bool) flip;
|
||||
top[FPSTR(_contrast)] = contrast;
|
||||
top[FPSTR(_refreshRate)] = refreshRate/10;
|
||||
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
|
||||
top[FPSTR(_sleepMode)] = (bool) sleepMode;
|
||||
top[FPSTR(_clockMode)] = (bool) clockMode;
|
||||
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
|
||||
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 newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
newType = top["type"] | newType;
|
||||
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
|
||||
flip = top[FPSTR(_flip)] | flip;
|
||||
contrast = top[FPSTR(_contrast)] | contrast;
|
||||
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/10) * 10;
|
||||
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
|
||||
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
|
||||
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
type = newType;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
bool pinsChanged = false;
|
||||
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
|
||||
if (pinsChanged || type!=newType) {
|
||||
if (type != NONE) delete u8x8;
|
||||
for (byte i=0; i<5; i++) {
|
||||
if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||
ioPin[i] = newPin[i];
|
||||
}
|
||||
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
|
||||
type = NONE;
|
||||
return true;
|
||||
} else type = newType;
|
||||
setup();
|
||||
needsRedraw |= true;
|
||||
}
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
setContrast(contrast);
|
||||
setFlipMode(flip);
|
||||
if (needsRedraw && !wakeDisplay()) redraw(true);
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !(top[_busClkFrequency]).isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = "refreshRate0.01Sec";
|
||||
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
|
||||
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
|
||||
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
|
||||
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
|
||||
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
|
||||
@@ -39,10 +39,11 @@
|
||||
|
||||
#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
|
||||
#define FLD_LINE_BRIGHTNESS 0
|
||||
#define FLD_LINE_MODE 0
|
||||
#define FLD_LINE_EFFECT_SPEED 0
|
||||
#define FLD_LINE_EFFECT_INTENSITY 0
|
||||
#define FLD_LINE_PALETTE 0
|
||||
#endif
|
||||
|
||||
|
||||
@@ -55,10 +56,10 @@ 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
|
||||
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
|
||||
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
|
||||
int8_t 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;
|
||||
|
||||
@@ -75,10 +76,20 @@ private:
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
bool currentEffectAndPaleeteInitialized = false;
|
||||
bool currentEffectAndPaletteInitialized = false;
|
||||
uint8_t effectCurrentIndex = 0;
|
||||
uint8_t effectPaletteIndex = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _DT_pin[];
|
||||
static const char _CLK_pin[];
|
||||
static const char _SW_pin[];
|
||||
|
||||
public:
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
@@ -86,6 +97,18 @@ public:
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
// BUG: configuring this usermod with conflicting pins
|
||||
// will cause it to de-allocate pins it does not own
|
||||
// (at second config)
|
||||
// This is the exact type of bug solved by pinManager
|
||||
// tracking the owner tags....
|
||||
pinA = pinB = pinC = -1;
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
pinMode(pinA, INPUT_PULLUP);
|
||||
pinMode(pinB, INPUT_PULLUP);
|
||||
pinMode(pinC, INPUT_PULLUP);
|
||||
@@ -101,10 +124,12 @@ public:
|
||||
// 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->setLineType(FLD_LINE_BRIGHTNESS);
|
||||
display->setMarkLine(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -128,12 +153,14 @@ public:
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
if (!enabled) return;
|
||||
|
||||
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) {
|
||||
if (!currentEffectAndPaletteInitialized) {
|
||||
findCurrentEffectAndPalette();
|
||||
}
|
||||
|
||||
@@ -153,19 +180,19 @@ public:
|
||||
if (display != nullptr) {
|
||||
switch(newState) {
|
||||
case 0:
|
||||
changedState = changeState("Brightness", FLD_LINE_3_BRIGHTNESS, 3);
|
||||
changedState = changeState("Brightness", FLD_LINE_BRIGHTNESS, 3);
|
||||
break;
|
||||
case 1:
|
||||
changedState = changeState("Select FX", FLD_LINE_3_EFFECT_SPEED, 2);
|
||||
changedState = changeState("Select FX", FLD_LINE_MODE, 2);
|
||||
break;
|
||||
case 2:
|
||||
changedState = changeState("FX Speed", FLD_LINE_3_EFFECT_SPEED, 3);
|
||||
changedState = changeState("FX Speed", FLD_LINE_EFFECT_SPEED, 3);
|
||||
break;
|
||||
case 3:
|
||||
changedState = changeState("FX Intensity", FLD_LINE_3_EFFECT_INTENSITY, 3);
|
||||
changedState = changeState("FX Intensity", FLD_LINE_EFFECT_INTENSITY, 3);
|
||||
break;
|
||||
case 4:
|
||||
changedState = changeState("Palette", FLD_LINE_3_PALETTE, 3);
|
||||
changedState = changeState("Palette", FLD_LINE_PALETTE, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -229,9 +256,9 @@ public:
|
||||
}
|
||||
|
||||
void findCurrentEffectAndPalette() {
|
||||
currentEffectAndPaleeteInitialized = true;
|
||||
currentEffectAndPaletteInitialized = true;
|
||||
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||
byte value = modes_alpha_indexes[i];
|
||||
//byte value = modes_alpha_indexes[i];
|
||||
if (modes_alpha_indexes[i] == effectCurrent) {
|
||||
effectCurrentIndex = i;
|
||||
break;
|
||||
@@ -239,7 +266,7 @@ public:
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||
byte value = palettes_alpha_indexes[i];
|
||||
//byte value = palettes_alpha_indexes[i];
|
||||
if (palettes_alpha_indexes[i] == strip.getSegment(0).palette) {
|
||||
effectPaletteIndex = i;
|
||||
break;
|
||||
@@ -255,7 +282,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
display->overlay("Mode change", stateName, 1500);
|
||||
display->setLineThreeType(lineThreeMode);
|
||||
display->setLineType(lineThreeMode);
|
||||
display->setMarkLine(markedLine);
|
||||
}
|
||||
#endif
|
||||
@@ -267,8 +294,8 @@ public:
|
||||
|
||||
//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);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
updateInterfaces(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
@@ -386,10 +413,73 @@ public:
|
||||
*/
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
|
||||
//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!"));
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_DT_pin)] = pinA;
|
||||
top[FPSTR(_CLK_pin)] = pinB;
|
||||
top[FPSTR(_SW_pin)] = pinC;
|
||||
DEBUG_PRINTLN(F("Rotary Encoder 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: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
int8_t newDTpin = pinA;
|
||||
int8_t newCLKpin = pinB;
|
||||
int8_t newSWpin = pinC;
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newDTpin = top[FPSTR(_DT_pin)] | newDTpin;
|
||||
newCLKpin = top[FPSTR(_CLK_pin)] | newCLKpin;
|
||||
newSWpin = top[FPSTR(_SW_pin)] | newSWpin;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
|
||||
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
if (pinA<0 || pinB<0 || pinC<0) {
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
setup();
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -399,3 +489,10 @@ public:
|
||||
return USERMOD_ID_ROTARY_ENC_UI;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
|
||||
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
|
||||
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
|
||||
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
|
||||
|
||||
45
usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
Normal file
45
usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Rotary Encoder UI Usermod ALT
|
||||
|
||||
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
|
||||
"usermod_v2_four_line_display"
|
||||
"usermod_v2_rotary_encoder_ui"
|
||||
|
||||
The core of these usermods are a copy of the originals. The main changes are done to the FourLineDisplay usermod.
|
||||
The display usermod UI has been completely changed.
|
||||
|
||||
|
||||
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
|
||||
Without the display it functions identical to the original.
|
||||
The original "usermod_v2_auto_save" will not work with the display just yet.
|
||||
|
||||
Press the encoder to cycle through the options:
|
||||
*Brightness
|
||||
*Speed
|
||||
*Intensity
|
||||
*Palette
|
||||
*Effect
|
||||
*Main Color (only if display is used)
|
||||
*Saturation (only if display is used)
|
||||
|
||||
Press and hold the encoder to display Network Info
|
||||
if AP is active then it will display AP ssid and Password
|
||||
|
||||
Also shows if the timer is enabled
|
||||
|
||||
[See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI)
|
||||
|
||||
## Installation
|
||||
|
||||
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions
|
||||
Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
|
||||
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
|
||||
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-10
|
||||
* First public release
|
||||
@@ -0,0 +1,569 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//
|
||||
// Inspired by the original v2 usermods
|
||||
// * usermod_v2_rotaty_encoder_ui
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// If FourLineDisplayUsermod is used the folowing options are also inabled
|
||||
//
|
||||
// * main color
|
||||
// * saturation of main color
|
||||
// * display network (long press buttion)
|
||||
//
|
||||
|
||||
#ifndef ENCODER_DT_PIN
|
||||
#define ENCODER_DT_PIN 18
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_CLK_PIN
|
||||
#define ENCODER_CLK_PIN 5
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_SW_PIN
|
||||
#define ENCODER_SW_PIN 19
|
||||
#endif
|
||||
|
||||
// The last UI state, remove color and saturation option if diplay not active(too many options)
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
#define LAST_UI_STATE 6
|
||||
#else
|
||||
#define LAST_UI_STATE 4
|
||||
#endif
|
||||
|
||||
|
||||
class RotaryEncoderUIUsermod : public Usermod {
|
||||
private:
|
||||
int fadeAmount = 5; // Amount to change every step (brightness)
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
unsigned long buttonHoldTIme;
|
||||
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
|
||||
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
|
||||
int8_t 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;
|
||||
bool networkShown = false;
|
||||
uint16_t currentHue1 = 6425; // default reboot color
|
||||
byte currentSat1 = 255;
|
||||
|
||||
#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 currentEffectAndPaletteInitialized = false;
|
||||
uint8_t effectCurrentIndex = 0;
|
||||
uint8_t effectPaletteIndex = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _DT_pin[];
|
||||
static const char _CLK_pin[];
|
||||
static const char _SW_pin[];
|
||||
|
||||
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()
|
||||
{
|
||||
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
// BUG: configuring this usermod with conflicting pins
|
||||
// will cause it to de-allocate pins it does not own
|
||||
// (at second config)
|
||||
// This is the exact type of bug solved by pinManager
|
||||
// tracking the owner tags....
|
||||
pinA = pinB = pinC = -1;
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
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->setMarkLine(1, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
initDone = true;
|
||||
Enc_A = digitalRead(pinA); // Read encoder pins
|
||||
Enc_B = digitalRead(pinB);
|
||||
Enc_A_prev = Enc_A;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (!currentEffectAndPaletteInitialized) {
|
||||
findCurrentEffectAndPalette();}
|
||||
|
||||
if(modes_alpha_indexes[effectCurrentIndex] != effectCurrent
|
||||
|| palettes_alpha_indexes[effectPaletteIndex] != effectPalette){
|
||||
currentEffectAndPaletteInitialized = false;
|
||||
}
|
||||
|
||||
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||
{
|
||||
button_state = digitalRead(pinC);
|
||||
if (prev_button_state != button_state)
|
||||
{
|
||||
if (button_state == HIGH && (millis()-buttonHoldTIme < 3000))
|
||||
{
|
||||
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", 1, 0, 1);
|
||||
break;
|
||||
case 1:
|
||||
changedState = changeState(" Speed", 1, 4, 2);
|
||||
break;
|
||||
case 2:
|
||||
changedState = changeState(" Intensity", 1 ,8, 3);
|
||||
break;
|
||||
case 3:
|
||||
changedState = changeState(" Color Palette", 2, 0, 4);
|
||||
break;
|
||||
case 4:
|
||||
changedState = changeState(" Effect", 3, 0, 5);
|
||||
break;
|
||||
case 5:
|
||||
changedState = changeState(" Main Color", 255, 255, 7);
|
||||
break;
|
||||
case 6:
|
||||
changedState = changeState(" Saturation", 255, 255, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changedState) {
|
||||
select_state = newState;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
networkShown = false;
|
||||
if(!prev_button_state)buttonHoldTIme = millis();
|
||||
}
|
||||
}
|
||||
|
||||
if (!prev_button_state && (millis()-buttonHoldTIme > 3000) && !networkShown) displayNetworkInfo(); //long press for network info
|
||||
|
||||
Enc_A = digitalRead(pinA); // Read encoder pins
|
||||
Enc_B = digitalRead(pinB);
|
||||
if ((Enc_A) && (!Enc_A_prev))
|
||||
{ // A has gone from high to low
|
||||
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
|
||||
{ // B is high so clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(true);
|
||||
break;
|
||||
case 1:
|
||||
changeEffectSpeed(true);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectIntensity(true);
|
||||
break;
|
||||
case 3:
|
||||
changePalette(true);
|
||||
break;
|
||||
case 4:
|
||||
changeEffect(true);
|
||||
break;
|
||||
case 5:
|
||||
changeHue(true);
|
||||
break;
|
||||
case 6:
|
||||
changeSat(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == HIGH)
|
||||
{ // B is low so counter-clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(false);
|
||||
break;
|
||||
case 1:
|
||||
changeEffectSpeed(false);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectIntensity(false);
|
||||
break;
|
||||
case 3:
|
||||
changePalette(false);
|
||||
break;
|
||||
case 4:
|
||||
changeEffect(false);
|
||||
break;
|
||||
case 5:
|
||||
changeHue(false);
|
||||
break;
|
||||
case 6:
|
||||
changeSat(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
}
|
||||
}
|
||||
|
||||
void displayNetworkInfo(){
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->networkOverlay(" NETWORK INFO", 15000);
|
||||
networkShown = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void findCurrentEffectAndPalette() {
|
||||
currentEffectAndPaletteInitialized = true;
|
||||
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||
if (modes_alpha_indexes[i] == effectCurrent) {
|
||||
effectCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||
if (palettes_alpha_indexes[i] == effectPalette) {
|
||||
effectPaletteIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return false;
|
||||
}
|
||||
display->overlay(stateName, 750, glyph);
|
||||
display->setMarkLine(markedLine, markedCol);
|
||||
}
|
||||
#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(CALL_MODE_DIRECT_CHANGE);
|
||||
updateInterfaces(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();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateBrightness();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateSpeed();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateIntensity();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void changeHue(bool increase){
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(increase) currentHue1 += 321;
|
||||
else currentHue1 -= 321;
|
||||
colorHStoRGB(currentHue1, currentSat1, col);
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
void changeSat(bool increase){
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(increase) currentSat1 = (currentSat1 + 5 <= 255 ? (currentSat1 + 5) : 255);
|
||||
else currentSat1 = (currentSat1 - 5 >= 0 ? (currentSat1 - 5) : 0);
|
||||
colorHStoRGB(currentHue1, currentSat1, col);
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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!"));
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_DT_pin)] = pinA;
|
||||
top[FPSTR(_CLK_pin)] = pinB;
|
||||
top[FPSTR(_SW_pin)] = pinC;
|
||||
DEBUG_PRINTLN(F("Rotary Encoder 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: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
int8_t newDTpin = pinA;
|
||||
int8_t newCLKpin = pinB;
|
||||
int8_t newSWpin = pinC;
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newDTpin = top[FPSTR(_DT_pin)] | newDTpin;
|
||||
newCLKpin = top[FPSTR(_CLK_pin)] | newCLKpin;
|
||||
newSWpin = top[FPSTR(_SW_pin)] | newSWpin;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
|
||||
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
if (pinA<0 || pinB<0 || pinC<0) {
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
setup();
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
|
||||
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
|
||||
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
|
||||
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
|
||||
@@ -64,13 +64,13 @@ void hourChime()
|
||||
{
|
||||
//strip.resetSegments();
|
||||
selectWordSegments(true);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
savePreset(13, false);
|
||||
selectWordSegments(false);
|
||||
//strip.getSegment(0).setOption(0, true);
|
||||
strip.getSegment(0).setOption(2, true);
|
||||
applyPreset(12);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
}
|
||||
|
||||
void displayTime(byte hour, byte minute)
|
||||
|
||||
432
wled00/FX.cpp
432
wled00/FX.cpp
@@ -388,41 +388,12 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* theater chase function
|
||||
*/
|
||||
uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool do_palette) {
|
||||
byte gap = 2 + ((255 - SEGMENT.intensity) >> 5);
|
||||
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
|
||||
uint32_t it = now / cycleTime;
|
||||
if (it != SEGENV.step) //new color
|
||||
{
|
||||
SEGENV.aux0 = (SEGENV.aux0 +1) % gap;
|
||||
SEGENV.step = it;
|
||||
}
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
if((i % gap) == SEGENV.aux0) {
|
||||
if (do_palette)
|
||||
{
|
||||
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
} else {
|
||||
setPixelColor(i, color1);
|
||||
}
|
||||
} else {
|
||||
setPixelColor(i, color2);
|
||||
}
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Theatre-style crawling lights.
|
||||
* Inspired by the Adafruit examples.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_theater_chase(void) {
|
||||
return theater_chase(SEGCOLOR(0), SEGCOLOR(1), true);
|
||||
return running(SEGCOLOR(0), SEGCOLOR(1), true);
|
||||
}
|
||||
|
||||
|
||||
@@ -431,7 +402,7 @@ uint16_t WS2812FX::mode_theater_chase(void) {
|
||||
* Inspired by the Adafruit examples.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_theater_chase_rainbow(void) {
|
||||
return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false);
|
||||
return running(color_wheel(SEGENV.step), SEGCOLOR(1), true);
|
||||
}
|
||||
|
||||
|
||||
@@ -976,29 +947,27 @@ uint16_t WS2812FX::mode_chase_flash_random(void) {
|
||||
/*
|
||||
* Alternating pixels running function.
|
||||
*/
|
||||
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) {
|
||||
uint8_t pxw = 1 + (SEGMENT.intensity >> 5);
|
||||
uint32_t cycleTime = 35 + (255 - SEGMENT.speed);
|
||||
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
|
||||
uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
|
||||
uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
|
||||
uint32_t it = now / cycleTime;
|
||||
if (SEGMENT.speed == 0) it = 0;
|
||||
|
||||
bool usePalette = color1 == SEGCOLOR(0);
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
if((i + SEGENV.aux0) % (pxw*2) < pxw) {
|
||||
if (color1 == SEGCOLOR(0))
|
||||
{
|
||||
setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0));
|
||||
} else
|
||||
{
|
||||
setPixelColor(SEGLEN -i -1, color1);
|
||||
}
|
||||
uint32_t col = color2;
|
||||
if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
|
||||
if (theatre) {
|
||||
if ((i % width) == SEGENV.aux0) col = color1;
|
||||
} else {
|
||||
setPixelColor(SEGLEN -i -1, color2);
|
||||
int8_t pos = (i % (width<<1));
|
||||
if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1;
|
||||
}
|
||||
setPixelColor(i,col);
|
||||
}
|
||||
|
||||
if (it != SEGENV.step )
|
||||
{
|
||||
SEGENV.aux0 = (SEGENV.aux0 +1) % (pxw*2);
|
||||
SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
|
||||
SEGENV.step = it;
|
||||
}
|
||||
return FRAMETIME;
|
||||
@@ -1181,10 +1150,10 @@ uint16_t WS2812FX::mode_fire_flicker(void) {
|
||||
uint32_t it = now / cycleTime;
|
||||
if (SEGENV.step == it) return FRAMETIME;
|
||||
|
||||
byte w = (SEGCOLOR(0) >> 24) & 0xFF;
|
||||
byte r = (SEGCOLOR(0) >> 16) & 0xFF;
|
||||
byte g = (SEGCOLOR(0) >> 8) & 0xFF;
|
||||
byte b = (SEGCOLOR(0) & 0xFF);
|
||||
byte w = (SEGCOLOR(0) >> 24);
|
||||
byte r = (SEGCOLOR(0) >> 16);
|
||||
byte g = (SEGCOLOR(0) >> 8);
|
||||
byte b = (SEGCOLOR(0) );
|
||||
byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255;
|
||||
lum /= (((256-SEGMENT.intensity)/16)+1);
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
@@ -1247,68 +1216,29 @@ uint16_t WS2812FX::mode_loading(void) {
|
||||
|
||||
|
||||
//American Police Light with all LEDs Red and Blue
|
||||
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
|
||||
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2)
|
||||
{
|
||||
uint16_t counter = now * ((SEGMENT.speed >> 2) +1);
|
||||
uint16_t idexR = (counter * SEGLEN) >> 16;
|
||||
if (idexR >= SEGLEN) idexR = 0;
|
||||
|
||||
uint16_t topindex = SEGLEN >> 1;
|
||||
uint16_t idexB = (idexR > topindex) ? idexR - topindex : idexR + topindex;
|
||||
if (SEGENV.call == 0) SEGENV.aux0 = idexR;
|
||||
if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs
|
||||
|
||||
if (all) { //different algo, ensuring immediate fill
|
||||
if (idexB > idexR) {
|
||||
fill(color2);
|
||||
for (uint16_t i = idexR; i < idexB; i++) setPixelColor(i, color1);
|
||||
} else {
|
||||
fill(color1);
|
||||
for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2);
|
||||
}
|
||||
} else { //regular dot-only mode
|
||||
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);
|
||||
setPixelColor(idexB+i, color2);
|
||||
}
|
||||
if (SEGENV.aux0 != idexR) {
|
||||
uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR;
|
||||
for (uint8_t i = 0; i <= gap ; i++) {
|
||||
if ((idexR - i) < 0) idexR = SEGLEN-1 + i;
|
||||
if ((idexB - i) < 0) idexB = SEGLEN-1 + i;
|
||||
setPixelColor(idexR-i, color1);
|
||||
setPixelColor(idexB-i, color2);
|
||||
}
|
||||
SEGENV.aux0 = idexR;
|
||||
}
|
||||
}
|
||||
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
|
||||
uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
|
||||
uint16_t offset = it % SEGLEN;
|
||||
|
||||
uint16_t width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip
|
||||
if (!width) width = 1;
|
||||
for (uint16_t i = 0; i < width; i++) {
|
||||
uint16_t indexR = (offset + i) % SEGLEN;
|
||||
uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN;
|
||||
setPixelColor(indexR, color1);
|
||||
setPixelColor(indexB, color2);
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
//American Police Light with all LEDs Red and Blue
|
||||
uint16_t WS2812FX::mode_police_all()
|
||||
{
|
||||
return police_base(RED, BLUE, true);
|
||||
}
|
||||
|
||||
|
||||
//Police Lights Red and Blue
|
||||
uint16_t WS2812FX::mode_police()
|
||||
{
|
||||
fill(SEGCOLOR(1));
|
||||
|
||||
return police_base(RED, BLUE, false);
|
||||
}
|
||||
|
||||
|
||||
//Police All with custom colors
|
||||
uint16_t WS2812FX::mode_two_areas()
|
||||
{
|
||||
return police_base(SEGCOLOR(0), SEGCOLOR(1), true);
|
||||
return police_base(RED, BLUE);
|
||||
}
|
||||
|
||||
|
||||
@@ -1318,7 +1248,142 @@ uint16_t WS2812FX::mode_two_dots()
|
||||
fill(SEGCOLOR(2));
|
||||
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
|
||||
|
||||
return police_base(SEGCOLOR(0), color2, false);
|
||||
return police_base(SEGCOLOR(0), color2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fairy, inspired by https://www.youtube.com/watch?v=zeOw5MZWq24
|
||||
*/
|
||||
//4 bytes
|
||||
typedef struct Flasher {
|
||||
uint16_t stateStart;
|
||||
uint8_t stateDur;
|
||||
bool stateOn;
|
||||
} flasher;
|
||||
|
||||
#define FLASHERS_PER_ZONE 6
|
||||
#define MAX_SHIMMER 92
|
||||
|
||||
uint16_t WS2812FX::mode_fairy() {
|
||||
//set every pixel to a 'random' color from palette (using seed so it doesn't change between frames)
|
||||
uint16_t PRNG16 = 5100 + _segment_index;
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
|
||||
setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0));
|
||||
}
|
||||
|
||||
//amount of flasher pixels depending on intensity (0: none, 255: every LED)
|
||||
if (SEGMENT.intensity == 0) return FRAMETIME;
|
||||
uint8_t flasherDistance = ((255 - SEGMENT.intensity) / 28) +1; //1-10
|
||||
uint16_t numFlashers = (SEGLEN / flasherDistance) +1;
|
||||
|
||||
uint16_t dataSize = sizeof(flasher) * numFlashers;
|
||||
if (!SEGENV.allocateData(dataSize)) return FRAMETIME; //allocation failed
|
||||
Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
|
||||
uint16_t now16 = now & 0xFFFF;
|
||||
|
||||
//Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers
|
||||
uint16_t zones = numFlashers/FLASHERS_PER_ZONE;
|
||||
if (!zones) zones = 1;
|
||||
uint8_t flashersInZone = numFlashers/zones;
|
||||
uint8_t flasherBri[FLASHERS_PER_ZONE*2 -1];
|
||||
|
||||
for (uint16_t z = 0; z < zones; z++) {
|
||||
uint16_t flasherBriSum = 0;
|
||||
uint16_t firstFlasher = z*flashersInZone;
|
||||
if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1));
|
||||
|
||||
for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
|
||||
uint16_t stateTime = now16 - flashers[f].stateStart;
|
||||
//random on/off time reached, switch state
|
||||
if (stateTime > flashers[f].stateDur * 10) {
|
||||
flashers[f].stateOn = !flashers[f].stateOn;
|
||||
if (flashers[f].stateOn) {
|
||||
flashers[f].stateDur = 12 + random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
|
||||
} else {
|
||||
flashers[f].stateDur = 20 + random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
|
||||
}
|
||||
//flashers[f].stateDur = 51 + random8(2 + ((255 - SEGMENT.speed) >> 1));
|
||||
flashers[f].stateStart = now16;
|
||||
if (stateTime < 255) {
|
||||
flashers[f].stateStart -= 255 -stateTime; //start early to get correct bri
|
||||
flashers[f].stateDur += 26 - stateTime/10;
|
||||
stateTime = 255 - stateTime;
|
||||
} else {
|
||||
stateTime = 0;
|
||||
}
|
||||
}
|
||||
if (stateTime > 255) stateTime = 255; //for flasher brightness calculation, fades in first 255 ms of state
|
||||
//flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-gamma8((510 - stateTime) >> 1) : gamma8((510 - stateTime) >> 1);
|
||||
flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? stateTime : 255 - (stateTime >> 0);
|
||||
flasherBriSum += flasherBri[f - firstFlasher];
|
||||
}
|
||||
//dim factor, to create "shimmer" as other pixels get less voltage if a lot of flashers are on
|
||||
uint8_t avgFlasherBri = flasherBriSum / flashersInZone;
|
||||
uint8_t globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers
|
||||
|
||||
for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
|
||||
uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255;
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
|
||||
uint16_t flasherPos = f*flasherDistance;
|
||||
setPixelColor(flasherPos, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), bri));
|
||||
for (uint16_t i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) {
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
|
||||
setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri));
|
||||
}
|
||||
}
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on getPixelColor
|
||||
* Warning: Uses 4 bytes of segment data per pixel
|
||||
*/
|
||||
uint16_t WS2812FX::mode_fairytwinkle() {
|
||||
uint16_t dataSize = sizeof(flasher) * SEGLEN;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
|
||||
uint16_t now16 = now & 0xFFFF;
|
||||
uint16_t PRNG16 = 5100 + _segment_index;
|
||||
|
||||
uint16_t riseFallTime = 400 + (255-SEGMENT.speed)*3;
|
||||
uint16_t maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1);
|
||||
|
||||
for (uint16_t f = 0; f < SEGLEN; f++) {
|
||||
uint16_t stateTime = now16 - flashers[f].stateStart;
|
||||
//random on/off time reached, switch state
|
||||
if (stateTime > flashers[f].stateDur * 100) {
|
||||
flashers[f].stateOn = !flashers[f].stateOn;
|
||||
bool init = !flashers[f].stateDur;
|
||||
if (flashers[f].stateOn) {
|
||||
flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1;
|
||||
} else {
|
||||
flashers[f].stateDur = riseFallTime/100 + random8(3 + ((255 - SEGMENT.speed) >> 6)) +1;
|
||||
}
|
||||
flashers[f].stateStart = now16;
|
||||
stateTime = 0;
|
||||
if (init) {
|
||||
flashers[f].stateStart -= riseFallTime; //start lit
|
||||
flashers[f].stateDur = riseFallTime/100 + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker
|
||||
stateTime = riseFallTime;
|
||||
}
|
||||
}
|
||||
if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change
|
||||
if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state
|
||||
uint8_t fadeprog = 255 - ((stateTime * 255) / riseFallTime);
|
||||
uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog);
|
||||
uint16_t lastR = PRNG16;
|
||||
uint16_t diff = 0;
|
||||
while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
|
||||
diff = (PRNG16 > lastR) ? PRNG16 - lastR : lastR - PRNG16;
|
||||
}
|
||||
setPixelColor(f, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri));
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
@@ -1326,21 +1391,20 @@ uint16_t WS2812FX::mode_two_dots()
|
||||
* Tricolor chase function
|
||||
*/
|
||||
uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
|
||||
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
|
||||
uint32_t it = now / cycleTime;
|
||||
uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour
|
||||
uint8_t index = it % width;
|
||||
uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
|
||||
uint32_t it = now / cycleTime; // iterator
|
||||
uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
|
||||
uint8_t index = it % (width*3);
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++, index++) {
|
||||
if(index > width-1) index = 0;
|
||||
for (uint16_t i = 0; i < SEGLEN; i++, index++) {
|
||||
if (index > (width*3)-1) index = 0;
|
||||
|
||||
uint32_t color = color1;
|
||||
if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
|
||||
else if(index > width/3-1) color = color2;
|
||||
if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
|
||||
else if (index > width-1) color = color2;
|
||||
|
||||
setPixelColor(SEGLEN - i -1, color);
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
@@ -1548,7 +1612,7 @@ uint16_t WS2812FX::mode_random_chase(void)
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
//7 bytes
|
||||
typedef struct Oscillator {
|
||||
int16_t pos;
|
||||
int8_t size;
|
||||
@@ -1780,7 +1844,7 @@ uint16_t WS2812FX::mode_fire_2012()
|
||||
// Step 1. Cool down every cell a little
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
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
|
||||
heat[i] = (temp==0 && i<ignition) ? 16 : temp; // prevent ignition area from becoming black
|
||||
}
|
||||
|
||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||
@@ -1994,7 +2058,7 @@ uint16_t WS2812FX::mode_colortwinkle()
|
||||
|
||||
if (fadeUp) {
|
||||
CRGB incrementalColor = fastled_col;
|
||||
incrementalColor.nscale8_video( fadeUpAmount);
|
||||
incrementalColor.nscale8_video(fadeUpAmount);
|
||||
fastled_col += incrementalColor;
|
||||
|
||||
if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) {
|
||||
@@ -2002,24 +2066,21 @@ uint16_t WS2812FX::mode_colortwinkle()
|
||||
}
|
||||
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
|
||||
|
||||
if (col_to_crgb(getPixelColor(i)) == prev) //fix "stuck" pixels
|
||||
{
|
||||
if (col_to_crgb(getPixelColor(i)) == prev) { //fix "stuck" pixels
|
||||
fastled_col += fastled_col;
|
||||
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
|
||||
}
|
||||
} else {
|
||||
fastled_col.nscale8( 255 - fadeDownAmount);
|
||||
fastled_col.nscale8(255 - fadeDownAmount);
|
||||
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t j = 0; j <= SEGLEN / 50; j++)
|
||||
{
|
||||
for (uint16_t j = 0; j <= SEGLEN / 50; j++) {
|
||||
if (random8() <= SEGMENT.intensity) {
|
||||
for (uint8_t times = 0; times < 5; times++) //attempt to spawn a new pixel 5 times
|
||||
{
|
||||
for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
|
||||
int i = random16(SEGLEN);
|
||||
if(getPixelColor(i) == 0) {
|
||||
if (getPixelColor(i) == 0) {
|
||||
fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND);
|
||||
uint16_t index = i >> 3;
|
||||
uint8_t bitNum = i & 0x07;
|
||||
@@ -2170,10 +2231,14 @@ typedef struct Ripple {
|
||||
uint16_t pos;
|
||||
} ripple;
|
||||
|
||||
#ifdef ESP8266
|
||||
#define MAX_RIPPLES 56
|
||||
#else
|
||||
#define MAX_RIPPLES 100
|
||||
#endif
|
||||
uint16_t WS2812FX::ripple_base(bool rainbow)
|
||||
{
|
||||
uint16_t maxRipples = 1 + (SEGLEN >> 2);
|
||||
if (maxRipples > 100) maxRipples = 100;
|
||||
uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
|
||||
uint16_t dataSize = sizeof(ripple) * maxRipples;
|
||||
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
@@ -2241,6 +2306,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow)
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
#undef MAX_RIPPLES
|
||||
|
||||
uint16_t WS2812FX::mode_ripple(void) {
|
||||
return ripple_base(false);
|
||||
@@ -2541,7 +2607,6 @@ uint16_t WS2812FX::mode_spots_fade()
|
||||
|
||||
|
||||
//each needs 12 bytes
|
||||
//Spark type is used for popcorn and 1D fireworks
|
||||
typedef struct Ball {
|
||||
unsigned long lastBounceTime;
|
||||
float impactVelocity;
|
||||
@@ -2651,7 +2716,7 @@ uint16_t WS2812FX::mode_sinelon_dual(void) {
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::mode_sinelon_rainbow(void) {
|
||||
return sinelon_base(true, true);
|
||||
return sinelon_base(false, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -2685,7 +2750,7 @@ typedef struct Spark {
|
||||
*/
|
||||
uint16_t WS2812FX::mode_popcorn(void) {
|
||||
//allocate segment data
|
||||
uint16_t maxNumPopcorn = 24;
|
||||
uint16_t maxNumPopcorn = 21; // max 21 on 16 segment ESP8266
|
||||
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
@@ -2744,7 +2809,7 @@ uint16_t WS2812FX::candle(bool multi)
|
||||
if (multi)
|
||||
{
|
||||
//allocate segment data
|
||||
uint16_t dataSize = (SEGLEN -1) *3;
|
||||
uint16_t dataSize = (SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
|
||||
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
|
||||
}
|
||||
|
||||
@@ -2831,9 +2896,12 @@ uint16_t WS2812FX::mode_candle_multi()
|
||||
/ based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
|
||||
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst
|
||||
*/
|
||||
#define STARBURST_MAX_FRAG 12
|
||||
|
||||
//each needs 64 byte
|
||||
#ifdef ESP8266
|
||||
#define STARBURST_MAX_FRAG 8 //52 bytes / star
|
||||
#else
|
||||
#define STARBURST_MAX_FRAG 10 //60 bytes / star
|
||||
#endif
|
||||
//each needs 20+STARBURST_MAX_FRAG*4 bytes
|
||||
typedef struct particle {
|
||||
CRGB color;
|
||||
uint32_t birth =0;
|
||||
@@ -2844,8 +2912,14 @@ typedef struct particle {
|
||||
} star;
|
||||
|
||||
uint16_t WS2812FX::mode_starburst(void) {
|
||||
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
|
||||
uint8_t segs = getActiveSegmentsNum();
|
||||
if (segs <= (MAX_NUM_SEGMENTS /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
|
||||
if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
|
||||
uint16_t maxStars = maxData / sizeof(star); //ESP8266: max. 4/9/19 stars/seg, ESP32: max. 10/21/42 stars/seg
|
||||
|
||||
uint8_t numStars = 1 + (SEGLEN >> 3);
|
||||
if (numStars > 15) numStars = 15;
|
||||
if (numStars > maxStars) numStars = maxStars;
|
||||
uint16_t dataSize = sizeof(star) * numStars;
|
||||
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
@@ -2946,21 +3020,30 @@ uint16_t WS2812FX::mode_starburst(void) {
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
#undef STARBURST_MAX_FRAG
|
||||
|
||||
/*
|
||||
* Exploding fireworks effect
|
||||
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
|
||||
*/
|
||||
|
||||
uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||
{
|
||||
//allocate segment data
|
||||
uint16_t numSparks = 2 + (SEGLEN >> 1);
|
||||
if (numSparks > 80) numSparks = 80;
|
||||
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
|
||||
uint8_t segs = getActiveSegmentsNum();
|
||||
if (segs <= (MAX_NUM_SEGMENTS /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
|
||||
if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
|
||||
int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg
|
||||
|
||||
uint16_t numSparks = min(2 + (SEGLEN >> 1), maxSparks);
|
||||
uint16_t dataSize = sizeof(spark) * numSparks;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated
|
||||
SEGENV.aux0 = 0;
|
||||
SEGENV.aux1 = dataSize;
|
||||
}
|
||||
|
||||
fill(BLACK);
|
||||
|
||||
bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED);
|
||||
@@ -3052,7 +3135,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||
SEGENV.aux0--;
|
||||
if (SEGENV.aux0 < 4) {
|
||||
SEGENV.aux0 = 0; //back to flare
|
||||
SEGENV.step = (SEGMENT.intensity > random8()); //decide firing side
|
||||
SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); //decide firing side
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3060,6 +3143,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
#undef MAX_SPARKS
|
||||
|
||||
|
||||
/*
|
||||
@@ -3069,7 +3153,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||
uint16_t WS2812FX::mode_drip(void)
|
||||
{
|
||||
//allocate segment data
|
||||
uint16_t numDrops = 4;
|
||||
uint8_t numDrops = 4;
|
||||
uint16_t dataSize = sizeof(spark) * numDrops;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
@@ -3077,7 +3161,7 @@ uint16_t WS2812FX::mode_drip(void)
|
||||
|
||||
Spark* drops = reinterpret_cast<Spark*>(SEGENV.data);
|
||||
|
||||
numDrops = 1 + (SEGMENT.intensity >> 6);
|
||||
numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
|
||||
|
||||
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
|
||||
gravity *= SEGLEN;
|
||||
@@ -3107,13 +3191,13 @@ uint16_t WS2812FX::mode_drip(void)
|
||||
if (drops[j].pos > 0) { // fall until end of segment
|
||||
drops[j].pos += drops[j].vel;
|
||||
if (drops[j].pos < 0) drops[j].pos = 0;
|
||||
drops[j].vel += gravity;
|
||||
drops[j].vel += gravity; // gravity is negative
|
||||
|
||||
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
|
||||
setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col));
|
||||
}
|
||||
@@ -3142,6 +3226,7 @@ uint16_t WS2812FX::mode_drip(void)
|
||||
* Tetris or Stacking (falling bricks) Effect
|
||||
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
|
||||
*/
|
||||
//12 bytes
|
||||
typedef struct Tetris {
|
||||
float pos;
|
||||
float speed;
|
||||
@@ -3163,8 +3248,8 @@ uint16_t WS2812FX::mode_tetrix(void) {
|
||||
}
|
||||
|
||||
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->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed
|
||||
drop->pos = SEGLEN; // start at end of segment (no need to subtract 1)
|
||||
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
|
||||
@@ -3196,13 +3281,17 @@ uint16_t WS2812FX::mode_tetrix(void) {
|
||||
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
|
||||
*/
|
||||
uint16_t WS2812FX::mode_plasma(void) {
|
||||
uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves.
|
||||
uint8_t thatPhase = beatsin8(7,-64,64);
|
||||
// initialize phases on start
|
||||
if (SEGENV.call == 0) {
|
||||
SEGENV.aux0 = random8(0,2); // add a bit of randomness
|
||||
}
|
||||
uint8_t thisPhase = beatsin8(6+SEGENV.aux0,-64,64);
|
||||
uint8_t thatPhase = beatsin8(7+SEGENV.aux0,-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 thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 ));
|
||||
uint8_t colorIndex = cubicwave8((i*(2+ 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(7,0, (128 - (SEGMENT.intensity>>1))));
|
||||
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
|
||||
setPixelColor(i, color.red, color.green, color.blue);
|
||||
}
|
||||
@@ -3519,7 +3608,7 @@ uint16_t WS2812FX::mode_twinkleup(void) { // A very short twinkl
|
||||
uint8_t ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work.
|
||||
uint8_t pixBri = sin8(ranstart + 16 * now/(256-SEGMENT.speed));
|
||||
if (random8() > SEGMENT.intensity) pixBri = 0;
|
||||
setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i*20, false, PALETTE_SOLID_WRAP, 0), pixBri));
|
||||
setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(random8()+now/100, false, PALETTE_SOLID_WRAP, 0), pixBri));
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
@@ -3531,7 +3620,7 @@ uint16_t WS2812FX::mode_noisepal(void) { // S
|
||||
uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30
|
||||
//#define scale 30
|
||||
|
||||
uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes
|
||||
uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes)
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data);
|
||||
@@ -3644,7 +3733,7 @@ uint16_t WS2812FX::mode_chunchun(void)
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
//13 bytes
|
||||
typedef struct Spotlight {
|
||||
float speed;
|
||||
uint8_t colorIdx;
|
||||
@@ -3661,6 +3750,11 @@ typedef struct Spotlight {
|
||||
#define SPOT_TYPE_3X_DOT 4
|
||||
#define SPOT_TYPE_4X_DOT 5
|
||||
#define SPOT_TYPES_COUNT 6
|
||||
#ifdef ESP8266
|
||||
#define SPOT_MAX_COUNT 17 //Number of simultaneous waves
|
||||
#else
|
||||
#define SPOT_MAX_COUNT 49 //Number of simultaneous waves
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Spotlights moving back and forth that cast dancing shadows.
|
||||
@@ -3671,7 +3765,7 @@ typedef struct Spotlight {
|
||||
*/
|
||||
uint16_t WS2812FX::mode_dancing_shadows(void)
|
||||
{
|
||||
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, 50);
|
||||
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
|
||||
bool initialize = SEGENV.aux0 != numSpotlights;
|
||||
SEGENV.aux0 = numSpotlights;
|
||||
|
||||
@@ -3810,7 +3904,7 @@ uint16_t WS2812FX::mode_washing_machine(void) {
|
||||
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
|
||||
*/
|
||||
uint16_t WS2812FX::mode_blends(void) {
|
||||
uint16_t dataSize = sizeof(uint32_t) * SEGLEN;
|
||||
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
|
||||
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
|
||||
@@ -3825,6 +3919,11 @@ uint16_t WS2812FX::mode_blends(void) {
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
//43 bytes
|
||||
typedef struct TvSim {
|
||||
uint32_t totalTime = 0;
|
||||
uint32_t fadeTime = 0;
|
||||
@@ -3845,11 +3944,6 @@ typedef struct TvSim {
|
||||
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;
|
||||
@@ -3957,11 +4051,15 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
|
||||
*/
|
||||
|
||||
//CONFIG
|
||||
#define BACKLIGHT 5
|
||||
#define W_MAX_COUNT 20 //Number of simultaneous waves
|
||||
#ifdef ESP8266
|
||||
#define W_MAX_COUNT 9 //Number of simultaneous waves
|
||||
#else
|
||||
#define W_MAX_COUNT 20 //Number of simultaneous waves
|
||||
#endif
|
||||
#define W_MAX_SPEED 6 //Higher number, higher speed
|
||||
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
|
||||
|
||||
//24 bytes
|
||||
class AuroraWave {
|
||||
private:
|
||||
uint16_t ttl;
|
||||
@@ -4056,10 +4154,10 @@ uint16_t WS2812FX::mode_aurora(void) {
|
||||
|
||||
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
|
||||
//Intensity slider changed or first call
|
||||
SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT;
|
||||
SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
|
||||
SEGENV.aux0 = SEGMENT.intensity;
|
||||
|
||||
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
|
||||
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
|
||||
return mode_static(); //allocation failed
|
||||
}
|
||||
|
||||
@@ -4082,9 +4180,13 @@ uint16_t WS2812FX::mode_aurora(void) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t backlight = 1; //dimmer backlight if less active colors
|
||||
if (SEGCOLOR(0)) backlight++;
|
||||
if (SEGCOLOR(1)) backlight++;
|
||||
if (SEGCOLOR(2)) backlight++;
|
||||
//Loop through LEDs to determine color
|
||||
for(int i = 0; i < SEGLEN; i++) {
|
||||
CRGB mixedRgb = CRGB(BACKLIGHT, BACKLIGHT, BACKLIGHT);
|
||||
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.
|
||||
@@ -4096,7 +4198,7 @@ uint16_t WS2812FX::mode_aurora(void) {
|
||||
}
|
||||
}
|
||||
|
||||
setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2], BACKLIGHT);
|
||||
setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]);
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
|
||||
209
wled00/FX.h
209
wled00/FX.h
@@ -24,8 +24,6 @@
|
||||
Modified for WLED
|
||||
*/
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#ifndef WS2812FX_h
|
||||
#define WS2812FX_h
|
||||
|
||||
@@ -55,19 +53,23 @@
|
||||
/* 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 16
|
||||
/* 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
|
||||
#define MAX_SEGMENT_DATA 4096
|
||||
#else
|
||||
#ifndef MAX_NUM_SEGMENTS
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
#endif
|
||||
#define MAX_NUM_TRANSITIONS 16
|
||||
#define MAX_SEGMENT_DATA 8192
|
||||
#ifndef MAX_NUM_SEGMENTS
|
||||
#define MAX_NUM_SEGMENTS 32
|
||||
#endif
|
||||
#define MAX_NUM_TRANSITIONS 24
|
||||
#define MAX_SEGMENT_DATA 20480
|
||||
#endif
|
||||
|
||||
/* How much data bytes each segment should max allocate to leave enough space for other segments,
|
||||
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
|
||||
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
#define MIN_SHOW_DELAY 15
|
||||
|
||||
@@ -159,16 +161,16 @@
|
||||
#define FX_MODE_COMET 41
|
||||
#define FX_MODE_FIREWORKS 42
|
||||
#define FX_MODE_RAIN 43
|
||||
#define FX_MODE_TETRIX 44
|
||||
#define FX_MODE_TETRIX 44 //was Merry Christmas prior to 0.12.0 (use "Chase 2" with Red/Green)
|
||||
#define FX_MODE_FIRE_FLICKER 45
|
||||
#define FX_MODE_GRADIENT 46
|
||||
#define FX_MODE_LOADING 47
|
||||
#define FX_MODE_POLICE 48
|
||||
#define FX_MODE_POLICE_ALL 49
|
||||
#define FX_MODE_POLICE 48 // candidate for removal (after below three)
|
||||
#define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity)
|
||||
#define FX_MODE_TWO_DOTS 50
|
||||
#define FX_MODE_TWO_AREAS 51
|
||||
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
|
||||
#define FX_MODE_RUNNING_DUAL 52
|
||||
#define FX_MODE_HALLOWEEN 53
|
||||
#define FX_MODE_HALLOWEEN 53 // candidate for removal
|
||||
#define FX_MODE_TRICOLOR_CHASE 54
|
||||
#define FX_MODE_TRICOLOR_WIPE 55
|
||||
#define FX_MODE_TRICOLOR_FADE 56
|
||||
@@ -229,7 +231,7 @@
|
||||
#define FX_MODE_CHUNCHUN 111
|
||||
#define FX_MODE_DANCING_SHADOWS 112
|
||||
#define FX_MODE_WASHING_MACHINE 113
|
||||
#define FX_MODE_CANDY_CANE 114
|
||||
#define FX_MODE_CANDY_CANE 114 // candidate for removal
|
||||
#define FX_MODE_BLENDS 115
|
||||
#define FX_MODE_TV_SIMULATOR 116
|
||||
#define FX_MODE_DYNAMIC_SMOOTH 117
|
||||
@@ -245,70 +247,82 @@ class WS2812FX {
|
||||
|
||||
// segment parameters
|
||||
public:
|
||||
typedef struct Segment { // 25 (28 in memory?) bytes
|
||||
typedef struct Segment { // 30 (32 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;
|
||||
uint8_t mode;
|
||||
uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected
|
||||
uint8_t grouping, spacing;
|
||||
uint8_t opacity;
|
||||
uint8_t speed;
|
||||
uint8_t intensity;
|
||||
uint8_t palette;
|
||||
uint8_t mode;
|
||||
uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected
|
||||
uint8_t grouping, spacing;
|
||||
uint8_t opacity;
|
||||
uint32_t colors[NUM_COLORS];
|
||||
uint8_t cct; //0==1900K, 255==10091K
|
||||
char *name;
|
||||
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);
|
||||
uint8_t b = (slot == 1) ? cct : opacity;
|
||||
ColorTransition::startTransition(b, colors[slot], instance->_transitionDur, segn, slot);
|
||||
colors[slot] = c; return true;
|
||||
}
|
||||
void setCCT(uint16_t k, uint8_t segn) {
|
||||
if (segn >= MAX_NUM_SEGMENTS) return;
|
||||
if (k > 255) { //kelvin value, convert to 0-255
|
||||
if (k < 1900) k = 1900;
|
||||
if (k > 10091) k = 10091;
|
||||
k = (k - 1900) >> 5;
|
||||
}
|
||||
if (cct == k) return;
|
||||
ColorTransition::startTransition(cct, colors[1], instance->_transitionDur, segn, 1);
|
||||
cct = k;
|
||||
}
|
||||
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);
|
||||
bool prevOn = false;
|
||||
if (n == SEG_OPTION_ON) {
|
||||
prevOn = getOption(SEG_OPTION_ON);
|
||||
if (!val && prevOn) { //fade off
|
||||
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}*/
|
||||
|
||||
if (n == SEG_OPTION_ON && val && !prevOn) { //fade on
|
||||
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
|
||||
}
|
||||
}
|
||||
bool getOption(uint8_t n)
|
||||
{
|
||||
return ((options >> n) & 0x01);
|
||||
}
|
||||
bool isSelected()
|
||||
inline bool isSelected()
|
||||
{
|
||||
return getOption(0);
|
||||
}
|
||||
bool isActive()
|
||||
inline bool isActive()
|
||||
{
|
||||
return stop > start;
|
||||
}
|
||||
uint16_t length()
|
||||
inline uint16_t length()
|
||||
{
|
||||
return stop - start;
|
||||
}
|
||||
uint16_t groupLength()
|
||||
inline uint16_t groupLength()
|
||||
{
|
||||
return grouping + spacing;
|
||||
}
|
||||
@@ -320,21 +334,48 @@ class WS2812FX {
|
||||
vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vLength;
|
||||
}
|
||||
uint8_t differs(Segment& b) {
|
||||
uint8_t d = 0;
|
||||
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (offset != b.offset) d |= SEG_DIFFERS_GSO;
|
||||
if (grouping != b.grouping) d |= SEG_DIFFERS_GSO;
|
||||
if (spacing != b.spacing) d |= SEG_DIFFERS_GSO;
|
||||
if (opacity != b.opacity) d |= SEG_DIFFERS_BRI;
|
||||
if (mode != b.mode) d |= SEG_DIFFERS_FX;
|
||||
if (speed != b.speed) d |= SEG_DIFFERS_FX;
|
||||
if (intensity != b.intensity) d |= SEG_DIFFERS_FX;
|
||||
if (palette != b.palette) d |= SEG_DIFFERS_FX;
|
||||
|
||||
if ((options & 0b00101111) != (b.options & 0b00101111)) d |= SEG_DIFFERS_OPT;
|
||||
for (uint8_t i = 0; i < NUM_COLORS; i++)
|
||||
{
|
||||
if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
} segment;
|
||||
|
||||
// segment runtime parameters
|
||||
typedef struct Segment_runtime { // 28 bytes
|
||||
unsigned long next_time;
|
||||
uint32_t step;
|
||||
uint32_t call;
|
||||
uint16_t aux0;
|
||||
uint16_t aux1;
|
||||
unsigned long next_time; // millis() of next update
|
||||
uint32_t step; // custom "step" var
|
||||
uint32_t call; // call counter
|
||||
uint16_t aux0; // custom var
|
||||
uint16_t aux1; // custom var
|
||||
byte* data = nullptr;
|
||||
bool allocateData(uint16_t len){
|
||||
if (data && _dataLen == len) return true; //already allocated
|
||||
deallocateData();
|
||||
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
|
||||
data = new (std::nothrow) byte[len];
|
||||
// if possible use SPI RAM on ESP32
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
if (psramFound())
|
||||
data = (byte*) ps_malloc(len);
|
||||
else
|
||||
#endif
|
||||
data = (byte*) malloc(len);
|
||||
if (!data) return false; //allocation failed
|
||||
WS2812FX::instance->_usedSegmentData += len;
|
||||
_dataLen = len;
|
||||
@@ -342,7 +383,7 @@ class WS2812FX {
|
||||
return true;
|
||||
}
|
||||
void deallocateData(){
|
||||
delete[] data;
|
||||
free(data);
|
||||
data = nullptr;
|
||||
WS2812FX::instance->_usedSegmentData -= _dataLen;
|
||||
_dataLen = 0;
|
||||
@@ -368,7 +409,7 @@ class WS2812FX {
|
||||
* the internal segment state should be reset.
|
||||
* Call resetIfRequired before calling the next effect function.
|
||||
*/
|
||||
void reset() { _requiresReset = true; }
|
||||
inline void reset() { _requiresReset = true; }
|
||||
private:
|
||||
uint16_t _dataLen = 0;
|
||||
bool _requiresReset = false;
|
||||
@@ -383,6 +424,7 @@ class WS2812FX {
|
||||
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
|
||||
if (!instance->_segments[segn].getOption(SEG_OPTION_ON)) return; //not if segment is off either
|
||||
uint8_t tIndex = 0xFF; //none found
|
||||
uint16_t tProgression = 0;
|
||||
uint8_t s = segn + (slot << 6); //merge slot and segment into one byte
|
||||
@@ -411,7 +453,8 @@ class WS2812FX {
|
||||
ColorTransition& t = instance->transitions[tIndex];
|
||||
if (t.segment == s) //this is an active transition on the same segment+color
|
||||
{
|
||||
t.briOld = t.currentBri();
|
||||
bool wasTurningOff = (oldBri == 0);
|
||||
t.briOld = t.currentBri(wasTurningOff, slot);
|
||||
t.colorOld = t.currentColor(oldCol);
|
||||
} else {
|
||||
t.briOld = oldBri;
|
||||
@@ -443,10 +486,15 @@ class WS2812FX {
|
||||
uint32_t currentColor(uint32_t colorNew) {
|
||||
return instance->color_blend(colorOld, colorNew, progress(true), true);
|
||||
}
|
||||
uint8_t currentBri() {
|
||||
uint8_t currentBri(bool turningOff = false, uint8_t slot = 0) {
|
||||
uint8_t segn = segment & 0x3F;
|
||||
if (segn >= MAX_NUM_SEGMENTS) return 0;
|
||||
uint8_t briNew = instance->_segments[segn].opacity;
|
||||
if (slot == 0) {
|
||||
if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0;
|
||||
} else { //transition slot 1 brightness for CCT transition
|
||||
briNew = instance->_segments[segn].cct;
|
||||
}
|
||||
uint32_t prog = progress() + 1;
|
||||
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
|
||||
}
|
||||
@@ -502,9 +550,9 @@ class WS2812FX {
|
||||
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
|
||||
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
|
||||
_mode[FX_MODE_POLICE] = &WS2812FX::mode_police;
|
||||
_mode[FX_MODE_POLICE_ALL] = &WS2812FX::mode_police_all;
|
||||
_mode[FX_MODE_FAIRY] = &WS2812FX::mode_fairy;
|
||||
_mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots;
|
||||
_mode[FX_MODE_TWO_AREAS] = &WS2812FX::mode_two_areas;
|
||||
_mode[FX_MODE_FAIRYTWINKLE] = &WS2812FX::mode_fairytwinkle;
|
||||
_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;
|
||||
@@ -584,7 +632,7 @@ class WS2812FX {
|
||||
}
|
||||
|
||||
void
|
||||
finalizeInit(uint16_t countPixels),
|
||||
finalizeInit(),
|
||||
service(void),
|
||||
blur(uint8_t),
|
||||
fill(uint32_t),
|
||||
@@ -599,13 +647,15 @@ class WS2812FX {
|
||||
setTransitionMode(bool t),
|
||||
calcGammaTable(float),
|
||||
trigger(void),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX),
|
||||
resetSegments(),
|
||||
makeAutoSegments(),
|
||||
fixInvalidSegments(),
|
||||
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),
|
||||
setColorOrder(uint8_t co),
|
||||
setPixelSegment(uint8_t n);
|
||||
setPixelSegment(uint8_t n),
|
||||
deserializeMap(uint8_t n=0);
|
||||
|
||||
bool
|
||||
isRgbw = false,
|
||||
@@ -613,46 +663,41 @@ class WS2812FX {
|
||||
gammaCorrectBri = false,
|
||||
gammaCorrectCol = true,
|
||||
applyToAllSelected = true,
|
||||
segmentsAreIdentical(Segment* a, Segment* b),
|
||||
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p),
|
||||
checkSegmentAlignment(void),
|
||||
hasCCTBus(void),
|
||||
// return true if the strip is being sent pixel updates
|
||||
isUpdating(void);
|
||||
|
||||
uint8_t
|
||||
mainSegment = 0,
|
||||
rgbwMode = RGBW_MODE_DUAL,
|
||||
paletteFade = 0,
|
||||
paletteBlend = 0,
|
||||
milliampsPerLed = 55,
|
||||
// getStripType(uint8_t strip=0),
|
||||
// setStripType(uint8_t type, uint8_t strip=0),
|
||||
cctBlending = 0,
|
||||
getBrightness(void),
|
||||
getMode(void),
|
||||
getSpeed(void),
|
||||
getModeCount(void),
|
||||
getPaletteCount(void),
|
||||
getMaxSegments(void),
|
||||
getActiveSegmentsNum(void),
|
||||
//getFirstSelectedSegment(void),
|
||||
getMainSegmentId(void),
|
||||
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,
|
||||
// setStripLen(uint8_t strip, uint16_t len),
|
||||
// getStripLen(uint8_t strip=0),
|
||||
triwave16(uint16_t),
|
||||
getLengthTotal(void),
|
||||
getLengthPhysical(void),
|
||||
getFps();
|
||||
|
||||
uint32_t
|
||||
@@ -728,9 +773,9 @@ class WS2812FX {
|
||||
mode_gradient(void),
|
||||
mode_loading(void),
|
||||
mode_police(void),
|
||||
mode_police_all(void),
|
||||
mode_fairy(void),
|
||||
mode_two_dots(void),
|
||||
mode_two_areas(void),
|
||||
mode_fairytwinkle(void),
|
||||
mode_running_dual(void),
|
||||
mode_bicolor_chase(void),
|
||||
mode_tricolor_chase(void),
|
||||
@@ -812,9 +857,6 @@ class WS2812FX {
|
||||
|
||||
uint16_t _cumulativeFps = 2;
|
||||
|
||||
void load_gradient_palette(uint8_t);
|
||||
void handle_palette(void);
|
||||
|
||||
bool
|
||||
_triggered;
|
||||
|
||||
@@ -829,7 +871,6 @@ class WS2812FX {
|
||||
color_wipe(bool, bool),
|
||||
dynamic(bool),
|
||||
scan(bool),
|
||||
theater_chase(uint32_t, uint32_t, bool),
|
||||
running_base(bool,bool),
|
||||
larson_scanner(bool),
|
||||
sinelon_base(bool,bool),
|
||||
@@ -837,8 +878,8 @@ class WS2812FX {
|
||||
chase(uint32_t, uint32_t, uint32_t, bool),
|
||||
gradient_base(bool),
|
||||
ripple_base(bool),
|
||||
police_base(uint32_t, uint32_t, bool),
|
||||
running(uint32_t, uint32_t),
|
||||
police_base(uint32_t, uint32_t),
|
||||
running(uint32_t, uint32_t, bool theatre=false),
|
||||
tricolor_chase(uint32_t, uint32_t),
|
||||
twinklefox_base(bool),
|
||||
spots_base(uint16_t),
|
||||
@@ -850,7 +891,9 @@ class WS2812FX {
|
||||
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);
|
||||
estimateCurrentAndLimitBri(void),
|
||||
load_gradient_palette(uint8_t),
|
||||
handle_palette(void);
|
||||
|
||||
uint16_t* customMappingTable = nullptr;
|
||||
uint16_t customMappingSize = 0;
|
||||
@@ -883,9 +926,9 @@ 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","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",
|
||||
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Chase 2","Aurora","Stream",
|
||||
"Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Fairy",
|
||||
"Two Dots","Fairytwinkle","Running Dual","Halloween","Chase 3","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",
|
||||
@@ -901,7 +944,9 @@ 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 2"
|
||||
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf",
|
||||
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
|
||||
"Candy2"
|
||||
])=====";
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
Modified heavily for WLED
|
||||
*/
|
||||
|
||||
#include "wled.h"
|
||||
#include "FX.h"
|
||||
#include "palettes.h"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
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]}
|
||||
*/
|
||||
|
||||
//factory defaults LED setup
|
||||
@@ -65,71 +65,51 @@
|
||||
#endif
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void WS2812FX::finalizeInit(uint16_t countPixels)
|
||||
void WS2812FX::finalizeInit(void)
|
||||
{
|
||||
RESET_RUNTIME;
|
||||
_length = countPixels;
|
||||
isRgbw = isOffRefreshRequred = false;
|
||||
|
||||
//if busses failed to load, add default (FS issue...)
|
||||
//if busses failed to load, add default (fresh install, 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++) {
|
||||
for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES; 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];
|
||||
}
|
||||
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
prevLen += count;
|
||||
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB);
|
||||
busses.add(defCfg);
|
||||
}
|
||||
}
|
||||
|
||||
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++;
|
||||
|
||||
_length = 0;
|
||||
for (uint8_t i=0; i<busses.getNumBusses(); i++) {
|
||||
Bus *bus = busses.getBus(i);
|
||||
if (bus == nullptr) continue;
|
||||
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
|
||||
//RGBW mode is enabled if at least one of the strips is RGBW
|
||||
isRgbw |= bus->isRgbw();
|
||||
//refresh is required to remain off if at least one of the strips requires the refresh.
|
||||
isOffRefreshRequred |= bus->isOffRefreshRequired();
|
||||
uint16_t busEnd = bus->getStart() + bus->getLength();
|
||||
if (busEnd > _length) _length = busEnd;
|
||||
#ifdef ESP8266
|
||||
if ((!IS_DIGITAL(b->getType()) || IS_2PIN(b->getType()))) continue;
|
||||
if ((!IS_DIGITAL(bus->getType()) || IS_2PIN(bus->getType()))) continue;
|
||||
uint8_t pins[5];
|
||||
b->getPins(pins);
|
||||
BusDigital* bd = static_cast<BusDigital*>(b);
|
||||
if (!bus->getPins(pins)) continue;
|
||||
BusDigital* bd = static_cast<BusDigital*>(bus);
|
||||
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];
|
||||
}
|
||||
//segments are created in makeAutoSegments();
|
||||
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
void WS2812FX::service() {
|
||||
@@ -157,13 +137,16 @@ void WS2812FX::service() {
|
||||
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];
|
||||
uint8_t _cct_t = SEGMENT.cct;
|
||||
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();
|
||||
if (slot == 1) _cct_t = transitions[t].currentBri(false, 1);
|
||||
_colors_t[slot] = transitions[t].currentColor(SEGMENT.colors[slot]);
|
||||
}
|
||||
if (!cctFromRgb || correctWB) busses.setSegmentCCT(_cct_t, correctWB);
|
||||
for (uint8_t c = 0; c < 3; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
handle_palette();
|
||||
delay = (this->*_mode[SEGMENT.mode])(); //effect function
|
||||
@@ -174,6 +157,7 @@ void WS2812FX::service() {
|
||||
}
|
||||
}
|
||||
_virtualSegmentLength = 0;
|
||||
busses.setSegmentCCT(-1);
|
||||
if(doShow) {
|
||||
yield();
|
||||
show();
|
||||
@@ -182,11 +166,7 @@ void WS2812FX::service() {
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
|
||||
uint8_t w = (c >> 24);
|
||||
uint8_t r = (c >> 16);
|
||||
uint8_t g = (c >> 8);
|
||||
uint8_t b = c ;
|
||||
setPixelColor(n, r, g, b, w);
|
||||
setPixelColor(n, R(c), G(c), B(c), W(c));
|
||||
}
|
||||
|
||||
//used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring
|
||||
@@ -197,34 +177,21 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
|
||||
int16_t realIndex = iGroup;
|
||||
if (IS_REVERSE) {
|
||||
if (IS_MIRROR) {
|
||||
realIndex = (SEGMENT.length() -1) / 2 - iGroup; //only need to index half the pixels
|
||||
realIndex = (SEGMENT.length() - 1) / 2 - iGroup; //only need to index half the pixels
|
||||
} else {
|
||||
realIndex = SEGMENT.length() - iGroup - 1;
|
||||
realIndex = (SEGMENT.length() - 1) - iGroup;
|
||||
}
|
||||
}
|
||||
|
||||
realIndex += SEGMENT.start;
|
||||
|
||||
return realIndex;
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
//auto calculate white channel value if enabled
|
||||
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
|
||||
//thank you to @Def3nder!
|
||||
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
} else if (rgbwMode == RGBW_MODE_AUTO_ACCURATE && w == 0)
|
||||
{
|
||||
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
r -= w; g -= w; b -= w;
|
||||
}
|
||||
}
|
||||
|
||||
if (SEGLEN) {//from segment
|
||||
uint16_t realIndex = realPixelIndex(i);
|
||||
uint16_t len = SEGMENT.length();
|
||||
|
||||
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
|
||||
if (_bri_t < 255) {
|
||||
@@ -233,14 +200,11 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
b = scale8(b, _bri_t);
|
||||
w = scale8(w, _bri_t);
|
||||
}
|
||||
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
|
||||
bool reversed = IS_REVERSE;
|
||||
uint16_t realIndex = realPixelIndex(i);
|
||||
uint16_t len = SEGMENT.length();
|
||||
uint32_t col = RGBW32(r, g, b, w);
|
||||
|
||||
/* Set all the pixels in the group */
|
||||
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
|
||||
int indexSet = realIndex + (reversed ? -j : j);
|
||||
uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
|
||||
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
|
||||
if (IS_MIRROR) { //set the corresponding mirrored pixel
|
||||
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
|
||||
@@ -252,8 +216,8 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
busses.setPixelColor(indexMir, col);
|
||||
}
|
||||
/* offset/phase */
|
||||
indexSet += SEGMENT.offset;
|
||||
if (indexSet >= SEGMENT.stop) indexSet -= len;
|
||||
indexSet += SEGMENT.offset;
|
||||
if (indexSet >= SEGMENT.stop) indexSet -= len;
|
||||
|
||||
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
|
||||
busses.setPixelColor(indexSet, col);
|
||||
@@ -261,9 +225,7 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
}
|
||||
} else { //live data, etc.
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
|
||||
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
busses.setPixelColor(i, col);
|
||||
busses.setPixelColor(i, RGBW32(r, g, b, w));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,12 +241,7 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
|
||||
// avoid race condition, caputre _callback value
|
||||
show_callback callback = _callback;
|
||||
if (callback) callback();
|
||||
|
||||
void WS2812FX::estimateCurrentAndLimitBri() {
|
||||
//power limit calculation
|
||||
//each LED can draw up 195075 "power units" (approx. 53mA)
|
||||
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step
|
||||
@@ -297,65 +254,72 @@ void WS2812FX::show(void) {
|
||||
actualMilliampsPerLed = 12; // from testing an actual strip
|
||||
}
|
||||
|
||||
if (ablMilliampsMax > 149 && actualMilliampsPerLed > 0) //0 mA per LED and too low numbers turn off calculation
|
||||
{
|
||||
uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed;
|
||||
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power
|
||||
if (powerBudget > puPerMilliamp * _length) //each LED uses about 1mA in standby, exclude that from power budget
|
||||
{
|
||||
powerBudget -= puPerMilliamp * _length;
|
||||
} else
|
||||
{
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
uint32_t powerSum = 0;
|
||||
|
||||
for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED
|
||||
{
|
||||
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(r,g),b)) * 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
powerSum += (r + g + b + w);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
uint32_t powerSum0 = powerSum;
|
||||
powerSum *= _brightness;
|
||||
|
||||
if (powerSum > powerBudget) //scale brightness down to stay in current limit
|
||||
{
|
||||
float scale = (float)powerBudget / (float)powerSum;
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
uint8_t newBri = scale8(_brightness, scaleB);
|
||||
busses.setBrightness(newBri);
|
||||
currentMilliamps = (powerSum0 * newBri) / puPerMilliamp;
|
||||
} else
|
||||
{
|
||||
currentMilliamps = powerSum / puPerMilliamp;
|
||||
busses.setBrightness(_brightness);
|
||||
}
|
||||
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
|
||||
currentMilliamps += _length; //add standby power back to estimate
|
||||
} else {
|
||||
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
|
||||
currentMilliamps = 0;
|
||||
busses.setBrightness(_brightness);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t pLen = getLengthPhysical();
|
||||
uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed;
|
||||
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power
|
||||
if (powerBudget > puPerMilliamp * pLen) { //each LED uses about 1mA in standby, exclude that from power budget
|
||||
powerBudget -= puPerMilliamp * pLen;
|
||||
} else {
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
uint32_t powerSum = 0;
|
||||
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
|
||||
uint16_t len = bus->getLength();
|
||||
uint32_t busPowerSum = 0;
|
||||
for (uint16_t i = 0; i < len; i++) { //sum up the usage of each LED
|
||||
uint32_t c = bus->getPixelColor(i);
|
||||
byte r = R(c), g = G(c), b = B(c), w = W(c);
|
||||
|
||||
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
|
||||
busPowerSum += (MAX(MAX(r,g),b)) * 3;
|
||||
} else {
|
||||
busPowerSum += (r + g + b + w);
|
||||
}
|
||||
}
|
||||
|
||||
if (bus->isRgbw()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
busPowerSum *= 3;
|
||||
busPowerSum = busPowerSum >> 2; //same as /= 4
|
||||
}
|
||||
powerSum += busPowerSum;
|
||||
}
|
||||
|
||||
uint32_t powerSum0 = powerSum;
|
||||
powerSum *= _brightness;
|
||||
|
||||
if (powerSum > powerBudget) //scale brightness down to stay in current limit
|
||||
{
|
||||
float scale = (float)powerBudget / (float)powerSum;
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
uint8_t newBri = scale8(_brightness, scaleB);
|
||||
busses.setBrightness(newBri); //to keep brightness uniform, sets virtual busses too
|
||||
currentMilliamps = (powerSum0 * newBri) / puPerMilliamp;
|
||||
} else {
|
||||
currentMilliamps = powerSum / puPerMilliamp;
|
||||
busses.setBrightness(_brightness);
|
||||
}
|
||||
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
|
||||
currentMilliamps += pLen; //add standby power back to estimate
|
||||
}
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
|
||||
// avoid race condition, caputre _callback value
|
||||
show_callback callback = _callback;
|
||||
if (callback) callback();
|
||||
|
||||
estimateCurrentAndLimitBri();
|
||||
|
||||
// some buses send asynchronously and this method will return before
|
||||
// all of the data has been sent.
|
||||
@@ -450,7 +414,7 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
setColor(slot, ((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
|
||||
setColor(slot, RGBW32(r, g, b, w));
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
@@ -478,14 +442,14 @@ void WS2812FX::setBrightness(uint8_t b) {
|
||||
if (gammaCorrectBri) b = gamma8(b);
|
||||
if (_brightness == b) return;
|
||||
_brightness = b;
|
||||
_segment_index = 0;
|
||||
if (_brightness == 0) { //unfreeze all segments on power off
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
_segments[i].setOption(SEG_OPTION_FREEZE, false);
|
||||
}
|
||||
}
|
||||
if (SEGENV.next_time > millis() + 22 && millis() - _lastShow > MIN_SHOW_DELAY) show();//apply brightness change immediately if no refresh soon
|
||||
unsigned long t = millis();
|
||||
if (_segment_runtimes[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getMode(void) {
|
||||
@@ -527,6 +491,15 @@ uint8_t WS2812FX::getMainSegmentId(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getActiveSegmentsNum(void) {
|
||||
uint8_t c = 0;
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive()) c++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getColor(void) {
|
||||
return _segments[getMainSegmentId()].colors[0];
|
||||
}
|
||||
@@ -542,6 +515,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
}
|
||||
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
if (i >= _length) return 0;
|
||||
|
||||
return busses.getPixelColor(i);
|
||||
}
|
||||
@@ -563,26 +537,51 @@ uint32_t WS2812FX::getLastShow(void) {
|
||||
return _lastShow;
|
||||
}
|
||||
|
||||
//TODO these need to be on a per-strip basis
|
||||
uint8_t WS2812FX::getColorOrder(void) {
|
||||
return COL_ORDER_GRB;
|
||||
uint16_t WS2812FX::getLengthTotal(void) {
|
||||
return _length;
|
||||
}
|
||||
|
||||
void WS2812FX::setColorOrder(uint8_t co) {
|
||||
//bus->SetColorOrder(co);
|
||||
uint16_t WS2812FX::getLengthPhysical(void) {
|
||||
uint16_t len = 0;
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
|
||||
len += bus->getLength();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
|
||||
bool WS2812FX::hasCCTBus(void) {
|
||||
if (cctFromRgb && !correctWB) return false;
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
switch (bus->getType()) {
|
||||
case TYPE_ANALOG_5CH:
|
||||
case TYPE_ANALOG_2CH:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset) {
|
||||
if (n >= MAX_NUM_SEGMENTS) return;
|
||||
Segment& seg = _segments[n];
|
||||
|
||||
//return if neither bounds nor grouping have changed
|
||||
if (seg.start == i1 && seg.stop == i2 && (!grouping || (seg.grouping == grouping && seg.spacing == spacing))) return;
|
||||
if (seg.start == i1 && seg.stop == i2
|
||||
&& (!grouping || (seg.grouping == grouping && seg.spacing == spacing))
|
||||
&& (offset == UINT16_MAX || offset == seg.offset)) return;
|
||||
|
||||
if (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off
|
||||
if (i2 <= i1) //disable segment
|
||||
{
|
||||
seg.stop = 0;
|
||||
seg.stop = 0;
|
||||
if (seg.name) {
|
||||
delete[] seg.name;
|
||||
seg.name = nullptr;
|
||||
}
|
||||
if (n == mainSegment) //if main segment is deleted, set first active as main segment
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
@@ -603,10 +602,12 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
|
||||
seg.grouping = grouping;
|
||||
seg.spacing = spacing;
|
||||
}
|
||||
if (offset < UINT16_MAX) seg.offset = offset;
|
||||
_segment_runtimes[n].reset();
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
|
||||
mainSegment = 0;
|
||||
memset(_segments, 0, sizeof(_segments));
|
||||
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
|
||||
@@ -621,6 +622,7 @@ void WS2812FX::resetSegments() {
|
||||
_segments[0].setOption(SEG_OPTION_SELECTED, 1);
|
||||
_segments[0].setOption(SEG_OPTION_ON, 1);
|
||||
_segments[0].opacity = 255;
|
||||
_segments[0].cct = 127;
|
||||
|
||||
for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
@@ -628,6 +630,7 @@ void WS2812FX::resetSegments() {
|
||||
_segments[i].grouping = 1;
|
||||
_segments[i].setOption(SEG_OPTION_ON, 1);
|
||||
_segments[i].opacity = 255;
|
||||
_segments[i].cct = 127;
|
||||
_segments[i].speed = DEFAULT_SPEED;
|
||||
_segments[i].intensity = DEFAULT_INTENSITY;
|
||||
_segment_runtimes[i].reset();
|
||||
@@ -635,15 +638,98 @@ void WS2812FX::resetSegments() {
|
||||
_segment_runtimes[0].reset();
|
||||
}
|
||||
|
||||
void WS2812FX::makeAutoSegments() {
|
||||
if (autoSegments) { //make one segment per bus
|
||||
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
|
||||
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
|
||||
uint8_t s = 0;
|
||||
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
|
||||
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++;
|
||||
}
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
|
||||
setSegment(i, segStarts[i], segStops[i]);
|
||||
}
|
||||
} else {
|
||||
//expand the main seg to the entire length, but only if there are no other segments
|
||||
uint8_t mainSeg = getMainSegmentId();
|
||||
|
||||
if (getActiveSegmentsNum() < 2) {
|
||||
setSegment(mainSeg, 0, _length);
|
||||
}
|
||||
}
|
||||
|
||||
fixInvalidSegments();
|
||||
}
|
||||
|
||||
void WS2812FX::fixInvalidSegments() {
|
||||
//make sure no segment is longer than total (sanity check)
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].start >= _length) setSegment(i, 0, 0);
|
||||
if (_segments[i].stop > _length) setSegment(i, _segments[i].start, _length);
|
||||
}
|
||||
}
|
||||
|
||||
//true if all segments align with a bus, or if a segment covers the total length
|
||||
bool WS2812FX::checkSegmentAlignment() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].start >= _segments[i].stop) continue; //inactive segment
|
||||
bool aligned = false;
|
||||
for (uint8_t b = 0; b<busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (_segments[i].start == bus->getStart() && _segments[i].stop == bus->getStart() + bus->getLength()) aligned = true;
|
||||
}
|
||||
if (_segments[i].start == 0 && _segments[i].stop == _length) aligned = true;
|
||||
if (!aligned) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
|
||||
//Note: If called in an interrupt (e.g. JSON API), it must be reset with "setPixelColor(255)",
|
||||
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint8_t _segment_index_prev = 0;
|
||||
uint16_t _virtualSegmentLength_prev = 0;
|
||||
bool _ps_set = false;
|
||||
#endif
|
||||
|
||||
void WS2812FX::setPixelSegment(uint8_t n)
|
||||
{
|
||||
if (n < MAX_NUM_SEGMENTS) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!_ps_set) {
|
||||
_segment_index_prev = _segment_index;
|
||||
_virtualSegmentLength_prev = _virtualSegmentLength;
|
||||
_ps_set = true;
|
||||
}
|
||||
#endif
|
||||
_segment_index = n;
|
||||
_virtualSegmentLength = SEGMENT.length();
|
||||
_virtualSegmentLength = SEGMENT.virtualLength();
|
||||
} else {
|
||||
_segment_index = 0;
|
||||
_virtualSegmentLength = 0;
|
||||
_virtualSegmentLength = 0;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (_ps_set) {
|
||||
_segment_index = _segment_index_prev;
|
||||
_virtualSegmentLength = _virtualSegmentLength_prev;
|
||||
_ps_set = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,13 +756,13 @@ void WS2812FX::setTransition(uint16_t t)
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t)
|
||||
{
|
||||
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
|
||||
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
|
||||
for (uint16_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
_segment_index = i;
|
||||
SEGMENT.setOption(SEG_OPTION_TRANSITIONAL, t);
|
||||
_segments[i].setOption(SEG_OPTION_TRANSITIONAL, t);
|
||||
|
||||
if (t && SEGMENT.mode == FX_MODE_STATIC && SEGENV.next_time > waitMax) SEGENV.next_time = waitMax;
|
||||
if (t && _segments[i].mode == FX_MODE_STATIC && _segment_runtimes[i].next_time > waitMax)
|
||||
_segment_runtimes[i].next_time = waitMax;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,22 +775,22 @@ uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend,
|
||||
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 = W(color1);
|
||||
uint32_t r1 = R(color1);
|
||||
uint32_t g1 = G(color1);
|
||||
uint32_t b1 = B(color1);
|
||||
|
||||
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 = W(color2);
|
||||
uint32_t r2 = R(color2);
|
||||
uint32_t g2 = G(color2);
|
||||
uint32_t b2 = B(color2);
|
||||
|
||||
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));
|
||||
return RGBW32(r3, g3, b3, w3);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -732,17 +818,17 @@ void WS2812FX::fade_out(uint8_t rate) {
|
||||
float mappedRate = float(rate) +1.1;
|
||||
|
||||
uint32_t color = SEGCOLOR(1); // target color
|
||||
int w2 = (color >> 24) & 0xff;
|
||||
int r2 = (color >> 16) & 0xff;
|
||||
int g2 = (color >> 8) & 0xff;
|
||||
int b2 = color & 0xff;
|
||||
int w2 = W(color);
|
||||
int r2 = R(color);
|
||||
int g2 = G(color);
|
||||
int b2 = B(color);
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
color = getPixelColor(i);
|
||||
int w1 = (color >> 24) & 0xff;
|
||||
int r1 = (color >> 16) & 0xff;
|
||||
int g1 = (color >> 8) & 0xff;
|
||||
int b1 = color & 0xff;
|
||||
int w1 = W(color);
|
||||
int r1 = R(color);
|
||||
int g1 = G(color);
|
||||
int b1 = B(color);
|
||||
|
||||
int wdelta = (w2 - w1) / mappedRate;
|
||||
int rdelta = (r2 - r1) / mappedRate;
|
||||
@@ -776,9 +862,9 @@ void WS2812FX::blur(uint8_t blur_amount)
|
||||
cur += carryover;
|
||||
if(i > 0) {
|
||||
uint32_t c = getPixelColor(i-1);
|
||||
uint8_t r = (c >> 16 & 0xFF);
|
||||
uint8_t g = (c >> 8 & 0xFF);
|
||||
uint8_t b = (c & 0xFF);
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
|
||||
}
|
||||
setPixelColor(i,cur.red, cur.green, cur.blue);
|
||||
@@ -861,16 +947,16 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
|
||||
|
||||
uint32_t WS2812FX::crgb_to_col(CRGB fastled)
|
||||
{
|
||||
return (((uint32_t)fastled.red << 16) | ((uint32_t)fastled.green << 8) | fastled.blue);
|
||||
return RGBW32(fastled.red, fastled.green, fastled.blue, 0);
|
||||
}
|
||||
|
||||
|
||||
CRGB WS2812FX::col_to_crgb(uint32_t color)
|
||||
{
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = (color >> 16 & 0xFF);
|
||||
fastled_col.green = (color >> 8 & 0xFF);
|
||||
fastled_col.blue = (color & 0xFF);
|
||||
fastled_col.red = R(color);
|
||||
fastled_col.green = G(color);
|
||||
fastled_col.blue = B(color);
|
||||
return fastled_col;
|
||||
}
|
||||
|
||||
@@ -1010,37 +1096,44 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
|
||||
return crgb_to_col(fastled_col);
|
||||
}
|
||||
|
||||
//@returns `true` if color, mode, speed, intensity and palette match
|
||||
bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b)
|
||||
{
|
||||
//if (a->start != b->start) return false;
|
||||
//if (a->stop != b->stop) return false;
|
||||
for (uint8_t i = 0; i < NUM_COLORS; i++)
|
||||
{
|
||||
if (a->colors[i] != b->colors[i]) return false;
|
||||
|
||||
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
|
||||
void WS2812FX::deserializeMap(uint8_t n) {
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/ledmap"));
|
||||
if (n) sprintf(fileName +7, "%d", n);
|
||||
strcat(fileName, ".json");
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
|
||||
if (!isFile) {
|
||||
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
|
||||
if (!n && customMappingTable != nullptr) {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (a->mode != b->mode) return false;
|
||||
if (a->speed != b->speed) return false;
|
||||
if (a->intensity != b->intensity) return false;
|
||||
if (a->palette != b->palette) return false;
|
||||
//if (a->getOption(SEG_OPTION_REVERSED) != b->getOption(SEG_OPTION_REVERSED)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WLED_USE_DYNAMIC_JSON
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
#else
|
||||
if (!requestJSONBufferLock(7)) return;
|
||||
#endif
|
||||
|
||||
//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
|
||||
DEBUG_PRINT(F("Reading LED map from "));
|
||||
DEBUG_PRINTLN(fileName);
|
||||
|
||||
DEBUG_PRINTLN(F("Reading LED map from /ledmap.json..."));
|
||||
|
||||
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
|
||||
if (!readObjectFromFile(fileName, nullptr, &doc)) {
|
||||
releaseJSONBufferLock();
|
||||
return; //if file does not exist just exit
|
||||
}
|
||||
|
||||
// erase old custom ledmap
|
||||
if (customMappingTable != nullptr) {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
customMappingSize = 0;
|
||||
}
|
||||
|
||||
JsonArray map = doc[F("map")];
|
||||
@@ -1051,6 +1144,8 @@ void WS2812FX::deserializeMap(void) {
|
||||
customMappingTable[i] = (uint16_t) map[i];
|
||||
}
|
||||
}
|
||||
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
//gamma 2.8 lookup table used for color correction
|
||||
@@ -1091,15 +1186,20 @@ uint8_t WS2812FX::gamma8(uint8_t b)
|
||||
uint32_t WS2812FX::gamma32(uint32_t color)
|
||||
{
|
||||
if (!gammaCorrectCol) return color;
|
||||
uint8_t w = (color >> 24);
|
||||
uint8_t r = (color >> 16);
|
||||
uint8_t g = (color >> 8);
|
||||
uint8_t b = color;
|
||||
uint8_t w = W(color);
|
||||
uint8_t r = R(color);
|
||||
uint8_t g = G(color);
|
||||
uint8_t b = B(color);
|
||||
w = gammaT[w];
|
||||
r = gammaT[r];
|
||||
g = gammaT[g];
|
||||
b = gammaT[b];
|
||||
return ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
||||
|
||||
//Bus static member definition, would belong in bus_manager.cpp
|
||||
int16_t Bus::_cct = -1;
|
||||
uint8_t Bus::_cctBlend = 0;
|
||||
uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL;
|
||||
@@ -44,10 +44,10 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
if (bri == 0)
|
||||
{
|
||||
bri = briLast;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroAlexaOn);
|
||||
applyPreset(macroAlexaOn, CALL_MODE_ALEXA);
|
||||
if (bri == 0) espalexaDevice->setValue(briLast); //stop Alexa from complaining if macroAlexaOn does not actually turn on
|
||||
}
|
||||
} else if (m == EspalexaDeviceProperty::off)
|
||||
@@ -58,32 +58,42 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
{
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroAlexaOff);
|
||||
applyPreset(macroAlexaOff, CALL_MODE_ALEXA);
|
||||
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();
|
||||
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
} else //color
|
||||
{
|
||||
if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white
|
||||
{
|
||||
uint16_t ct = espalexaDevice->getCt();
|
||||
if (strip.isRgbw)
|
||||
{
|
||||
if (!ct) return;
|
||||
uint16_t k = 1000000 / ct; //mireds to kelvin
|
||||
|
||||
if (strip.hasCCTBus()) {
|
||||
uint8_t segid = strip.getMainSegmentId();
|
||||
WS2812FX::Segment& seg = strip.getSegment(segid);
|
||||
uint8_t cctPrev = seg.cct;
|
||||
seg.setCCT(k, segid);
|
||||
if (seg.cct != cctPrev) effectChanged = true; //send UDP
|
||||
col[0]= 0; col[1]= 0; col[2]= 0; col[3]= 255;
|
||||
} else 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;
|
||||
case 234: col[0]=127; col[1]=127; col[2]=127; col[3]=255; break;
|
||||
case 284: col[0]= 0; col[1]= 0; col[2]= 0; col[3]=255; break;
|
||||
case 350: col[0]=130; col[1]= 90; col[2]= 0; col[3]=255; break;
|
||||
case 383: col[0]=255; col[1]=153; col[2]= 0; col[3]=255; break;
|
||||
default : colorKtoRGB(k, col);
|
||||
}
|
||||
} else {
|
||||
colorCTtoRGB(ct, col);
|
||||
colorKtoRGB(k, col);
|
||||
}
|
||||
} else {
|
||||
uint32_t color = espalexaDevice->getRGB();
|
||||
@@ -93,7 +103,7 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
col[2] = ( color & 0xFF);
|
||||
col[3] = 0;
|
||||
}
|
||||
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,45 +44,45 @@ void updateBlynk()
|
||||
BLYNK_WRITE(V0)
|
||||
{
|
||||
bri = param.asInt();//bri
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BLYNK);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V1)
|
||||
{
|
||||
blHue = param.asInt();//hue
|
||||
colorHStoRGB(blHue*10,blSat,(false)? colSec:col);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BLYNK);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V2)
|
||||
{
|
||||
blSat = param.asInt();//sat
|
||||
colorHStoRGB(blHue*10,blSat,(false)? colSec:col);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BLYNK);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V3)
|
||||
{
|
||||
bool on = (param.asInt()>0);
|
||||
if (!on != !bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BLYNK);}
|
||||
if (!on != !bri) {toggleOnOff(); colorUpdated(CALL_MODE_BLYNK);}
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V4)
|
||||
{
|
||||
effectCurrent = param.asInt()-1;//fx
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BLYNK);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V5)
|
||||
{
|
||||
effectSpeed = param.asInt();//sx
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BLYNK);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V6)
|
||||
{
|
||||
effectIntensity = param.asInt();//ix
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BLYNK);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V7)
|
||||
|
||||
@@ -10,96 +10,143 @@
|
||||
#include "bus_wrapper.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
//colors.cpp
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
void colorRGBtoRGBW(byte* rgb);
|
||||
|
||||
// enable additional debug output
|
||||
#ifdef WLED_DEBUG
|
||||
#ifndef ESP8266
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#define DEBUG_PRINT(x) Serial.print(x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||
#define DEBUG_PRINTF(x...) Serial.printf(x)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x...)
|
||||
#endif
|
||||
|
||||
#define GET_BIT(var,bit) (((var)>>(bit))&0x01)
|
||||
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
|
||||
#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
|
||||
|
||||
//color mangling macros
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
#define R(c) (byte((c) >> 16))
|
||||
#define G(c) (byte((c) >> 8))
|
||||
#define B(c) (byte(c))
|
||||
#define W(c) (byte((c) >> 24))
|
||||
|
||||
//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;
|
||||
uint16_t count;
|
||||
uint16_t start;
|
||||
uint8_t colorOrder;
|
||||
bool reversed;
|
||||
uint8_t skipAmount;
|
||||
bool refreshReq;
|
||||
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;
|
||||
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) {
|
||||
refreshReq = (bool) GET_BIT(busType,7);
|
||||
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
|
||||
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip;
|
||||
uint8_t nPins = 1;
|
||||
if (type > 47) nPins = 2;
|
||||
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address
|
||||
else 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];
|
||||
}
|
||||
|
||||
//validates start and length and extends total if needed
|
||||
bool adjustBounds(uint16_t& total) {
|
||||
if (!count) count = 1;
|
||||
if (count > MAX_LEDS_PER_BUS) count = MAX_LEDS_PER_BUS;
|
||||
if (start >= MAX_LEDS) return false;
|
||||
//limit length of strip if it would exceed total permissible LEDs
|
||||
if (start + count > MAX_LEDS) count = MAX_LEDS - start;
|
||||
//extend total count accordingly
|
||||
if (start + count > total) total = start + count;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//parent class of BusDigital and BusPwm
|
||||
//parent class of BusDigital, BusPwm, and BusNetwork
|
||||
class Bus {
|
||||
public:
|
||||
Bus(uint8_t type, uint16_t start) {
|
||||
_type = type;
|
||||
_start = start;
|
||||
};
|
||||
|
||||
virtual void show() {}
|
||||
virtual bool canShow() { return true; }
|
||||
Bus(uint8_t type, uint16_t start) {
|
||||
_type = type;
|
||||
_start = start;
|
||||
};
|
||||
|
||||
virtual void setPixelColor(uint16_t pix, uint32_t c) {};
|
||||
virtual ~Bus() {} //throw the bus under the bus
|
||||
|
||||
virtual void setBrightness(uint8_t b) {};
|
||||
virtual void show() {}
|
||||
virtual bool canShow() { return true; }
|
||||
virtual void setStatusPixel(uint32_t c) {}
|
||||
virtual void setPixelColor(uint16_t pix, uint32_t c) {}
|
||||
virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
|
||||
virtual void setBrightness(uint8_t b) {}
|
||||
virtual void cleanup() {}
|
||||
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
|
||||
inline uint16_t getLength() { return _len; }
|
||||
virtual void setColorOrder() {}
|
||||
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
|
||||
virtual uint8_t skippedLeds() { return 0; }
|
||||
inline uint16_t getStart() { return _start; }
|
||||
inline void setStart(uint16_t start) { _start = start; }
|
||||
inline uint8_t getType() { return _type; }
|
||||
inline bool isOk() { return _valid; }
|
||||
inline bool isOffRefreshRequired() { return _needsRefresh; }
|
||||
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
|
||||
|
||||
virtual uint32_t getPixelColor(uint16_t pix) { return 0; };
|
||||
virtual bool isRgbw() { return Bus::isRgbw(_type); }
|
||||
static bool isRgbw(uint8_t type) {
|
||||
if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true;
|
||||
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true;
|
||||
return false;
|
||||
}
|
||||
static void setCCT(uint16_t cct) {
|
||||
_cct = cct;
|
||||
}
|
||||
static void setCCTBlend(uint8_t b) {
|
||||
if (b > 100) b = 100;
|
||||
_cctBlend = (b * 127) / 100;
|
||||
//compile-time limiter for hardware that can't power both white channels at max
|
||||
#ifdef WLED_MAX_CCT_BLEND
|
||||
if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND;
|
||||
#endif
|
||||
}
|
||||
inline static void setAutoWhiteMode(uint8_t m) { if (m < 4) _autoWhiteMode = m; }
|
||||
inline static uint8_t getAutoWhiteMode() { return _autoWhiteMode; }
|
||||
|
||||
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;
|
||||
bool reversed = false;
|
||||
|
||||
protected:
|
||||
uint8_t _type = TYPE_NONE;
|
||||
uint8_t _bri = 255;
|
||||
uint16_t _start = 0;
|
||||
bool _valid = false;
|
||||
uint8_t _type = TYPE_NONE;
|
||||
uint8_t _bri = 255;
|
||||
uint16_t _start = 0;
|
||||
uint16_t _len = 1;
|
||||
bool _valid = false;
|
||||
bool _needsRefresh = false;
|
||||
static uint8_t _autoWhiteMode;
|
||||
static int16_t _cct;
|
||||
static uint8_t _cctBlend;
|
||||
|
||||
uint32_t autoWhiteCalc(uint32_t c) {
|
||||
if (_autoWhiteMode == RGBW_MODE_MANUAL_ONLY) return c;
|
||||
uint8_t w = W(c);
|
||||
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
|
||||
if (w > 0 && _autoWhiteMode == RGBW_MODE_DUAL) return c;
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -107,23 +154,24 @@ class BusDigital : public Bus {
|
||||
public:
|
||||
BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) {
|
||||
if (!IS_DIGITAL(bc.type) || !bc.count) return;
|
||||
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) 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])) {
|
||||
if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
|
||||
cleanup(); return;
|
||||
}
|
||||
_pins[1] = bc.pins[1];
|
||||
}
|
||||
reversed = bc.reversed;
|
||||
_needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814;
|
||||
_skip = bc.skipAmount; //sacrificial pixels
|
||||
_len = bc.count + _skip;
|
||||
_iType = PolyBus::getI(bc.type, _pins, nr);
|
||||
if (_iType == I_NONE) return;
|
||||
_busPtr = PolyBus::create(_iType, _pins, _len);
|
||||
_busPtr = PolyBus::create(_iType, _pins, _len, nr);
|
||||
_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);
|
||||
DEBUG_PRINTF("Successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n",nr, _len, bc.type, _pins[0],_pins[1],_iType);
|
||||
};
|
||||
|
||||
inline void show() {
|
||||
@@ -145,7 +193,18 @@ class BusDigital : public Bus {
|
||||
PolyBus::setBrightness(_busPtr, _iType, b);
|
||||
}
|
||||
|
||||
//If LEDs are skipped, it is possible to use the first as a status LED.
|
||||
//TODO only show if no new show due in the next 50ms
|
||||
void setStatusPixel(uint32_t c) {
|
||||
if (_skip && canShow()) {
|
||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrder);
|
||||
PolyBus::show(_busPtr, _iType);
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814) c = autoWhiteCalc(c);
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
if (reversed) pix = _len - pix -1;
|
||||
else pix += _skip;
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
|
||||
@@ -176,10 +235,6 @@ class BusDigital : public Bus {
|
||||
_colorOrder = colorOrder;
|
||||
}
|
||||
|
||||
inline bool isRgbw() {
|
||||
return (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
|
||||
}
|
||||
|
||||
inline uint8_t skippedLeds() {
|
||||
return _skip;
|
||||
}
|
||||
@@ -189,13 +244,13 @@ class BusDigital : public Bus {
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
//Serial.println("Digital Cleanup");
|
||||
DEBUG_PRINTLN(F("Digital Cleanup."));
|
||||
PolyBus::cleanup(_busPtr, _iType);
|
||||
_iType = I_NONE;
|
||||
_valid = false;
|
||||
_busPtr = nullptr;
|
||||
pinManager.deallocatePin(_pins[0]);
|
||||
pinManager.deallocatePin(_pins[1]);
|
||||
pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
|
||||
pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
|
||||
}
|
||||
|
||||
~BusDigital() {
|
||||
@@ -206,7 +261,6 @@ class BusDigital : public Bus {
|
||||
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;
|
||||
};
|
||||
@@ -215,6 +269,7 @@ class BusDigital : public Bus {
|
||||
class BusPwm : public Bus {
|
||||
public:
|
||||
BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) {
|
||||
_valid = false;
|
||||
if (!IS_PWM(bc.type)) return;
|
||||
uint8_t numPins = NUM_PWM_PINS(bc.type);
|
||||
|
||||
@@ -229,10 +284,11 @@ class BusPwm : public Bus {
|
||||
#endif
|
||||
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
_pins[i] = bc.pins[i];
|
||||
if (!pinManager.allocatePin(_pins[i])) {
|
||||
uint8_t currentPin = bc.pins[i];
|
||||
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
|
||||
deallocatePins(); return;
|
||||
}
|
||||
_pins[i] = currentPin; // store only after allocatePin() succeeds
|
||||
#ifdef ESP8266
|
||||
pinMode(_pins[i], OUTPUT);
|
||||
#else
|
||||
@@ -246,31 +302,66 @@ class BusPwm : public Bus {
|
||||
|
||||
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;
|
||||
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
|
||||
if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
|
||||
c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
}
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
uint8_t w = W(c);
|
||||
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
|
||||
if (_cct > -1) {
|
||||
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
|
||||
else if (_cct < 256) cct = _cct;
|
||||
} else {
|
||||
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
|
||||
}
|
||||
|
||||
uint8_t ww, cw;
|
||||
#ifdef WLED_USE_IC_CCT
|
||||
ww = w;
|
||||
cw = cct;
|
||||
#else
|
||||
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
|
||||
if (cct < _cctBlend) ww = 255;
|
||||
else ww = ((255-cct) * 255) / (255 - _cctBlend);
|
||||
|
||||
if ((255-cct) < _cctBlend) cw = 255;
|
||||
else cw = (cct * 255) / (255 - _cctBlend);
|
||||
|
||||
ww = (w * ww) / 255; //brightness scaling
|
||||
cw = (w * cw) / 255;
|
||||
#endif
|
||||
|
||||
switch (_type) {
|
||||
case TYPE_ANALOG_1CH: //one channel (white), 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_1CH: //one channel (white), relies on auto white calculation
|
||||
_data[0] = w;
|
||||
break;
|
||||
case TYPE_ANALOG_2CH: //warm white + cold white
|
||||
_data[1] = cw;
|
||||
_data[0] = ww;
|
||||
break;
|
||||
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
|
||||
// perhaps a non-linear adjustment would be in order. need to test
|
||||
_data[4] = cw;
|
||||
w = ww;
|
||||
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;
|
||||
_data[3] = w;
|
||||
case TYPE_ANALOG_3CH: //standard dumb RGB
|
||||
_data[0] = r; _data[1] = g; _data[2] = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//does no index check
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2]));
|
||||
if (!_valid) return 0;
|
||||
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
|
||||
}
|
||||
|
||||
void show() {
|
||||
if (!_valid) return;
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
uint8_t scaled = (_data[i] * _bri) / 255;
|
||||
@@ -288,15 +379,14 @@ class BusPwm : public Bus {
|
||||
}
|
||||
|
||||
uint8_t getPins(uint8_t* pinArray) {
|
||||
if (!_valid) return 0;
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
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();
|
||||
}
|
||||
@@ -315,13 +405,13 @@ class BusPwm : public Bus {
|
||||
void deallocatePins() {
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
|
||||
if (!pinManager.isPinOk(_pins[i])) continue;
|
||||
#ifdef ESP8266
|
||||
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
|
||||
#else
|
||||
if (_ledcStart < 16) ledcDetachPin(_pins[i]);
|
||||
#endif
|
||||
pinManager.deallocatePin(_pins[i]);
|
||||
}
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
pinManager.deallocateLedc(_ledcStart, numPins);
|
||||
@@ -329,6 +419,109 @@ class BusPwm : public Bus {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BusNetwork : public Bus {
|
||||
public:
|
||||
BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start) {
|
||||
_valid = false;
|
||||
// switch (bc.type) {
|
||||
// case TYPE_NET_ARTNET_RGB:
|
||||
// _rgbw = false;
|
||||
// _UDPtype = 2;
|
||||
// break;
|
||||
// case TYPE_NET_E131_RGB:
|
||||
// _rgbw = false;
|
||||
// _UDPtype = 1;
|
||||
// break;
|
||||
// case TYPE_NET_DDP_RGB:
|
||||
// _rgbw = false;
|
||||
// _UDPtype = 0;
|
||||
// break;
|
||||
// default:
|
||||
_rgbw = false;
|
||||
_UDPtype = bc.type - TYPE_NET_DDP_RGB;
|
||||
// break;
|
||||
// }
|
||||
_UDPchannels = _rgbw ? 4 : 3;
|
||||
_data = (byte *)malloc(bc.count * _UDPchannels);
|
||||
if (_data == nullptr) return;
|
||||
memset(_data, 0, bc.count * _UDPchannels);
|
||||
_len = bc.count;
|
||||
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
|
||||
_broadcastLock = false;
|
||||
_valid = true;
|
||||
};
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (!_valid || pix >= _len) return;
|
||||
if (isRgbw()) c = autoWhiteCalc(c);
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
uint16_t offset = pix * _UDPchannels;
|
||||
_data[offset] = R(c);
|
||||
_data[offset+1] = G(c);
|
||||
_data[offset+2] = B(c);
|
||||
if (_rgbw) _data[offset+3] = W(c);
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
if (!_valid || pix >= _len) return 0;
|
||||
uint16_t offset = pix * _UDPchannels;
|
||||
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0);
|
||||
}
|
||||
|
||||
void show() {
|
||||
if (!_valid || !canShow()) return;
|
||||
_broadcastLock = true;
|
||||
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw);
|
||||
_broadcastLock = false;
|
||||
}
|
||||
|
||||
inline bool canShow() {
|
||||
// this should be a return value from UDP routine if it is still sending data out
|
||||
return !_broadcastLock;
|
||||
}
|
||||
|
||||
inline void setBrightness(uint8_t b) {
|
||||
_bri = b;
|
||||
}
|
||||
|
||||
uint8_t getPins(uint8_t* pinArray) {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
pinArray[i] = _client[i];
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
inline bool isRgbw() {
|
||||
return _rgbw;
|
||||
}
|
||||
|
||||
inline uint16_t getLength() {
|
||||
return _len;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
_type = I_NONE;
|
||||
_valid = false;
|
||||
if (_data != nullptr) free(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
~BusNetwork() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private:
|
||||
IPAddress _client;
|
||||
uint8_t _bri = 255;
|
||||
uint8_t _UDPtype;
|
||||
uint8_t _UDPchannels;
|
||||
bool _rgbw;
|
||||
bool _broadcastLock;
|
||||
byte *_data;
|
||||
};
|
||||
|
||||
|
||||
class BusManager {
|
||||
public:
|
||||
BusManager() {
|
||||
@@ -339,7 +532,7 @@ class BusManager {
|
||||
static uint32_t memUsage(BusConfig &bc) {
|
||||
uint8_t type = bc.type;
|
||||
uint16_t len = bc.count;
|
||||
if (type < 32) {
|
||||
if (type > 15 && type < 32) {
|
||||
#ifdef ESP8266
|
||||
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
|
||||
if (type > 29) return len*20; //RGBW
|
||||
@@ -352,15 +545,16 @@ class BusManager {
|
||||
return len*6;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (type > 31 && type < 48) return 5;
|
||||
if (type > 31 && type < 48) return 5;
|
||||
if (type == 44 || type == 45) return len*4; //RGBW
|
||||
return len*3;
|
||||
return len*3; //RGB
|
||||
}
|
||||
|
||||
int add(BusConfig &bc) {
|
||||
if (numBusses >= WLED_MAX_BUSSES) return -1;
|
||||
if (IS_DIGITAL(bc.type)) {
|
||||
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
|
||||
busses[numBusses] = new BusNetwork(bc);
|
||||
} else if (IS_DIGITAL(bc.type)) {
|
||||
busses[numBusses] = new BusDigital(bc, numBusses);
|
||||
} else {
|
||||
busses[numBusses] = new BusPwm(bc);
|
||||
@@ -370,7 +564,7 @@ class BusManager {
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void removeAll() {
|
||||
//Serial.println("Removing all.");
|
||||
DEBUG_PRINTLN(F("Removing all."));
|
||||
//prevents crashes due to deleting busses while in use.
|
||||
while (!canAllShow()) yield();
|
||||
for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
|
||||
@@ -383,13 +577,18 @@ class BusManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
void setStatusPixel(uint32_t c) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
busses[i]->setStatusPixel(c);
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
if (pix < bstart || pix >= bstart + b->getLength()) continue;
|
||||
busses[i]->setPixelColor(pix - bstart, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +598,15 @@ class BusManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false) {
|
||||
if (cct > 255) cct = 255;
|
||||
if (cct >= 0) {
|
||||
//if white balance correction allowed, save as kelvin value instead of 0-255
|
||||
if (allowWBCorrection) cct = 1900 + (cct << 5);
|
||||
} else cct = -1;
|
||||
Bus::setCCT(cct);
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
@@ -425,23 +633,15 @@ class BusManager {
|
||||
return numBusses;
|
||||
}
|
||||
|
||||
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
|
||||
uint16_t getTotalLength() {
|
||||
uint16_t len = 0;
|
||||
for (uint8_t i=0; i<numBusses; i++ ) len += busses[i]->getLength();
|
||||
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
|
||||
#endif
|
||||
|
||||
@@ -36,66 +36,38 @@
|
||||
|
||||
/*** 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
|
||||
#define I_32_RN_NEO_3 17
|
||||
#define I_32_I0_NEO_3 18
|
||||
#define I_32_I1_NEO_3 19
|
||||
//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
|
||||
#define I_32_RN_NEO_4 20
|
||||
#define I_32_I0_NEO_4 21
|
||||
#define I_32_I1_NEO_4 22
|
||||
//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
|
||||
#define I_32_RN_400_3 23
|
||||
#define I_32_I0_400_3 24
|
||||
#define I_32_I1_400_3 25
|
||||
//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
|
||||
#define I_32_RN_TM1_4 26
|
||||
#define I_32_I0_TM1_4 27
|
||||
#define I_32_I1_TM1_4 28
|
||||
//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
|
||||
#define I_HS_DOT_3 29 //hardware SPI
|
||||
#define I_SS_DOT_3 30 //soft SPI
|
||||
|
||||
//LPD8806
|
||||
#define I_HS_LPD_3 59
|
||||
#define I_SS_LPD_3 60
|
||||
#define I_HS_LPD_3 31
|
||||
#define I_SS_LPD_3 32
|
||||
|
||||
//WS2801
|
||||
#define I_HS_WS1_3 61
|
||||
#define I_SS_WS1_3 62
|
||||
#define I_HS_WS1_3 33
|
||||
#define I_SS_WS1_3 34
|
||||
|
||||
//P9813
|
||||
#define I_HS_P98_3 63
|
||||
#define I_SS_P98_3 64
|
||||
#define I_HS_P98_3 35
|
||||
#define I_SS_P98_3 36
|
||||
|
||||
|
||||
/*** ESP8266 Neopixel methods ***/
|
||||
@@ -125,63 +97,46 @@
|
||||
/*** 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_RN_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod>
|
||||
#define B_32_I0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#define B_32_I1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod>
|
||||
#endif
|
||||
//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_RN_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod>
|
||||
#define B_32_I0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#define B_32_I1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod>
|
||||
#endif
|
||||
//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_RN_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod>
|
||||
#define B_32_I0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#define B_32_I1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod>
|
||||
#endif
|
||||
//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_RN_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method>
|
||||
#define B_32_I0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#define B_32_I1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method>
|
||||
#endif
|
||||
//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
|
||||
#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_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi40MhzMethod>
|
||||
//#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi20MhzMethod>
|
||||
//#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801SpiMethod> // 10MHz
|
||||
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi2MhzMethod> //slower, more compatible
|
||||
#define B_SS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Method>
|
||||
|
||||
//P9813
|
||||
@@ -225,46 +180,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Begin(); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break;
|
||||
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
|
||||
#endif
|
||||
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
|
||||
case I_HS_DOT_3: (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;
|
||||
@@ -277,7 +212,7 @@ class PolyBus {
|
||||
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) {
|
||||
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel) {
|
||||
void* busPtr = nullptr;
|
||||
switch (busType) {
|
||||
case I_NONE: break;
|
||||
@@ -300,46 +235,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -376,46 +291,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break;
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -449,46 +344,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: return (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -546,46 +421,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: (static_cast<B_32_RN_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;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_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;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_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;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_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;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -619,46 +474,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -693,46 +528,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_NEO_3: col = (static_cast<B_32_I0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: col = (static_cast<B_32_RN_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: col = (static_cast<B_32_RN_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -784,46 +599,26 @@ class PolyBus {
|
||||
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_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
|
||||
case I_32_I0_NEO_3: delete (static_cast<B_32_I0_NEO_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: delete (static_cast<B_32_RN_NEO_4*>(busPtr)); break;
|
||||
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_400_3: delete (static_cast<B_32_RN_400_3*>(busPtr)); break;
|
||||
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
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;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break;
|
||||
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
|
||||
#endif
|
||||
#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;
|
||||
@@ -872,18 +667,24 @@ class PolyBus {
|
||||
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;
|
||||
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
if (num > 9) return I_NONE;
|
||||
if (num > 7) offset = num -7;
|
||||
#else //ESP32 S2 only has 4 RMT channels
|
||||
if (num > 5) return I_NONE;
|
||||
if (num > 4) offset = num -4;
|
||||
#endif
|
||||
switch (busType) {
|
||||
case TYPE_WS2812_RGB:
|
||||
case TYPE_WS2812_WWA:
|
||||
return I_32_R0_NEO_3 + offset;
|
||||
return I_32_RN_NEO_3 + offset;
|
||||
case TYPE_SK6812_RGBW:
|
||||
return I_32_R0_NEO_4 + offset;
|
||||
return I_32_RN_NEO_4 + offset;
|
||||
case TYPE_WS2811_400KHZ:
|
||||
return I_32_R0_400_3 + offset;
|
||||
return I_32_RN_400_3 + offset;
|
||||
case TYPE_TM1814:
|
||||
return I_32_R0_TM1_4 + offset;
|
||||
return I_32_RN_TM1_4 + offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -5,15 +5,67 @@
|
||||
*/
|
||||
|
||||
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
||||
#define WLED_LONG_PRESS 600 //long press if button is released after held for at least 600ms
|
||||
#define WLED_DOUBLE_PRESS 350 //double press if another press within 350ms after a short press
|
||||
#define WLED_LONG_REPEATED_ACTION 300 //how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
|
||||
#define WLED_LONG_AP 6000 //how long the button needs to be held to activate WLED-AP
|
||||
|
||||
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
|
||||
|
||||
void shortPressAction(uint8_t b)
|
||||
{
|
||||
if (!macroButton[b])
|
||||
{
|
||||
toggleOnOff();
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BUTTON);
|
||||
if (!macroButton[b]) {
|
||||
switch (b) {
|
||||
case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroButton[b]);
|
||||
applyPreset(macroButton[b], CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, "short");
|
||||
}
|
||||
}
|
||||
|
||||
void longPressAction(uint8_t b)
|
||||
{
|
||||
if (!macroLongPress[b]) {
|
||||
switch (b) {
|
||||
case 0: _setRandomColor(false,true); break;
|
||||
default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, "long");
|
||||
}
|
||||
}
|
||||
|
||||
void doublePressAction(uint8_t b)
|
||||
{
|
||||
if (!macroDoublePress[b]) {
|
||||
switch (b) {
|
||||
//case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break; //instant short press on button 0 if no macro set
|
||||
default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, "double");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +81,7 @@ bool isButtonPressed(uint8_t i)
|
||||
if (digitalRead(btnPin[i]) == LOW) return true;
|
||||
break;
|
||||
case BTN_TYPE_PUSH_ACT_HIGH:
|
||||
case BTN_TYPE_SWITCH_ACT_HIGH:
|
||||
case BTN_TYPE_PIR_SENSOR:
|
||||
if (digitalRead(btnPin[i]) == HIGH) return true;
|
||||
break;
|
||||
case BTN_TYPE_TOUCH:
|
||||
@@ -43,6 +95,7 @@ bool isButtonPressed(uint8_t i)
|
||||
|
||||
void handleSwitch(uint8_t b)
|
||||
{
|
||||
// isButtonPressed() handles inverted/noninverted logic
|
||||
if (buttonPressedBefore[b] != isButtonPressed(b)) {
|
||||
buttonPressedTime[b] = millis();
|
||||
buttonPressedBefore[b] = !buttonPressedBefore[b];
|
||||
@@ -51,17 +104,26 @@ void handleSwitch(uint8_t 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]);
|
||||
if (!buttonPressedBefore[b]) { // on -> off
|
||||
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON);
|
||||
else { //turn on
|
||||
if (!bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BUTTON);}
|
||||
if (!bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
|
||||
}
|
||||
} else { //HIGH, rising edge, switch opened
|
||||
if (macroLongPress[b]) applyPreset(macroLongPress[b]);
|
||||
} else { // off -> on
|
||||
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
|
||||
else { //turn off
|
||||
if (bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BUTTON);}
|
||||
if (bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
|
||||
}
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
if (buttonType[b] == BTN_TYPE_PIR_SENSOR) sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)b);
|
||||
else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, !buttonPressedBefore[b] ? "off" : "on");
|
||||
}
|
||||
|
||||
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
|
||||
}
|
||||
}
|
||||
@@ -74,6 +136,9 @@ void handleAnalog(uint8_t b)
|
||||
#else
|
||||
uint16_t aRead = analogRead(btnPin[b]) >> 4; // convert 12bit read to 8bit
|
||||
#endif
|
||||
|
||||
if (buttonType[b] == BTN_TYPE_ANALOG_INVERTED) aRead = 255 - aRead;
|
||||
|
||||
// remove noise & reduce frequency of UI updates
|
||||
aRead &= 0xFC;
|
||||
|
||||
@@ -132,18 +197,14 @@ void handleAnalog(uint8_t 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);
|
||||
updateInterfaces(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);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
void handleButton()
|
||||
@@ -152,61 +213,64 @@ void handleButton()
|
||||
|
||||
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;
|
||||
if ((btnPin[b]<0 && !(buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED)) || buttonType[b] == BTN_TYPE_NONE) continue;
|
||||
#else
|
||||
if (btnPin[b]<0 || buttonType[b] == BTN_TYPE_NONE) continue;
|
||||
#endif
|
||||
|
||||
if (buttonType[b] == BTN_TYPE_ANALOG && millis() - lastRead > 250) { // button is not a button but a potentiometer
|
||||
if (usermods.handleButton(b)) continue; // did usermod handle buttons
|
||||
|
||||
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && 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)
|
||||
//button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
||||
if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) {
|
||||
handleSwitch(b); continue;
|
||||
}
|
||||
|
||||
//momentary button logic
|
||||
if (isButtonPressed(b)) //pressed
|
||||
{
|
||||
if (isButtonPressed(b)) { //pressed
|
||||
|
||||
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
|
||||
buttonPressedBefore[b] = true;
|
||||
|
||||
if (millis() - buttonPressedTime[b] > 600) //long press
|
||||
{
|
||||
if (!buttonLongPressed[b])
|
||||
{
|
||||
if (macroLongPress[b]) {applyPreset(macroLongPress[b]);}
|
||||
else _setRandomColor(false,true);
|
||||
|
||||
buttonLongPressed[b] = true;
|
||||
if (millis() - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
|
||||
if (!buttonLongPressed[b]) longPressAction(b);
|
||||
else if (b) { //repeatable action (~3 times per s) on button > 0
|
||||
longPressAction(b);
|
||||
buttonPressedTime[b] = millis() - WLED_LONG_REPEATED_ACTION; //300ms
|
||||
}
|
||||
buttonLongPressed[b] = true;
|
||||
}
|
||||
}
|
||||
else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released
|
||||
{
|
||||
|
||||
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
|
||||
|
||||
long dur = millis() - buttonPressedTime[b];
|
||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
|
||||
bool doublePress = buttonWaitTime[b];
|
||||
bool doublePress = buttonWaitTime[b]; //did we have a short press before?
|
||||
buttonWaitTime[b] = 0;
|
||||
|
||||
if (dur > 6000 && b==0) //long press on button 0
|
||||
{
|
||||
if (b == 0 && dur > WLED_LONG_AP) { //long press on button 0 (when released)
|
||||
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);
|
||||
} else if (!buttonLongPressed[b]) { //short press
|
||||
if (b == 0 && !macroDoublePress[b]) { //don't wait for double press on button 0 if no double press macro set
|
||||
shortPressAction(b);
|
||||
} else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0)
|
||||
if (doublePress) {
|
||||
doublePressAction(b);
|
||||
} else {
|
||||
buttonWaitTime[b] = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
buttonPressedBefore[b] = false;
|
||||
buttonLongPressed[b] = false;
|
||||
}
|
||||
|
||||
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 450 && !buttonPressedBefore[b])
|
||||
{
|
||||
//if 350ms elapsed since last short press release it is a short press
|
||||
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) {
|
||||
buttonWaitTime[b] = 0;
|
||||
shortPressAction(b);
|
||||
}
|
||||
@@ -235,8 +299,11 @@ void handleIO()
|
||||
#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);
|
||||
PinOwner ledPinOwner = pinManager.getPinOwner(LED_BUILTIN);
|
||||
if (!strip.isOffRefreshRequred && (ledPinOwner == PinOwner::None || ledPinOwner == PinOwner::BusDigital)) {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
}
|
||||
#endif
|
||||
if (rlyPin>=0) {
|
||||
pinMode(rlyPin, OUTPUT);
|
||||
|
||||
246
wled00/cfg.cpp
246
wled00/cfg.cpp
@@ -1,4 +1,5 @@
|
||||
#include "wled.h"
|
||||
#include "wled_ethernet.h"
|
||||
|
||||
/*
|
||||
* Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS.
|
||||
@@ -13,11 +14,19 @@ void getStringFromJson(char* dest, const char* src, size_t len) {
|
||||
}
|
||||
|
||||
bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
bool needsSave = false;
|
||||
//int rev_major = doc["rev"][0]; // 1
|
||||
//int rev_minor = doc["rev"][1]; // 0
|
||||
|
||||
//long vid = doc[F("vid")]; // 2010020
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet["type"]);
|
||||
// NOTE: Ethernet configuration takes priority over other use of pins
|
||||
WLED::instance().initEthernet();
|
||||
#endif
|
||||
|
||||
JsonObject id = doc["id"];
|
||||
getStringFromJson(cmDNS, id[F("mdns")], 33);
|
||||
getStringFromJson(serverDescription, id[F("name")], 33);
|
||||
@@ -53,11 +62,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
CJSON(apBehavior, ap[F("behav")]);
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet["type"]);
|
||||
#endif
|
||||
|
||||
/*
|
||||
JsonArray ap_ip = ap["ip"];
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
@@ -71,20 +75,21 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
JsonObject hw = doc[F("hw")];
|
||||
|
||||
// initialize LED pins and lengths prior to other HW
|
||||
// initialize LED pins and lengths prior to other HW (except for ethernet)
|
||||
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.rgbwMode, hw_led[F("rgbwm")]);
|
||||
Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | Bus::getAutoWhiteMode());
|
||||
CJSON(correctWB, hw_led["cct"]);
|
||||
CJSON(cctFromRgb, hw_led[F("cr")]);
|
||||
CJSON(strip.cctBlending, hw_led[F("cb")]);
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
if (fromFS || !ins.isNull()) {
|
||||
uint8_t s = 0; //bus iterator
|
||||
strip.isRgbw = false;
|
||||
uint8_t s = 0; // bus iterator
|
||||
busses.removeAll();
|
||||
uint32_t mem = 0;
|
||||
for (JsonObject elm : ins) {
|
||||
@@ -99,44 +104,39 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (i>4) break;
|
||||
}
|
||||
|
||||
uint16_t length = elm[F("len")];
|
||||
if (length==0) continue;
|
||||
uint16_t length = elm[F("len")] | 1;
|
||||
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;
|
||||
uint16_t start = elm["start"] | 0;
|
||||
if (length==0 || start + length > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop
|
||||
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);
|
||||
bool refresh = elm["ref"] | false;
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
s++;
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
|
||||
mem += busses.memUsage(bc);
|
||||
if (mem <= MAX_LED_MEMORY) busses.add(bc);
|
||||
mem += BusManager::memUsage(bc);
|
||||
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
|
||||
}
|
||||
strip.finalizeInit(ledCount);
|
||||
// finalization done in beginStrip()
|
||||
}
|
||||
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
||||
|
||||
// read multiple button configuration
|
||||
JsonArray hw_btn_ins = hw[F("btn")][F("ins")];
|
||||
JsonObject btn_obj = hw["btn"];
|
||||
JsonArray hw_btn_ins = btn_obj[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)) {
|
||||
if (pin > -1 && pinManager.allocatePin(pin, false, PinOwner::Button)) {
|
||||
btnPin[s] = pin;
|
||||
pinMode(btnPin[s], INPUT_PULLUP);
|
||||
} else {
|
||||
btnPin[s] = -1;
|
||||
}
|
||||
JsonArray hw_btn_ins_0_macros = btn[F("macros")];
|
||||
JsonArray hw_btn_ins_0_macros = btn["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]);
|
||||
@@ -152,20 +152,27 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
} else {
|
||||
// new install/missing configuration (button 0 has defaults)
|
||||
if (fromFS)
|
||||
for (uint8_t s=1; s<WLED_MAX_BUTTONS; s++) {
|
||||
if (fromFS) {
|
||||
// relies upon only being called once with fromFS == true, which is currently true.
|
||||
uint8_t s = 0;
|
||||
if (pinManager.allocatePin(btnPin[0], false, PinOwner::Button)) { // initialized to #define value BTNPIN, or zero if not defined(!)
|
||||
++s; // do not clear default button if allocated successfully
|
||||
}
|
||||
for (; 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")]);
|
||||
CJSON(touchThreshold,btn_obj[F("tt")]);
|
||||
CJSON(buttonPublishMqtt,btn_obj["mqtt"]);
|
||||
|
||||
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
|
||||
if (hw_ir_pin > -2) {
|
||||
if (pinManager.allocatePin(hw_ir_pin,false)) {
|
||||
if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) {
|
||||
irPin = hw_ir_pin;
|
||||
} else {
|
||||
irPin = -1;
|
||||
@@ -176,7 +183,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject relay = hw[F("relay")];
|
||||
int hw_relay_pin = relay["pin"] | -2;
|
||||
if (hw_relay_pin > -2) {
|
||||
if (pinManager.allocatePin(hw_relay_pin,true)) {
|
||||
if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) {
|
||||
rlyPin = hw_relay_pin;
|
||||
pinMode(rlyPin, OUTPUT);
|
||||
} else {
|
||||
@@ -192,9 +199,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject light = doc[F("light")];
|
||||
CJSON(briMultiplier, light[F("scale-bri")]);
|
||||
CJSON(strip.paletteBlend, light[F("pal-mode")]);
|
||||
CJSON(autoSegments, light[F("aseg")]);
|
||||
|
||||
float light_gc_bri = light[F("gc")]["bri"];
|
||||
float light_gc_col = light[F("gc")]["col"]; // 2.8
|
||||
float light_gc_bri = light["gc"]["bri"];
|
||||
float light_gc_col = light["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;
|
||||
@@ -213,10 +221,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;
|
||||
|
||||
CJSON(nightlightTargetBri, light_nl[F("tbri")]);
|
||||
CJSON(macroNl, light_nl[F("macro")]);
|
||||
CJSON(macroNl, light_nl["macro"]);
|
||||
|
||||
JsonObject def = doc[F("def")];
|
||||
CJSON(bootPreset, def[F("ps")]);
|
||||
CJSON(bootPreset, def["ps"]);
|
||||
CJSON(turnOnAtBoot, def["on"]); // true
|
||||
CJSON(briS, def["bri"]); // 128
|
||||
|
||||
@@ -229,7 +237,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject if_sync_recv = if_sync["recv"];
|
||||
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
|
||||
CJSON(receiveNotificationColor, if_sync_recv["col"]);
|
||||
CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
|
||||
CJSON(receiveNotificationEffects, if_sync_recv["fx"]);
|
||||
CJSON(receiveGroups, if_sync_recv["grp"]);
|
||||
//! following line might be a problem if called after boot
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
|
||||
@@ -237,11 +246,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
prev = notifyDirectDefault;
|
||||
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
|
||||
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(notifyButton, if_sync_send["btn"]);
|
||||
CJSON(notifyAlexa, if_sync_send["va"]);
|
||||
CJSON(notifyHue, if_sync_send["hue"]);
|
||||
CJSON(notifyMacro, if_sync_send["macro"]);
|
||||
CJSON(notifyTwice, if_sync_send[F("twice")]);
|
||||
CJSON(syncGroups, if_sync_send["grp"]);
|
||||
|
||||
JsonObject if_nodes = interfaces["nodes"];
|
||||
CJSON(nodeListEnabled, if_nodes[F("list")]);
|
||||
@@ -250,6 +260,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject if_live = interfaces["live"];
|
||||
CJSON(receiveDirect, if_live["en"]);
|
||||
CJSON(e131Port, if_live["port"]); // 5568
|
||||
if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation
|
||||
CJSON(e131Multicast, if_live[F("mc")]);
|
||||
|
||||
JsonObject if_live_dmx = if_live[F("dmx")];
|
||||
@@ -264,10 +275,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
|
||||
CJSON(arlsOffset, if_live[F("offset")]); // 0
|
||||
|
||||
CJSON(alexaEnabled, interfaces[F("va")][F("alexa")]); // false
|
||||
CJSON(alexaEnabled, interfaces["va"][F("alexa")]); // false
|
||||
|
||||
CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]);
|
||||
CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]);
|
||||
CJSON(macroAlexaOn, interfaces["va"]["macros"][0]);
|
||||
CJSON(macroAlexaOff, interfaces["va"]["macros"][1]);
|
||||
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
const char* apikey = interfaces["blynk"][F("token")] | "Hidden";
|
||||
@@ -294,7 +305,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
JsonObject if_hue = interfaces[F("hue")];
|
||||
JsonObject if_hue = interfaces["hue"];
|
||||
CJSON(huePollingEnabled, if_hue["en"]);
|
||||
CJSON(huePollLightId, if_hue["id"]);
|
||||
tdd = if_hue[F("iv")] | -1;
|
||||
@@ -342,17 +353,17 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(countdownHour, cntdwn_goal[3]);
|
||||
CJSON(countdownMin, cntdwn_goal[4]);
|
||||
CJSON(countdownSec, cntdwn_goal[5]);
|
||||
CJSON(macroCountdown, cntdwn[F("macro")]);
|
||||
CJSON(macroCountdown, cntdwn["macro"]);
|
||||
setCountdown();
|
||||
|
||||
JsonArray timers = tm[F("ins")];
|
||||
JsonArray timers = tm["ins"];
|
||||
uint8_t it = 0;
|
||||
for (JsonObject timer : timers) {
|
||||
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["min"]);
|
||||
CJSON(timerMacro[it], timer[F("macro")]);
|
||||
CJSON(timerMacro[it], timer["macro"]);
|
||||
|
||||
byte dowPrev = timerWeekday[it];
|
||||
//note: act is currently only 0 or 1.
|
||||
@@ -385,26 +396,25 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject dmx = doc["dmx"];
|
||||
CJSON(DMXChannels, dmx[F("chan")]);
|
||||
CJSON(DMXGap,dmx[F("gap")]);
|
||||
CJSON(DMXStart, dmx[F("start")]);
|
||||
CJSON(DMXStart, dmx["start"]);
|
||||
CJSON(DMXStartLED,dmx[F("start-led")]);
|
||||
|
||||
JsonArray dmx_fixmap = dmx[F("fixmap")];
|
||||
it = 0;
|
||||
for (int i : dmx_fixmap) {
|
||||
if (it > 14) break;
|
||||
for (int i = 0; i < dmx_fixmap.size(); i++) {
|
||||
if (i > 14) break;
|
||||
CJSON(DMXFixtureMap[i],dmx_fixmap[i]);
|
||||
it++;
|
||||
}
|
||||
|
||||
CJSON(e131ProxyUniverse, dmx[F("e131proxy")]);
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN(F("Starting usermod config."));
|
||||
JsonObject usermods_settings = doc["um"];
|
||||
if (!usermods_settings.isNull()) {
|
||||
bool allComplete = usermods.readFromConfig(usermods_settings);
|
||||
if (!allComplete && fromFS) serializeConfig();
|
||||
needsSave = !usermods.readFromConfig(usermods_settings);
|
||||
}
|
||||
|
||||
if (fromFS) return false;
|
||||
if (fromFS) return needsSave;
|
||||
doReboot = doc[F("rb")] | doReboot;
|
||||
return (doc["sv"] | true);
|
||||
}
|
||||
@@ -416,17 +426,27 @@ void deserializeConfigFromFS() {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WLED_USE_DYNAMIC_JSON
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
#else
|
||||
if (!requestJSONBufferLock(1)) return;
|
||||
#endif
|
||||
|
||||
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();
|
||||
releaseJSONBufferLock();
|
||||
return;
|
||||
}
|
||||
|
||||
deserializeConfig(doc.as<JsonObject>(), true);
|
||||
// NOTE: This routine deserializes *and* applies the configuration
|
||||
// Therefore, must also initialize ethernet from this function
|
||||
bool needsSave = deserializeConfig(doc.as<JsonObject>(), true);
|
||||
releaseJSONBufferLock();
|
||||
|
||||
if (needsSave) serializeConfig(); // usermods required new prameters
|
||||
}
|
||||
|
||||
void serializeConfig() {
|
||||
@@ -434,7 +454,11 @@ void serializeConfig() {
|
||||
|
||||
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
|
||||
|
||||
#ifdef WLED_USE_DYNAMIC_JSON
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
#else
|
||||
if (!requestJSONBufferLock(2)) return;
|
||||
#endif
|
||||
|
||||
JsonArray rev = doc.createNestedArray("rev");
|
||||
rev.add(1); //major settings revision
|
||||
@@ -480,20 +504,42 @@ void serializeConfig() {
|
||||
|
||||
JsonObject wifi = doc.createNestedObject("wifi");
|
||||
wifi[F("sleep")] = !noWifiSleep;
|
||||
wifi[F("phy")] = 1;
|
||||
//wifi[F("phy")] = 1;
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc.createNestedObject("eth");
|
||||
ethernet["type"] = ethernetType;
|
||||
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
|
||||
JsonArray pins = ethernet.createNestedArray("pin");
|
||||
for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) pins.add(esp32_nonconfigurable_ethernet_pins[p].pin);
|
||||
if (ethernetBoards[ethernetType].eth_power>=0) pins.add(ethernetBoards[ethernetType].eth_power);
|
||||
if (ethernetBoards[ethernetType].eth_mdc>=0) pins.add(ethernetBoards[ethernetType].eth_mdc);
|
||||
if (ethernetBoards[ethernetType].eth_mdio>=0) pins.add(ethernetBoards[ethernetType].eth_mdio);
|
||||
switch (ethernetBoards[ethernetType].eth_clk_mode) {
|
||||
case ETH_CLOCK_GPIO0_IN:
|
||||
case ETH_CLOCK_GPIO0_OUT:
|
||||
pins.add(0);
|
||||
break;
|
||||
case ETH_CLOCK_GPIO16_OUT:
|
||||
pins.add(16);
|
||||
break;
|
||||
case ETH_CLOCK_GPIO17_OUT:
|
||||
pins.add(17);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
JsonObject hw = doc.createNestedObject("hw");
|
||||
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = ledCount;
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade
|
||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led[F("rgbwm")] = strip.rgbwMode;
|
||||
hw_led["cct"] = correctWB;
|
||||
hw_led[F("cr")] = cctFromRgb;
|
||||
hw_led[F("cb")] = strip.cctBlending;
|
||||
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
@@ -501,7 +547,7 @@ void serializeConfig() {
|
||||
Bus *bus = busses.getBus(s);
|
||||
if (!bus || bus->getLength()==0) break;
|
||||
JsonObject ins = hw_led_ins.createNestedObject();
|
||||
ins[F("start")] = bus->getStart();
|
||||
ins["start"] = bus->getStart();
|
||||
ins[F("len")] = bus->getLength();
|
||||
JsonArray ins_pin = ins.createNestedArray("pin");
|
||||
uint8_t pins[5];
|
||||
@@ -510,7 +556,9 @@ void serializeConfig() {
|
||||
ins[F("order")] = bus->getColorOrder();
|
||||
ins["rev"] = bus->reversed;
|
||||
ins[F("skip")] = bus->skippedLeds();
|
||||
ins["type"] = bus->getType();
|
||||
ins["type"] = bus->getType() & 0x7F;
|
||||
ins["ref"] = bus->isOffRefreshRequired();
|
||||
ins[F("rgbw")] = bus->isRgbw();
|
||||
}
|
||||
|
||||
// button(s)
|
||||
@@ -518,33 +566,24 @@ void serializeConfig() {
|
||||
hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used)
|
||||
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
|
||||
|
||||
// there is always at least one button
|
||||
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
|
||||
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[0]);
|
||||
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
|
||||
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();
|
||||
// configuration for all buttons
|
||||
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
|
||||
JsonObject 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");
|
||||
JsonArray 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");
|
||||
JsonArray 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;
|
||||
hw_btn["mqtt"] = buttonPublishMqtt;
|
||||
|
||||
JsonObject hw_ir = hw.createNestedObject("ir");
|
||||
hw_ir["pin"] = irPin;
|
||||
hw_ir[F("type")] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
|
||||
hw_ir["type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
|
||||
|
||||
JsonObject hw_relay = hw.createNestedObject(F("relay"));
|
||||
hw_relay["pin"] = rlyPin;
|
||||
@@ -556,6 +595,7 @@ void serializeConfig() {
|
||||
JsonObject light = doc.createNestedObject(F("light"));
|
||||
light[F("scale-bri")] = briMultiplier;
|
||||
light[F("pal-mode")] = strip.paletteBlend;
|
||||
light[F("aseg")] = autoSegments;
|
||||
|
||||
JsonObject light_gc = light.createNestedObject("gc");
|
||||
light_gc["bri"] = (strip.gammaCorrectBri) ? 2.8 : 1.0;
|
||||
@@ -570,10 +610,10 @@ void serializeConfig() {
|
||||
light_nl[F("mode")] = nightlightMode;
|
||||
light_nl["dur"] = nightlightDelayMinsDefault;
|
||||
light_nl[F("tbri")] = nightlightTargetBri;
|
||||
light_nl[F("macro")] = macroNl;
|
||||
light_nl["macro"] = macroNl;
|
||||
|
||||
JsonObject def = doc.createNestedObject("def");
|
||||
def[F("ps")] = bootPreset;
|
||||
def["ps"] = bootPreset;
|
||||
def["on"] = turnOnAtBoot;
|
||||
def["bri"] = briS;
|
||||
|
||||
@@ -587,14 +627,16 @@ void serializeConfig() {
|
||||
if_sync_recv["bri"] = receiveNotificationBrightness;
|
||||
if_sync_recv["col"] = receiveNotificationColor;
|
||||
if_sync_recv["fx"] = receiveNotificationEffects;
|
||||
if_sync_recv["grp"] = receiveGroups;
|
||||
|
||||
JsonObject if_sync_send = if_sync.createNestedObject("send");
|
||||
if_sync_send[F("dir")] = notifyDirect;
|
||||
if_sync_send[F("btn")] = notifyButton;
|
||||
if_sync_send[F("va")] = notifyAlexa;
|
||||
if_sync_send[F("hue")] = notifyHue;
|
||||
if_sync_send[F("macro")] = notifyMacro;
|
||||
if_sync_send["btn"] = notifyButton;
|
||||
if_sync_send["va"] = notifyAlexa;
|
||||
if_sync_send["hue"] = notifyHue;
|
||||
if_sync_send["macro"] = notifyMacro;
|
||||
if_sync_send[F("twice")] = notifyTwice;
|
||||
if_sync_send["grp"] = syncGroups;
|
||||
|
||||
JsonObject if_nodes = interfaces.createNestedObject("nodes");
|
||||
if_nodes[F("list")] = nodeListEnabled;
|
||||
@@ -610,6 +652,7 @@ void serializeConfig() {
|
||||
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
|
||||
if_live_dmx[F("addr")] = DMXAddress;
|
||||
if_live_dmx[F("mode")] = DMXMode;
|
||||
|
||||
if_live[F("timeout")] = realtimeTimeoutMs / 100;
|
||||
if_live[F("maxbri")] = arlsForceMaxBri;
|
||||
if_live[F("no-gc")] = arlsDisableGammaCorrection;
|
||||
@@ -685,7 +728,7 @@ void serializeConfig() {
|
||||
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;
|
||||
cntdwn["macro"] = macroCountdown;
|
||||
|
||||
JsonArray timers_ins = timers.createNestedArray("ins");
|
||||
|
||||
@@ -695,7 +738,7 @@ void serializeConfig() {
|
||||
timers_ins0["en"] = (timerWeekday[i] & 0x01);
|
||||
timers_ins0[F("hour")] = timerHours[i];
|
||||
timers_ins0["min"] = timerMinutes[i];
|
||||
timers_ins0[F("macro")] = timerMacro[i];
|
||||
timers_ins0["macro"] = timerMacro[i];
|
||||
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
|
||||
}
|
||||
|
||||
@@ -709,12 +752,15 @@ void serializeConfig() {
|
||||
JsonObject dmx = doc.createNestedObject("dmx");
|
||||
dmx[F("chan")] = DMXChannels;
|
||||
dmx[F("gap")] = DMXGap;
|
||||
dmx[F("start")] = DMXStart;
|
||||
dmx["start"] = DMXStart;
|
||||
dmx[F("start-led")] = DMXStartLED;
|
||||
|
||||
JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap"));
|
||||
for (byte i = 0; i < 15; i++)
|
||||
for (byte i = 0; i < 15; i++) {
|
||||
dmx_fixmap.add(DMXFixtureMap[i]);
|
||||
}
|
||||
|
||||
dmx[F("e131proxy")] = e131ProxyUniverse;
|
||||
#endif
|
||||
|
||||
JsonObject usermods_settings = doc.createNestedObject("um");
|
||||
@@ -723,16 +769,24 @@ void serializeConfig() {
|
||||
File f = WLED_FS.open("/cfg.json", "w");
|
||||
if (f) serializeJson(doc, f);
|
||||
f.close();
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
//settings in /wsec.json, not accessible via webserver, for passwords and tokens
|
||||
bool deserializeConfigSec() {
|
||||
DEBUG_PRINTLN(F("Reading settings from /wsec.json..."));
|
||||
|
||||
#ifdef WLED_USE_DYNAMIC_JSON
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
#else
|
||||
if (!requestJSONBufferLock(3)) return false;
|
||||
#endif
|
||||
|
||||
bool success = readObjectFromFile("/wsec.json", nullptr, &doc);
|
||||
if (!success) return false;
|
||||
if (!success) {
|
||||
releaseJSONBufferLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
|
||||
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
|
||||
@@ -755,7 +809,7 @@ bool deserializeConfigSec() {
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47);
|
||||
getStringFromJson(hueApiKey, interfaces["hue"][F("key")], 47);
|
||||
#endif
|
||||
|
||||
JsonObject ota = doc["ota"];
|
||||
@@ -764,13 +818,18 @@ bool deserializeConfigSec() {
|
||||
CJSON(wifiLock, ota[F("lock-wifi")]);
|
||||
CJSON(aOtaEnabled, ota[F("aota")]);
|
||||
|
||||
releaseJSONBufferLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void serializeConfigSec() {
|
||||
DEBUG_PRINTLN(F("Writing settings to /wsec.json..."));
|
||||
|
||||
#ifdef WLED_USE_DYNAMIC_JSON
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
#else
|
||||
if (!requestJSONBufferLock(4)) return;
|
||||
#endif
|
||||
|
||||
JsonObject nw = doc.createNestedObject("nw");
|
||||
|
||||
@@ -805,4 +864,5 @@ void serializeConfigSec() {
|
||||
File f = WLED_FS.open("/wsec.json", "w");
|
||||
if (f) serializeJson(doc, f);
|
||||
f.close();
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
@@ -6,36 +6,25 @@
|
||||
|
||||
void colorFromUint32(uint32_t in, bool secondary)
|
||||
{
|
||||
if (secondary) {
|
||||
colSec[3] = in >> 24 & 0xFF;
|
||||
colSec[0] = in >> 16 & 0xFF;
|
||||
colSec[1] = in >> 8 & 0xFF;
|
||||
colSec[2] = in & 0xFF;
|
||||
} else {
|
||||
col[3] = in >> 24 & 0xFF;
|
||||
col[0] = in >> 16 & 0xFF;
|
||||
col[1] = in >> 8 & 0xFF;
|
||||
col[2] = in & 0xFF;
|
||||
}
|
||||
byte *_col = secondary ? colSec : col;
|
||||
_col[0] = R(in);
|
||||
_col[1] = G(in);
|
||||
_col[2] = B(in);
|
||||
_col[3] = W(in);
|
||||
}
|
||||
|
||||
//load a color without affecting the white channel
|
||||
void colorFromUint24(uint32_t in, bool secondary)
|
||||
{
|
||||
if (secondary) {
|
||||
colSec[0] = in >> 16 & 0xFF;
|
||||
colSec[1] = in >> 8 & 0xFF;
|
||||
colSec[2] = in & 0xFF;
|
||||
} else {
|
||||
col[0] = in >> 16 & 0xFF;
|
||||
col[1] = in >> 8 & 0xFF;
|
||||
col[2] = in & 0xFF;
|
||||
}
|
||||
byte *_col = secondary ? colSec : col;
|
||||
_col[0] = R(in);
|
||||
_col[1] = G(in);
|
||||
_col[2] = B(in);
|
||||
}
|
||||
|
||||
//store color components in uint32_t
|
||||
uint32_t colorFromRgbw(byte* rgbw) {
|
||||
return (rgbw[0] << 16) + (rgbw[1] << 8) + rgbw[2] + (rgbw[3] << 24);
|
||||
return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
|
||||
}
|
||||
|
||||
//relatively change white brightness, minumum A=5
|
||||
@@ -64,9 +53,9 @@ 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 (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
|
||||
}
|
||||
|
||||
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
|
||||
void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
|
||||
{
|
||||
float r = 0, g = 0, b = 0;
|
||||
@@ -84,7 +73,7 @@ void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
|
||||
g = round(288.1221695283 * pow((temp - 60), -0.0755148492));
|
||||
b = 255;
|
||||
}
|
||||
g += 15; //mod by Aircoookie, a bit less accurate but visibly less pinkish
|
||||
//g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish
|
||||
rgb[0] = (uint8_t) constrain(r, 0, 255);
|
||||
rgb[1] = (uint8_t) constrain(g, 0, 255);
|
||||
rgb[2] = (uint8_t) constrain(b, 0, 255);
|
||||
@@ -111,7 +100,6 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins
|
||||
} else {
|
||||
rgb[0]=237;rgb[1]=255;rgb[2]=239;//150
|
||||
}
|
||||
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
@@ -169,7 +157,6 @@ 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 (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)
|
||||
@@ -197,10 +184,10 @@ void colorFromDecOrHexString(byte* rgb, char* in)
|
||||
c = strtoul(in, NULL, 10);
|
||||
}
|
||||
|
||||
rgb[3] = (c >> 24) & 0xFF;
|
||||
rgb[0] = (c >> 16) & 0xFF;
|
||||
rgb[1] = (c >> 8) & 0xFF;
|
||||
rgb[2] = c & 0xFF;
|
||||
rgb[0] = R(c);
|
||||
rgb[1] = G(c);
|
||||
rgb[2] = B(c);
|
||||
rgb[3] = W(c);
|
||||
}
|
||||
|
||||
//contrary to the colorFromDecOrHexString() function, this uses the more standard RRGGBB / RRGGBBWW order
|
||||
@@ -212,14 +199,14 @@ bool colorFromHexString(byte* rgb, const char* in) {
|
||||
uint32_t c = strtoul(in, NULL, 16);
|
||||
|
||||
if (inputSize == 6) {
|
||||
rgb[0] = (c >> 16) & 0xFF;
|
||||
rgb[1] = (c >> 8) & 0xFF;
|
||||
rgb[2] = c & 0xFF;
|
||||
rgb[0] = (c >> 16);
|
||||
rgb[1] = (c >> 8);
|
||||
rgb[2] = c ;
|
||||
} else {
|
||||
rgb[0] = (c >> 24) & 0xFF;
|
||||
rgb[1] = (c >> 16) & 0xFF;
|
||||
rgb[2] = (c >> 8) & 0xFF;
|
||||
rgb[3] = c & 0xFF;
|
||||
rgb[0] = (c >> 24);
|
||||
rgb[1] = (c >> 16);
|
||||
rgb[2] = (c >> 8);
|
||||
rgb[3] = c ;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -236,11 +223,80 @@ float maxf (float v, float w)
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
uint32_t colorRGBtoRGBW(uint32_t c)
|
||||
{
|
||||
byte rgb[4];
|
||||
rgb[0] = R(c);
|
||||
rgb[1] = G(c);
|
||||
rgb[2] = B(c);
|
||||
rgb[3] = W(c);
|
||||
colorRGBtoRGBW(rgb);
|
||||
return RGBW32(rgb[0], rgb[1], rgb[2], rgb[3]);
|
||||
}
|
||||
|
||||
void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
|
||||
{
|
||||
float low = minf(rgb[0],minf(rgb[1],rgb[2]));
|
||||
float high = maxf(rgb[0],maxf(rgb[1],rgb[2]));
|
||||
if (high < 0.1f) return;
|
||||
float sat = 100.0f * ((high - low) / high);; // maximum saturation is 100 (corrected from 255)
|
||||
float sat = 100.0f * ((high - low) / high); // maximum saturation is 100 (corrected from 255)
|
||||
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
|
||||
}
|
||||
*/
|
||||
|
||||
byte correctionRGB[4] = {0,0,0,0};
|
||||
uint16_t lastKelvin = 0;
|
||||
|
||||
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
|
||||
{
|
||||
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
|
||||
if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB
|
||||
lastKelvin = kelvin;
|
||||
byte rgbw[4];
|
||||
rgbw[0] = ((uint16_t) correctionRGB[0] * R(rgb)) /255; // correct R
|
||||
rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
|
||||
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
|
||||
rgbw[3] = W(rgb);
|
||||
return colorFromRgbw(rgbw);
|
||||
}
|
||||
|
||||
//approximates a Kelvin color temperature from an RGB color.
|
||||
//this does no check for the "whiteness" of the color,
|
||||
//so should be used combined with a saturation check (as done by auto-white)
|
||||
//values from http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html (10deg)
|
||||
//equation spreadsheet at https://bit.ly/30RkHaN
|
||||
//accuracy +-50K from 1900K up to 8000K
|
||||
//minimum returned: 1900K, maximum returned: 10091K (range of 8192)
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb) {
|
||||
//if not either red or blue is 255, color is dimmed. Scale up
|
||||
uint8_t r = R(rgb), b = B(rgb);
|
||||
if (r == b) return 6550; //red == blue at about 6600K (also can't go further if both R and B are 0)
|
||||
|
||||
if (r > b) {
|
||||
//scale blue up as if red was at 255
|
||||
uint16_t scale = 0xFFFF / r; //get scale factor (range 257-65535)
|
||||
b = ((uint16_t)b * scale) >> 8;
|
||||
//For all temps K<6600 R is bigger than B (for full bri colors R=255)
|
||||
//-> Use 9 linear approximations for blackbody radiation blue values from 2000-6600K (blue is always 0 below 2000K)
|
||||
if (b < 33) return 1900 + b *6;
|
||||
if (b < 72) return 2100 + (b-33) *10;
|
||||
if (b < 101) return 2492 + (b-72) *14;
|
||||
if (b < 132) return 2900 + (b-101) *16;
|
||||
if (b < 159) return 3398 + (b-132) *19;
|
||||
if (b < 186) return 3906 + (b-159) *22;
|
||||
if (b < 210) return 4500 + (b-186) *25;
|
||||
if (b < 230) return 5100 + (b-210) *30;
|
||||
return 5700 + (b-230) *34;
|
||||
} else {
|
||||
//scale red up as if blue was at 255
|
||||
uint16_t scale = 0xFFFF / b; //get scale factor (range 257-65535)
|
||||
r = ((uint16_t)r * scale) >> 8;
|
||||
//For all temps K>6600 B is bigger than R (for full bri colors B=255)
|
||||
//-> Use 2 linear approximations for blackbody radiation red values from 6600-10091K (blue is always 0 below 2000K)
|
||||
if (r > 225) return 6600 + (254-r) *50;
|
||||
uint16_t k = 8080 + (225-r) *86;
|
||||
return (k > 10091) ? 10091 : k;
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,12 @@
|
||||
#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h"
|
||||
#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h"
|
||||
#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h"
|
||||
#define USERMOD_ID_BATTERY_STATUS_BASIC 18 //Usermod "usermod_v2_battery_status_basic.h"
|
||||
#define USERMOD_ID_PWM_FAN 19 //Usermod "usermod_PWM_fan.h"
|
||||
#define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h"
|
||||
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
|
||||
#define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h"
|
||||
#define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
@@ -66,17 +72,18 @@
|
||||
#define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec
|
||||
|
||||
//Notifier callMode
|
||||
#define NOTIFIER_CALL_MODE_INIT 0 //no updates on init, can be used to disable updates
|
||||
#define NOTIFIER_CALL_MODE_DIRECT_CHANGE 1
|
||||
#define NOTIFIER_CALL_MODE_BUTTON 2
|
||||
#define NOTIFIER_CALL_MODE_NOTIFICATION 3
|
||||
#define NOTIFIER_CALL_MODE_NIGHTLIGHT 4
|
||||
#define NOTIFIER_CALL_MODE_NO_NOTIFY 5
|
||||
#define NOTIFIER_CALL_MODE_FX_CHANGED 6 //no longer used
|
||||
#define NOTIFIER_CALL_MODE_HUE 7
|
||||
#define NOTIFIER_CALL_MODE_PRESET_CYCLE 8
|
||||
#define NOTIFIER_CALL_MODE_BLYNK 9
|
||||
#define NOTIFIER_CALL_MODE_ALEXA 10
|
||||
#define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates
|
||||
#define CALL_MODE_DIRECT_CHANGE 1
|
||||
#define CALL_MODE_BUTTON 2
|
||||
#define CALL_MODE_NOTIFICATION 3
|
||||
#define CALL_MODE_NIGHTLIGHT 4
|
||||
#define CALL_MODE_NO_NOTIFY 5
|
||||
#define CALL_MODE_FX_CHANGED 6 //no longer used
|
||||
#define CALL_MODE_HUE 7
|
||||
#define CALL_MODE_PRESET_CYCLE 8
|
||||
#define CALL_MODE_BLYNK 9
|
||||
#define CALL_MODE_ALEXA 10
|
||||
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
|
||||
|
||||
//RGB to RGBW conversion mode
|
||||
#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider
|
||||
@@ -110,13 +117,17 @@
|
||||
#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels)
|
||||
#define DMX_MODE_MULTIPLE_RGBW 6 //every LED is addressed with its own RGBW (ledCount * 4 channels)
|
||||
|
||||
//Light capability byte (unused) 0bRRCCTTTT
|
||||
//Light capability byte (unused) 0bRCCCTTTT
|
||||
//bits 0/1/2/3: specifies a type of LED driver. A single "driver" may have different chip models but must have the same protocol/behavior
|
||||
//bits 4/5: specifies the class of LED driver - 0b00 (dec. 0-15) unconfigured/reserved
|
||||
// - 0b01 (dec. 16-31) digital (data pin only)
|
||||
// - 0b10 (dec. 32-47) analog (PWM)
|
||||
// - 0b11 (dec. 48-63) digital (data + clock / SPI)
|
||||
//bits 6/7 are reserved and set to 0b00
|
||||
//bits 4/5/6: specifies the class of LED driver - 0b000 (dec. 0-15) unconfigured/reserved
|
||||
// - 0b001 (dec. 16-31) digital (data pin only)
|
||||
// - 0b010 (dec. 32-47) analog (PWM)
|
||||
// - 0b011 (dec. 48-63) digital (data + clock / SPI)
|
||||
// - 0b100 (dec. 64-79) unused/reserved
|
||||
// - 0b101 (dec. 80-95) virtual network busses
|
||||
// - 0b110 (dec. 96-111) unused/reserved
|
||||
// - 0b111 (dec. 112-127) unused/reserved
|
||||
//bit 7 is reserved and set to 0
|
||||
|
||||
#define TYPE_NONE 0 //light is not configured
|
||||
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
|
||||
@@ -140,6 +151,10 @@
|
||||
#define TYPE_APA102 51
|
||||
#define TYPE_LPD8806 52
|
||||
#define TYPE_P9813 53
|
||||
//Network types (master broadcast) (80-95)
|
||||
#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus)
|
||||
#define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus)
|
||||
#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus)
|
||||
|
||||
#define IS_DIGITAL(t) ((t) & 0x10) //digital are 16-31 and 48-63
|
||||
#define IS_PWM(t) ((t) > 40 && (t) < 46)
|
||||
@@ -161,9 +176,10 @@
|
||||
#define BTN_TYPE_PUSH 2
|
||||
#define BTN_TYPE_PUSH_ACT_HIGH 3
|
||||
#define BTN_TYPE_SWITCH 4
|
||||
#define BTN_TYPE_SWITCH_ACT_HIGH 5
|
||||
#define BTN_TYPE_PIR_SENSOR 5
|
||||
#define BTN_TYPE_TOUCH 6
|
||||
#define BTN_TYPE_ANALOG 7
|
||||
#define BTN_TYPE_ANALOG_INVERTED 8
|
||||
|
||||
//Ethernet board types
|
||||
#define WLED_NUM_ETH_TYPES 7
|
||||
@@ -194,6 +210,14 @@
|
||||
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
|
||||
#define SEG_OPTION_TRANSITIONAL 7
|
||||
|
||||
//Segment differs return byte
|
||||
#define SEG_DIFFERS_BRI 0x01
|
||||
#define SEG_DIFFERS_OPT 0x02
|
||||
#define SEG_DIFFERS_COL 0x04
|
||||
#define SEG_DIFFERS_FX 0x08
|
||||
#define SEG_DIFFERS_BOUNDS 0x10
|
||||
#define SEG_DIFFERS_GSO 0x20
|
||||
|
||||
//Playlist option byte
|
||||
#define PL_OPTION_SHUFFLE 0x01
|
||||
|
||||
@@ -204,6 +228,7 @@
|
||||
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
|
||||
#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached
|
||||
#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist
|
||||
#define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist
|
||||
#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured
|
||||
#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented)
|
||||
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)
|
||||
@@ -218,10 +243,10 @@
|
||||
|
||||
#define NTP_PACKET_SIZE 48
|
||||
|
||||
// maximum number of LEDs - more than 1500 LEDs (or 500 DMA "LEDPIN 3" driven ones) will cause a low memory condition on ESP8266
|
||||
//maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses
|
||||
#ifndef MAX_LEDS
|
||||
#ifdef ESP8266
|
||||
#define MAX_LEDS 1664 // can't rely on memory limit to limit this to 1600 LEDs
|
||||
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
|
||||
#else
|
||||
#define MAX_LEDS 8192
|
||||
#endif
|
||||
@@ -229,7 +254,7 @@
|
||||
|
||||
#ifndef MAX_LED_MEMORY
|
||||
#ifdef ESP8266
|
||||
#define MAX_LED_MEMORY 5000
|
||||
#define MAX_LED_MEMORY 4000
|
||||
#else
|
||||
#define MAX_LED_MEMORY 64000
|
||||
#endif
|
||||
@@ -240,12 +265,20 @@
|
||||
#endif
|
||||
|
||||
// string temp buffer (now stored in stack locally)
|
||||
#define OMAX 2048
|
||||
#ifdef ESP8266
|
||||
#define SETTINGS_STACK_BUF_SIZE 2048
|
||||
#else
|
||||
#define SETTINGS_STACK_BUF_SIZE 3096
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#define E131_MAX_UNIVERSE_COUNT 20
|
||||
#define E131_MAX_UNIVERSE_COUNT 20
|
||||
#else
|
||||
#define E131_MAX_UNIVERSE_COUNT 10
|
||||
#ifdef ESP8266
|
||||
#define E131_MAX_UNIVERSE_COUNT 9
|
||||
#else
|
||||
#define E131_MAX_UNIVERSE_COUNT 12
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit
|
||||
@@ -270,7 +303,7 @@
|
||||
|
||||
// Maximum size of node map (list of other WLED instances)
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_NODES 15
|
||||
#define WLED_MAX_NODES 24
|
||||
#else
|
||||
#define WLED_MAX_NODES 150
|
||||
#endif
|
||||
@@ -280,15 +313,15 @@
|
||||
#ifdef ESP8266
|
||||
#define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards
|
||||
#else
|
||||
#define LEDPIN 16 // alligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards
|
||||
#define LEDPIN 2 // Changed from 16 to restore compatibility with ESP32-pico
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
#if (LEDPIN == 2)
|
||||
#undef LEDPIN
|
||||
#define LEDPIN 3
|
||||
#warning "Pin conflict compiling with DMX and LEDs on pin 2. The default LED pin has been changed to pin 3."
|
||||
#define LEDPIN 1
|
||||
#warning "Pin conflict compiling with DMX and LEDs on pin 2. The default LED pin has been changed to pin 1."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
}
|
||||
DMXMap = "";
|
||||
for (i=0;i<512;i++) {
|
||||
isstart = "";
|
||||
if ((i+1) % 10 == 0) {
|
||||
isstart="S"
|
||||
}
|
||||
DMXMap += "<div class=\"anytype " + isstart + " type" + dmxchans[i] + "\">" + String(i+1) + "<br />" + dmxlabels[dmxchans[i]] + "</div>";
|
||||
DMXMap += "<div class=\"anytype type" + dmxchans[i] + "\">" + String(i+1) + "<br />" + dmxlabels[dmxchans[i]] + "</div>";
|
||||
}
|
||||
document.getElementById("map").innerHTML = DMXMap;
|
||||
}</script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -45,17 +45,30 @@
|
||||
<div class ="container">
|
||||
<div id="Colors" class="tabcontent">
|
||||
<div id="picker" class="noslide"></div>
|
||||
<div id="vwrap">
|
||||
<div class="sliderwrap il" id="vwrap">
|
||||
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="128" step="any" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
</div>
|
||||
<div id="kwrap">
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="rgbwrap">
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderR" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" />
|
||||
<p class="labels">RGB color</p>
|
||||
<div class="sliderwrap il" id="rwrap">
|
||||
<input id="sliderR" class="noslide" onchange="setColor(0)" oninput="fromRgb()" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderG" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderwrap il" id="gwrap">
|
||||
<input id="sliderG" class="noslide" onchange="setColor(0)" oninput="fromRgb()" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderB" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderwrap il" id="bwrap">
|
||||
<input id="sliderB" class="noslide" onchange="setColor(0)" oninput="fromRgb()" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
</div>
|
||||
@@ -66,6 +79,13 @@
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wbal">
|
||||
<p class="labels">White balance</p>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="qcs-w">
|
||||
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
|
||||
@@ -93,12 +113,12 @@
|
||||
Color palette
|
||||
</p>
|
||||
<div class="il">
|
||||
<div id="selectPalette" class="list">
|
||||
<div id="pallist" class="list">
|
||||
<div class="lstI" data-id="0">
|
||||
<label class="check schkl">
|
||||
|
||||
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
|
||||
<span class="checkmark schk"></span>
|
||||
<span class="radiomark schk"></span>
|
||||
</label>
|
||||
<div class="lstIcontent">
|
||||
<span class="lstIname">
|
||||
@@ -153,9 +173,10 @@
|
||||
<div id="segutil2">
|
||||
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
|
||||
</div>
|
||||
<p>Transition: <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s</p>
|
||||
</div>
|
||||
|
||||
<div id="Favorites" class="tabcontent">
|
||||
<div id="Presets" class="tabcontent">
|
||||
<div id="putil">
|
||||
|
||||
</div>
|
||||
@@ -164,8 +185,7 @@
|
||||
</div>
|
||||
<div id="pcont">
|
||||
Loading...
|
||||
</div><br>
|
||||
Transition <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -173,7 +193,7 @@
|
||||
<button class="tablinks" onclick="openTab(0)"><i class="icons"></i><p class="tab-label">Colors</p></button>
|
||||
<button class="tablinks" onclick="openTab(1)"><i class="icons"></i><p class="tab-label">Effects</p></button>
|
||||
<button class="tablinks" onclick="openTab(2)"><i class="icons"></i><p class="tab-label">Segments</p></button>
|
||||
<button class="tablinks" onclick="openTab(3)"><i class="icons"></i><p class="tab-label">Favorites</p></button>
|
||||
<button class="tablinks" onclick="openTab(3)"><i class="icons"></i><p class="tab-label">Presets</p></button>
|
||||
</div>
|
||||
|
||||
<div id="connind"></div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -21,18 +21,6 @@
|
||||
<body>
|
||||
<div id="canv" />
|
||||
<script>
|
||||
console.info("Live-Preview websocket opening");
|
||||
var socket = new WebSocket("ws://"+document.location.host+"/ws");
|
||||
|
||||
socket.onopen = function () {
|
||||
console.info("Live-Preview websocket is opened");
|
||||
socket.send("{'lv':true}");
|
||||
}
|
||||
|
||||
socket.onclose = function () { console.info("Live-Preview websocket is closing"); }
|
||||
|
||||
socket.onerror = function (event) { console.error("Live-Preview websocket error:", event); }
|
||||
|
||||
function updatePreview(leds) {
|
||||
var str = "linear-gradient(90deg,";
|
||||
var len = leds.length;
|
||||
@@ -46,7 +34,7 @@
|
||||
document.getElementById("canv").style.background = str;
|
||||
}
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
function getLiveJson(event) {
|
||||
try {
|
||||
var json = JSON.parse(event.data);
|
||||
if (json && json.leds) {
|
||||
@@ -54,9 +42,23 @@
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Live-Preview websocket error:",err);
|
||||
}
|
||||
console.error("Live-Preview ws error:",err);
|
||||
}
|
||||
}
|
||||
|
||||
var ws = top.window.ws;
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
console.info("Use top WS for peek");
|
||||
ws.send("{'lv':true}");
|
||||
} else {
|
||||
console.info("Peek ws opening");
|
||||
ws = new WebSocket("ws://"+document.location.host+"/ws");
|
||||
ws.onopen = function () {
|
||||
console.info("Peek WS opened");
|
||||
ws.send("{'lv':true}");
|
||||
}
|
||||
}
|
||||
ws.addEventListener('message',getLiveJson);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,23 +17,15 @@
|
||||
color: #fff;
|
||||
font-family: Verdana, Helvetica, sans-serif;
|
||||
border: 1px solid #333;
|
||||
border-radius: var(--h);
|
||||
font-size: 6vmin;
|
||||
height: var(--h);
|
||||
width: 95%;
|
||||
width: calc(100% - 40px);
|
||||
margin-top: 2vh;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function BB()
|
||||
{
|
||||
if (window.frameElement) {
|
||||
document.getElementById("b").style.display = "none";
|
||||
document.documentElement.style.setProperty('--h',"13.86vh");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="BB()">
|
||||
<body>
|
||||
<form action="/"><button type=submit id="b">Back</button></form>
|
||||
<form action="/settings/wifi"><button type="submit">WiFi Setup</button></form>
|
||||
<form action="/settings/leds"><button type="submit">LED Preferences</button></form>
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
<meta name="viewport" content="width=500">
|
||||
<title>LED Settings</title>
|
||||
<script>
|
||||
var d=document,laprev=55,maxB=1,maxM=5000,maxPB=4096,bquot=0; //maximum bytes for LED allocation: 5kB for 8266, 32kB for 32
|
||||
function H()
|
||||
var d=document,laprev=55,maxB=1,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||
var customStarts=false,startsDirty=[];
|
||||
function H()
|
||||
{
|
||||
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings");
|
||||
window.open("https://kno.wled.ge/features/settings/#led-settings");
|
||||
}
|
||||
function B()
|
||||
{
|
||||
@@ -18,35 +19,59 @@
|
||||
function off(n){
|
||||
d.getElementsByName(n)[0].value = -1;
|
||||
}
|
||||
function bLimits(b,p,m) {
|
||||
maxB = b; maxM = m; maxPB = p;
|
||||
var timeout;
|
||||
function showToast(text, error = false)
|
||||
{
|
||||
var x = gId("toast");
|
||||
x.innerHTML = text;
|
||||
x.className = error ? "error":"show";
|
||||
clearTimeout(timeout);
|
||||
x.style.animation = 'none';
|
||||
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
|
||||
}
|
||||
function bLimits(b,p,m,l) {
|
||||
maxB = b; maxM = m; maxPB = p; maxL = l;
|
||||
}
|
||||
function pinsOK() {
|
||||
var LCs = d.getElementsByTagName("input");
|
||||
for (i=0; i<LCs.length; i++) {
|
||||
var nm = LCs[i].name.substring(0,2);
|
||||
// ignore IP address
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
|
||||
var n = LCs[i].name.substring(2);
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value, 10); // LED type SELECT
|
||||
if (t>=80) continue;
|
||||
}
|
||||
//check for pin conflicts
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
|
||||
if (LCs[i].value!="" && LCs[i].value!="-1") {
|
||||
if (d.um_p && d.um_p.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.um_p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;}
|
||||
else if (LCs[i].value > 5 && LCs[i].value < 12) {alert("Sorry, pins 6-11 can not be used.");LCs[i].value="";LCs[i].focus();return false;}
|
||||
else if (!(nm == "IR" || nm=="BT") && LCs[i].value > 33) {alert("Sorry, pins >33 are input only.");LCs[i].value="";LCs[i].focus();return false;}
|
||||
for (j=i+1; j<LCs.length; j++)
|
||||
{
|
||||
var n2 = LCs[j].name.substring(0,2);
|
||||
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR")
|
||||
if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {alert(`Pin conflict between ${nm}/${n2}!`);LCs[j].value="";LCs[j].focus();return false;}
|
||||
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") {
|
||||
if (n2.substring(0,1)==="L") {
|
||||
var m = LCs[j].name.substring(2);
|
||||
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
|
||||
if (t2>=80) continue;
|
||||
}
|
||||
if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`);LCs[j].value="";LCs[j].focus();return false;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function trySubmit(e) {
|
||||
d.Sf.data.value = '';
|
||||
e.preventDefault();
|
||||
if (!pinsOK()) {e.stopPropagation();return false;} // Prevent form submission and contact with server
|
||||
if (bquot > 100) {var msg = "Too many LEDs for me to handle!"; if (maxM < 10000) msg += "\n\rConsider using an ESP32."; alert(msg);}
|
||||
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||
}
|
||||
function S(){GetV();setABL();}
|
||||
function S(){GetV();checkSi();setABL();}
|
||||
function enABL()
|
||||
{
|
||||
var en = gId('able').checked;
|
||||
@@ -79,21 +104,21 @@
|
||||
UI();
|
||||
}
|
||||
//returns mem usage
|
||||
function getMem(type, len, p0) {
|
||||
if (type < 32) {
|
||||
function getMem(t, len, p0) {
|
||||
if (t < 32) {
|
||||
if (maxM < 10000 && p0==3) { //8266 DMA uses 5x the mem
|
||||
if (type > 29) return len*20; //RGBW
|
||||
if (t > 29) return len*20; //RGBW
|
||||
return len*15;
|
||||
} else if (maxM >= 10000) //ESP32 RMT uses double buffer?
|
||||
{
|
||||
if (type > 29) return len*8; //RGBW
|
||||
if (t > 29) return len*8; //RGBW
|
||||
return len*6;
|
||||
}
|
||||
if (type > 29) return len*4; //RGBW
|
||||
if (t > 29) return len*4; //RGBW
|
||||
return len*3;
|
||||
}
|
||||
if (type > 31 && type < 48) return 5;
|
||||
if (type == 44 || type == 45) return len*4; //RGBW
|
||||
if (t > 31 && t < 48) return 5;
|
||||
if (t == 44 || t == 45) return len*4; //RGBW
|
||||
return len*3;
|
||||
}
|
||||
function UI(change=false)
|
||||
@@ -105,85 +130,128 @@
|
||||
if (d.Sf.LA.value == 255) laprev = 12;
|
||||
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
|
||||
|
||||
// enable/disable LED fields
|
||||
var s = d.getElementsByTagName("select");
|
||||
for (i=0; i<s.length; i++) {
|
||||
// is the field a LED type?
|
||||
if (s[i].name.substring(0,2)=="LT") {
|
||||
n=s[i].name.substring(2);
|
||||
var type = parseInt(s[i].value,10);
|
||||
gId("p0d"+n).innerHTML = (type > 49) ? "Data:" : (type >41) ? "Pins:" : "Pin:";
|
||||
gId("p1d"+n).innerHTML = (type > 49) ? "Clk:" : "";
|
||||
var LK = d.getElementsByName("L1"+n)[0];
|
||||
var n = s[i].name.substring(2);
|
||||
var t = parseInt(s[i].value,10);
|
||||
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t >41) ? "GPIOs:" : "GPIO:";
|
||||
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
|
||||
var LK = d.getElementsByName("L1"+n)[0]; // clock pin
|
||||
|
||||
memu += getMem(type, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value);
|
||||
memu += getMem(t, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value); // calc memory
|
||||
|
||||
// enumerate pins
|
||||
for (p=1; p<5; p++) {
|
||||
var LK = d.getElementsByName("L"+p+n)[0];
|
||||
var LK = d.getElementsByName("L"+p+n)[0]; // secondary pins
|
||||
if (!LK) continue;
|
||||
if ((type>49 && p==1) || (type>41 && type < 50 && (p+40 < type))) // TYPE_xxxx values from const.h
|
||||
if (((t>=80 && t<96) && p<4) || (t>49 && p==1) || (t>41 && t < 50 && (p+40 < t))) // TYPE_xxxx values from const.h
|
||||
{
|
||||
// display pin field
|
||||
LK.style.display = "inline";
|
||||
LK.required = true;
|
||||
} else {
|
||||
// hide pin field
|
||||
LK.style.display = "none";
|
||||
LK.required = false;
|
||||
LK.value="";
|
||||
}
|
||||
}
|
||||
if (type == 30 || type == 31 || (type > 40 && type < 46 && type != 43)) isRGBW = true;
|
||||
gId("dig"+n).style.display = (type > 31 && type < 48) ? "none":"inline";
|
||||
gId("psd"+n).innerHTML = (type > 31 && type < 48) ? "Index:":"Start:";
|
||||
if (change) {
|
||||
gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state
|
||||
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
|
||||
}
|
||||
gId("rf"+n).onclick = (t == 31) ? (function(){return false}) : (function(){}); // prevent change for TM1814
|
||||
isRGBW |= (t == 30 || t == 31 || (t > 40 && t < 46 && t != 43)); // RGBW checkbox, TYPE_xxxx values from const.h
|
||||
gId("co"+n).style.display = ((t>=80 && t<96) || t == 41 || t == 42) ? "none":"inline"; // hide color order for PWM W & WW/CW
|
||||
gId("dig"+n+"c").style.display = (t > 40 && t < 48) ? "none":"inline"; // hide count for analog
|
||||
gId("dig"+n+"r").style.display = (t>=80 && t<96) ? "none":"inline"; // hide reversed for virtual
|
||||
gId("dig"+n+"s").style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
|
||||
gId("dig"+n+"f").style.display = (t>=16 && t<32 || t>=50 && t<64) ? "inline":"none"; // hide refresh
|
||||
gId("rev"+n).innerHTML = (t > 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
|
||||
gId("psd"+n).innerHTML = (t > 40 && t < 48) ? "Index:":"Start:"; // change analog start description
|
||||
}
|
||||
}
|
||||
|
||||
// display white channel calculation method
|
||||
var myC = d.querySelectorAll('.wc'),
|
||||
l = myC.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
myC[i].style.display = (isRGBW) ? 'inline':'none';
|
||||
}
|
||||
|
||||
if (d.activeElement == d.getElementsByName("LC")[0]) {
|
||||
var o = d.getElementsByClassName("iST");
|
||||
var i = o.length;
|
||||
if (i == 1) d.getElementsByName("LC0")[0].value = d.getElementsByName("LC")[0].value;
|
||||
}
|
||||
|
||||
// check for pin conflicts
|
||||
var LCs = d.getElementsByTagName("input");
|
||||
var sLC = 0, maxLC = 0;
|
||||
var sLC = 0, sPC = 0, maxLC = 0;
|
||||
for (i=0; i<LCs.length; i++) {
|
||||
var nm = LCs[i].name.substring(0,2);
|
||||
if (nm=="LC" && LCs[i].name !== "LC") {
|
||||
var n=LCs[i].name.substring(2);
|
||||
var nm = LCs[i].name.substring(0,2); // field name
|
||||
var n = LCs[i].name.substring(2); // bus number
|
||||
// do we have a led count field
|
||||
if (nm=="LC") {
|
||||
var c=parseInt(LCs[i].value,10);
|
||||
if(gId("ls"+n).readOnly) gId("ls"+n).value=sLC;
|
||||
if(c){sLC+=c;if(c>maxLC)maxLC=c;}
|
||||
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC;
|
||||
gId("ls"+n).disabled = !customStarts;
|
||||
if(c){
|
||||
var s = parseInt(gId("ls"+n).value);
|
||||
if (s+c > sLC) sLC = s+c;
|
||||
if(c>maxLC)maxLC=c;
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs
|
||||
} // increase led count
|
||||
continue;
|
||||
}
|
||||
// do we have led pins for digital leds
|
||||
if (nm=="L0" || nm=="L1") {
|
||||
var lc=d.getElementsByName("LC"+LCs[i].name.substring(2))[0];
|
||||
lc.max=maxPB;
|
||||
var lc=d.getElementsByName("LC"+n)[0];
|
||||
lc.max=maxPB; // update max led count value
|
||||
}
|
||||
// ignore IP address (stored in pins for virtual busses)
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
if (t>=80) {
|
||||
LCs[i].max = 255;
|
||||
LCs[i].min = 0;
|
||||
LCs[i].style.color="#fff";
|
||||
continue; // do not check conflicts
|
||||
} else {
|
||||
LCs[i].max = 33;
|
||||
LCs[i].min = -1;
|
||||
}
|
||||
}
|
||||
// check for pin conflicts
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
|
||||
if (LCs[i].value!="" && LCs[i].value!="-1") {
|
||||
var p = [];
|
||||
if (d.um_p && Array.isArray(d.um_p)) for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]);
|
||||
var p = []; // used pin array
|
||||
if (d.um_p && Array.isArray(d.um_p)) for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with reservations
|
||||
for (j=0; j<LCs.length; j++) {
|
||||
if (i==j) continue;
|
||||
var n2 = LCs[j].name.substring(0,2);
|
||||
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR")
|
||||
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10));
|
||||
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") {
|
||||
if (n2.substring(0,1)==="L") {
|
||||
var m = LCs[j].name.substring(2);
|
||||
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
|
||||
if (t2>=80) continue;
|
||||
}
|
||||
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10)); // add current pin
|
||||
}
|
||||
}
|
||||
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color="#fff";
|
||||
// now check for conflicts
|
||||
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=parseInt(LCs[i].value,10)>33?"orange":"#fff";
|
||||
}
|
||||
}
|
||||
// update total led count
|
||||
gId("lc").textContent = sLC;
|
||||
gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)";
|
||||
|
||||
// memory usage and warnings
|
||||
gId('m0').innerHTML = memu;
|
||||
bquot = memu / maxM * 100;
|
||||
gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`;
|
||||
gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none';
|
||||
gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange';
|
||||
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>WARNING: Using over ${maxM}B!</b>)` : "") : "800 LEDs per pin";
|
||||
|
||||
var val = Math.ceil((100 + sLC * laprev)/500)/2;
|
||||
gId('ledwarning').style.display = (maxLC > Math.min(maxPB,800) || bquot > 80) ? 'inline':'none';
|
||||
gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange';
|
||||
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>ERROR: Using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
|
||||
// calculate power
|
||||
var val = Math.ceil((100 + sPC * laprev)/500)/2;
|
||||
val = (val > 5) ? Math.ceil(val) : val;
|
||||
var s = "";
|
||||
var is12V = (d.Sf.LAsel.value == 30);
|
||||
@@ -197,26 +265,25 @@
|
||||
s += val;
|
||||
s += "A supply connected to LEDs";
|
||||
}
|
||||
var val2 = Math.ceil((100 + sLC * laprev)/1500)/2;
|
||||
var val2 = Math.ceil((100 + sPC * laprev)/1500)/2;
|
||||
val2 = (val2 > 5) ? Math.ceil(val2) : val2;
|
||||
var s2 = "(for most effects, ~";
|
||||
s2 += val2;
|
||||
s2 += "A is enough)<br>";
|
||||
gId('psu').innerHTML = s;
|
||||
gId('psu2').innerHTML = isWS2815 ? "" : s2;
|
||||
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
|
||||
}
|
||||
function lastEnd(i) {
|
||||
if (i<1) return 0;
|
||||
v = parseInt(d.getElementsByName("LS"+(i-1))[0].value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value);
|
||||
var type = parseInt(d.getElementsByName("LT"+(i-1))[0].value);
|
||||
if (type > 31 && type < 48) v = 1; //PWM busses
|
||||
var t = parseInt(d.getElementsByName("LT"+(i-1))[0].value);
|
||||
if (t > 31 && t < 48) v = 1; //PWM busses
|
||||
if (isNaN(v)) return 0;
|
||||
return v;
|
||||
}
|
||||
function addLEDs(n)
|
||||
function addLEDs(n,init=true)
|
||||
{
|
||||
if (n>1) {maxB=n; gId("+").style.display="inline"; return;}
|
||||
|
||||
var o = d.getElementsByClassName("iST");
|
||||
var i = o.length;
|
||||
|
||||
@@ -226,10 +293,10 @@
|
||||
if (n==1) {
|
||||
// npm run build has trouble minimizing spaces inside string
|
||||
var cn = `<div class="iST">
|
||||
${i>0?'<hr style="width:260px">':''}
|
||||
<hr style="width:260px">
|
||||
${i+1}:
|
||||
<select name="LT${i}" onchange="UI()">
|
||||
<option value="22">WS281x</option>
|
||||
<select name="LT${i}" onchange="UI(true)">
|
||||
<option value="22" selected>WS281x</option>
|
||||
<option value="30">SK6812 RGBW</option>
|
||||
<option value="31">TM1814</option>
|
||||
<option value="24">400kHz</option>
|
||||
@@ -238,12 +305,16 @@ ${i+1}:
|
||||
<option value="52">LPD8806</option>
|
||||
<option value="53">P9813</option>
|
||||
<option value="41">PWM White</option>
|
||||
<option value="42">PWM WWCW</option>
|
||||
<option value="42">PWM CCT</option>
|
||||
<option value="43">PWM RGB</option>
|
||||
<option value="44">PWM RGBW</option>
|
||||
<option value="45">PWM RGBWC</option>
|
||||
<option value="45">PWM RGB+CCT</option>
|
||||
<!--option value="46">PWM RGB+DCCT</option-->
|
||||
<option value="80">DDP RGB (network)</option>
|
||||
<!--option value="81">E1.31 RGB (network)</option-->
|
||||
<!--option value="82">ArtNet RGB (network)</option-->
|
||||
</select>
|
||||
Color Order:
|
||||
<div id="co${i}" style="display:inline">Color Order:
|
||||
<select name="CO${i}">
|
||||
<option value="0">GRB</option>
|
||||
<option value="1">RGB</option>
|
||||
@@ -251,19 +322,19 @@ Color Order:
|
||||
<option value="3">RBG</option>
|
||||
<option value="4">BGR</option>
|
||||
<option value="5">GBR</option>
|
||||
</select><br>
|
||||
<span id="p0d${i}">Pin:</span> <input type="number" name="L0${i}" min="0" max="40" required style="width:35px" onchange="UI()"/>
|
||||
<span id="p1d${i}">Clock:</span> <input type="number" name="L1${i}" min="0" max="40" style="width:35px" onchange="UI()"/>
|
||||
<span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="40" style="width:35px" onchange="UI()"/>
|
||||
<span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="40" style="width:35px" onchange="UI()"/>
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="40" style="width:35px" onchange="UI()"/>
|
||||
</select></div>
|
||||
<br>
|
||||
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" min="0" max="8191" value="${lastEnd(i)}" required />
|
||||
<div id="dig${i}" style="display:inline">
|
||||
Count: <input type="number" name="LC${i}" min="0" max="${maxPB}" value="1" required oninput="UI()" /><br>
|
||||
Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
|
||||
Skip 1<sup>st</sup> LED: <input id="sl${i}" type="checkbox" name="SL${i}"><br>
|
||||
</div>
|
||||
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" class="l starts" min="0" max="8191" value="${lastEnd(i)}" oninput="startsDirty[${i}]=true;UI();" required />
|
||||
<div id="dig${i}c" style="display:inline">Length: <input type="number" name="LC${i}" class="l" min="1" max="${maxPB}" value="1" required oninput="UI()" /></div>
|
||||
<br>
|
||||
<span id="p0d${i}">GPIO:</span> <input type="number" name="L0${i}" min="0" max="33" required class="xs" onchange="UI()"/>
|
||||
<span id="p1d${i}"></span><input type="number" name="L1${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
|
||||
<div id="dig${i}s" style="display:inline"><br>Skip 1<sup>st</sup> LED: <input id="sl${i}" type="checkbox" name="SL${i}"></div>
|
||||
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
|
||||
</div>`;
|
||||
f.insertAdjacentHTML("beforeend", cn);
|
||||
}
|
||||
@@ -274,29 +345,116 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
|
||||
gId("+").style.display = (i<maxB-1) ? "inline":"none";
|
||||
gId("-").style.display = (i>0) ? "inline":"none";
|
||||
|
||||
UI();
|
||||
if (!init) UI();
|
||||
}
|
||||
function addBtn(i,p,t) {
|
||||
var c = gId("btns").innerHTML;
|
||||
var bt = "BT" + i;
|
||||
var be = "BE" + i;
|
||||
c += `Button ${i} pin: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" style="width:35px" value="${p}"> `;
|
||||
c += `<select name="${be}">`
|
||||
c += `Button ${i} GPIO: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += ` <select name="${be}">`
|
||||
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
||||
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
|
||||
c += `<option value="3" ${t==3?"selected":""}>Push inverted</option>`;
|
||||
c += `<option value="4" ${t==4?"selected":""}>Switch</option>`;
|
||||
c += `<option value="5" ${t==4?"selected":""}>Switch inverted</option>`;
|
||||
c += `<option value="5" ${t==5?"selected":""}>PIR sensor</option>`;
|
||||
c += `<option value="6" ${t==6?"selected":""}>Touch</option>`;
|
||||
c += `<option value="7" ${t==7?"selected":""}>Analog</option>`;
|
||||
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
|
||||
c += `</select>`;
|
||||
c += `<span style="cursor: pointer;" onclick="off('${bt}')"> ×</span><br>`;
|
||||
gId("btns").innerHTML = c;
|
||||
}
|
||||
function tglSi(cs) {
|
||||
customStarts = cs;
|
||||
if (!customStarts) startsDirty = []; //set all starts to clean
|
||||
UI();
|
||||
}
|
||||
function checkSi() { //on load, checks whether there are custom start fields
|
||||
var cs = false;
|
||||
for (var i=1; i < d.getElementsByClassName("iST").length; i++) {
|
||||
var v = parseInt(gId("ls"+(i-1)).value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value);
|
||||
if (v != parseInt(gId("ls"+i).value)) {cs = true; startsDirty[i] = true;}
|
||||
}
|
||||
if (parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;}
|
||||
gId("si").checked = cs;
|
||||
tglSi(cs);
|
||||
}
|
||||
function uploadFile(name) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
|
||||
req.addEventListener('error', function(e){showToast(e.stack,true);});
|
||||
req.open("POST", "/upload");
|
||||
var formData = new FormData();
|
||||
formData.append("data", d.Sf.data.files[0], name);
|
||||
req.send(formData);
|
||||
d.Sf.data.value = '';
|
||||
return false;
|
||||
}
|
||||
// https://stackoverflow.com/questions/7346563/loading-local-json-file
|
||||
function loadCfg(o) {
|
||||
var f, fr;
|
||||
|
||||
if (typeof window.FileReader !== 'function') {
|
||||
alert("The file API isn't supported on this browser yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!o.files) {
|
||||
alert("This browser doesn't support the `files` property of file inputs.");
|
||||
} else if (!o.files[0]) {
|
||||
alert("Please select a JSON file first!");
|
||||
} else {
|
||||
f = o.files[0];
|
||||
fr = new FileReader();
|
||||
fr.onload = receivedText;
|
||||
fr.readAsText(f);
|
||||
}
|
||||
o.value = '';
|
||||
|
||||
function receivedText(e) {
|
||||
let lines = e.target.result;
|
||||
var c = JSON.parse(lines);
|
||||
if (c.hw) {
|
||||
if (c.hw.led) {
|
||||
for (var i=0; i<10; i++) addLEDs(-1);
|
||||
var l = c.hw.led;
|
||||
l.ins.forEach((v,i,a)=>{
|
||||
addLEDs(1);
|
||||
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
|
||||
d.getElementsByName("LT"+i)[0].value = v.type;
|
||||
d.getElementsByName("LS"+i)[0].value = v.start;
|
||||
d.getElementsByName("LC"+i)[0].value = v.len;
|
||||
d.getElementsByName("CO"+i)[0].value = v.order;
|
||||
d.getElementsByName("SL"+i)[0].checked = v.skip;
|
||||
d.getElementsByName("RF"+i)[0].checked = v.ref;
|
||||
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
||||
});
|
||||
}
|
||||
if (c.hw.btn) {
|
||||
var b = c.hw.btn;
|
||||
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
|
||||
b.ins.forEach((v,i,a)=>{
|
||||
addBtn(i,v.pin[0],v.type);
|
||||
});
|
||||
d.getElementsByName("TT")[0].value = b.tt;
|
||||
}
|
||||
if (c.hw.ir) {
|
||||
d.getElementsByName("IR")[0].value = c.hw.ir.pin;
|
||||
d.getElementsByName("IT")[0].value = c.hw.ir.type;
|
||||
}
|
||||
if (c.hw.relay) {
|
||||
d.getElementsByName("RL")[0].value = c.hw.relay.pin;
|
||||
d.getElementsByName("RM")[0].checked = c.hw.relay.inv;
|
||||
}
|
||||
UI();
|
||||
}
|
||||
}
|
||||
}
|
||||
function GetV()
|
||||
{
|
||||
//values injected by server while sending HTML
|
||||
//maxM=5000;maxPB=1536;d.um_p=[1,6,7,8,9,10,11];addLEDs(3);d.Sf.LC.value=250;addLEDs(1);d.Sf.L00.value=2;d.Sf.L10.value=0;d.Sf.LC0.value=250;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=0;d.Sf.LS0.checked=0;d.Sf.MA.value=5400;d.Sf.LA.value=55;d.getElementsByClassName("pow")[0].innerHTML="350mA";d.Sf.CA.value=40;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=3;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=64;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=0;addBtn(0,0,2);addBtn(1,3,4);addBtn(2,-1,0);d.Sf.IR.value=-1;
|
||||
//d.um_p=[6,7,8,9,10,11,14,15,13,1,21,19,22,25,26,27,5,23,18,17];bLimits(10,2048,64000,8192);d.Sf.MS.checked=1;d.Sf.CCT.checked=0;addLEDs(1);d.Sf.L00.value=192;d.Sf.L10.value=168;d.Sf.L20.value=0;d.Sf.L30.value=61;d.Sf.LC0.value=421;d.Sf.LT0.value=80;d.Sf.CO0.value=1;d.Sf.LS0.value=0;d.Sf.CV0.checked=0;d.Sf.SL0.checked=0;d.Sf.RF0.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=127;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=0;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=1;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=-1;d.Sf.RM.checked=1;addBtn(0,-1,0);addBtn(1,-1,0);addBtn(2,-1,0);addBtn(3,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=8;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@@ -308,14 +466,14 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>LED & Hardware setup</h2>
|
||||
Total LED count: <input name="LC" id="LC" type="number" min="1" max="8192" oninput="UI()" required><br>
|
||||
Total LEDs: <span id="lc">?</span> <span id="pc"></span><br>
|
||||
<i>Recommended power supply for brightest white:</i><br>
|
||||
<b><span id="psu">?</span></b><br>
|
||||
<span id="psu2"><br></span>
|
||||
<br>
|
||||
Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br>
|
||||
<div id="abl">
|
||||
Maximum Current: <input name="MA" type="number" min="250" max="65000" oninput="UI()" required> mA<br>
|
||||
Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br>
|
||||
<div id="ampwarning" style="color: orange; display: none;">
|
||||
⚠ Your power supply provides high current.<br>
|
||||
To improve the safety of your setup,<br>
|
||||
@@ -339,44 +497,52 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
|
||||
</div>
|
||||
<h3>Hardware setup</h3>
|
||||
<div id="mLC">LED outputs:</div>
|
||||
<button type="button" id="+" onclick="addLEDs(1)" style="display:none;border-radius:20px;height:36px;">+</button>
|
||||
<button type="button" id="-" onclick="addLEDs(-1)" style="display:none;border-radius:20px;width:36px;height:36px;">-</button><br>
|
||||
<hr style="width:260px">
|
||||
<button type="button" id="+" onclick="addLEDs(1,false)" style="display:none;border-radius:20px;height:36px;">+</button>
|
||||
<button type="button" id="-" onclick="addLEDs(-1,false)" style="display:none;border-radius:20px;width:36px;height:36px;">-</button><br>
|
||||
LED Memory Usage: <span id="m0">0</span> / <span id="m1">?</span> B<br>
|
||||
<div id="dbar" style="display:inline-block; width: 100px; height: 10px; border-radius: 20px;"></div><br>
|
||||
<div id="ledwarning" style="color: orange; display: none;">
|
||||
⚠ You might run into stability or lag issues.<br>
|
||||
Use less than <span id="wreason">800 LEDs per pin</span> for the best experience!<br>
|
||||
</div><hr style="width:260px">
|
||||
Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
|
||||
</div>
|
||||
<hr style="width:260px">
|
||||
Make a segment for each output: <input type="checkbox" name="MS"> <br>
|
||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
|
||||
<hr style="width:260px">
|
||||
<div id="btns"></div>
|
||||
Touch threshold: <input type="number" min="0" max="100" name="TT" required><br>
|
||||
IR pin: <input type="number" min="-1" max="40" name="IR" onchange="UI()" style="width:35px"> <select name="IT">
|
||||
<option value="0">Remote disabled</option>
|
||||
<option value="1">24-key RGB</option>
|
||||
<option value="2">24-key with CT</option>
|
||||
<option value="3">40-key blue</option>
|
||||
<option value="4">44-key RGB</option>
|
||||
<option value="5">21-key RGB</option>
|
||||
<option value="6">6-key black</option>
|
||||
<option value="7">9-key red</option>
|
||||
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
||||
IR GPIO: <input type="number" min="-1" max="40" name="IR" onchange="UI()" class="xs"><select name="IT" onchange="UI()">
|
||||
<option value=0>Remote disabled</option>
|
||||
<option value=1>24-key RGB</option>
|
||||
<option value=2>24-key with CT</option>
|
||||
<option value=3>40-key blue</option>
|
||||
<option value=4>44-key RGB</option>
|
||||
<option value=5>21-key RGB</option>
|
||||
<option value=6>6-key black</option>
|
||||
<option value=7>9-key red</option>
|
||||
<option value=8>JSON remote</option>
|
||||
</select><span style="cursor: pointer;" onclick="off('IR')"> ×</span><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a><br>
|
||||
Relay pin: <input type="number" min="-1" max="40" name="RL" onchange="UI()" style="width:35px"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')"> ×</span><br>
|
||||
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile('/ir.json');"><br></div>
|
||||
<div id="toast"></div>
|
||||
<a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br>
|
||||
Relay GPIO: <input type="number" min="-1" max="33" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')"> ×</span><br>
|
||||
<hr style="width:260px">
|
||||
<h3>Defaults</h3>
|
||||
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
|
||||
Default brightness: <input name="CA" type="number" min="0" max="255" required> (0-255)<br><br>
|
||||
Apply preset <input name="BP" type="number" min="0" max="250" required> at boot (0 uses defaults)
|
||||
Default brightness: <input name="CA" type="number" class="s" min="0" max="255" required> (0-255)<br><br>
|
||||
Apply preset <input name="BP" type="number" class="s" min="0" max="250" required> at boot (0 uses defaults)
|
||||
<br><br>
|
||||
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
||||
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br>
|
||||
Brightness factor: <input name="BF" type="number" min="1" max="255" required> %
|
||||
Brightness factor: <input name="BF" type="number" class="s" min="1" max="255" required> %%
|
||||
<h3>Transitions</h3>
|
||||
Crossfade: <input type="checkbox" name="TF"><br>
|
||||
Transition Time: <input name="TD" maxlength="5" size="2"> ms<br>
|
||||
Transition Time: <input name="TD" type="number" class="l" min="0" max="65500"> ms<br>
|
||||
Enable Palette transitions: <input type="checkbox" name="PF">
|
||||
<h3>Timed light</h3>
|
||||
Default Duration: <input name="TL" type="number" min="1" max="255" required> min<br>
|
||||
Default Target brightness: <input name="TB" type="number" min="0" max="255" required><br>
|
||||
Default Duration: <input name="TL" type="number" class="s" min="1" max="255" required> min<br>
|
||||
Default Target brightness: <input name="TB" type="number" class="s" min="0" max="255" required><br>
|
||||
Mode:
|
||||
<select name="TW">
|
||||
<option value="0">Wait and set</option>
|
||||
@@ -384,6 +550,19 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
|
||||
<option value="2">Fade Color</option>
|
||||
<option value="3">Sunrise</option>
|
||||
</select>
|
||||
<h3>White management</h3>
|
||||
White Balance correction: <input type="checkbox" name="CCT"> <br>
|
||||
<span class="wc">
|
||||
Auto-calculate white channel from RGB:<br>
|
||||
<select name="AW">
|
||||
<option value=0>None</option>
|
||||
<option value=1>Brighter</option>
|
||||
<option value=2>Accurate</option>
|
||||
<option value=3>Dual</option>
|
||||
</select>
|
||||
<br>
|
||||
Calculate CCT from RGB: <input type="checkbox" name="CR"> <br>
|
||||
CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %%</span>
|
||||
<h3>Advanced</h3>
|
||||
Palette blending:
|
||||
<select name="PB">
|
||||
@@ -392,17 +571,10 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
|
||||
<option value="2">Linear (never wrap)</option>
|
||||
<option value="3">None (not recommended)</option>
|
||||
</select><br>
|
||||
<span class="wc">
|
||||
Auto-calculate white channel from RGB:<br>
|
||||
<select name="AW">
|
||||
<option value=0>None</option>
|
||||
<option value=1>Brighter</option>
|
||||
<option value=2>Accurate</option>
|
||||
<option value=3>Dual</option>
|
||||
<option value=4>Legacy</option>
|
||||
</select>
|
||||
<br></span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
<hr style="width:260px">
|
||||
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"> <input type="button" value="Apply" onclick="loadCfg(d.Sf.data2);"><br></div>
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Misc Settings</title>
|
||||
<script>
|
||||
var d = document;
|
||||
function H()
|
||||
{
|
||||
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings");
|
||||
@@ -17,6 +18,34 @@
|
||||
{
|
||||
window.open("/update","_self");
|
||||
}
|
||||
function gId(s)
|
||||
{
|
||||
return d.getElementById(s);
|
||||
}
|
||||
function isObject(item) {
|
||||
return (item && typeof item === 'object' && !Array.isArray(item));
|
||||
}
|
||||
var timeout;
|
||||
function showToast(text, error = false)
|
||||
{
|
||||
var x = gId("toast");
|
||||
x.innerHTML = text;
|
||||
x.className = error ? "error":"show";
|
||||
clearTimeout(timeout);
|
||||
x.style.animation = 'none';
|
||||
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
|
||||
}
|
||||
function uploadFile(fO,name) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
|
||||
req.addEventListener('error', function(e){showToast(e.stack,true);});
|
||||
req.open("POST", "/upload");
|
||||
var formData = new FormData();
|
||||
formData.append("data", fO.files[0], name);
|
||||
req.send(formData);
|
||||
fO.value = '';
|
||||
return false;
|
||||
}
|
||||
function GetV()
|
||||
{
|
||||
//values injected by server while sending HTML
|
||||
@@ -44,6 +73,14 @@
|
||||
<h3>Software Update</h3>
|
||||
<button type="button" onclick="U()">Manual OTA Update</button><br>
|
||||
Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
|
||||
<h3>Backup & Restore</h3>
|
||||
<a class="btn lnk" href="/presets.json?download" target="download-frame">Backup presets</a><br>
|
||||
<div>Restore presets<br><input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/presets.json');"><br></div><br>
|
||||
<a class="btn lnk" href="/cfg.json?download" target="download-frame">Backup configuration</a><br>
|
||||
<div>Restore configuration<br><input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/cfg.json');"><br></div>
|
||||
<div style="color: #fa0;">⚠ Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
|
||||
Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
|
||||
For security reasons, passwords are not backed up.
|
||||
<h3>About</h3>
|
||||
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
@@ -51,7 +88,9 @@
|
||||
(c) 2016-2021 Christian Schwinne <br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
|
||||
Server message: <span class="sip"> Response error! </span><hr>
|
||||
<div id="toast"></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
</form>
|
||||
<iframe name=download-frame style='display:none;'></iframe>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,23 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>Sync Settings</title>
|
||||
<script>var d=document;
|
||||
function gId(s)
|
||||
{
|
||||
return d.getElementById(s);
|
||||
}
|
||||
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}
|
||||
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
|
||||
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
|
||||
function SP(){var p = d.Sf.DI.value; d.getElementById("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
|
||||
function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();}
|
||||
function FC()
|
||||
{
|
||||
for(j=0;j<8;j++)
|
||||
{
|
||||
gId("G"+(j+1)).checked=gId("GS").value>>j&1;
|
||||
gId("R"+(j+1)).checked=gId("GR").value>>j&1;
|
||||
}
|
||||
}
|
||||
function GC()
|
||||
{
|
||||
var a=0, b=0;
|
||||
|
||||
var m=1;
|
||||
for(j=0;j<8;j++)
|
||||
{
|
||||
a+=gId("G"+(j+1)).checked*m;
|
||||
b+=gId("R"+(j+1)).checked*m;
|
||||
m*=2;
|
||||
}
|
||||
gId("GS").value=a;
|
||||
gId("GR").value=b;
|
||||
}
|
||||
function SP(){var p = d.Sf.DI.value; gId("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
|
||||
function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();FC();}
|
||||
function S(){GetV();SetVal();}
|
||||
function GetV(){var d=document;}
|
||||
</script>
|
||||
<style>@import url("style.css");</style></head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<form id="form_s" name="Sf" method="post" onsubmit="GC()">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>Sync setup</h2>
|
||||
<h3>WLED Broadcast</h3>
|
||||
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>
|
||||
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>
|
||||
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br><br>
|
||||
<input name="GS" id="GS" type="number" style="display: none;"> <!-- hidden inputs for bitwise group checkboxes -->
|
||||
<input name="GR" id="GR" type="number" style="display: none;">
|
||||
<table style="margin: 0 auto;">
|
||||
<tr>
|
||||
<td>Sync groups</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
<td>6</td>
|
||||
<td>7</td>
|
||||
<td>8</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Send:</td>
|
||||
<td><input type="checkbox" id="G1" name="G1"></td>
|
||||
<td><input type="checkbox" id="G2" name="G2"></td>
|
||||
<td><input type="checkbox" id="G3" name="G3"></td>
|
||||
<td><input type="checkbox" id="G4" name="G4"></td>
|
||||
<td><input type="checkbox" id="G5" name="G5"></td>
|
||||
<td><input type="checkbox" id="G6" name="G6"></td>
|
||||
<td><input type="checkbox" id="G7" name="G7"></td>
|
||||
<td><input type="checkbox" id="G8" name="G8"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Receive:</td>
|
||||
<td><input type="checkbox" id="R1" name="R1"></td>
|
||||
<td><input type="checkbox" id="R2" name="R2"></td>
|
||||
<td><input type="checkbox" id="R3" name="R3"></td>
|
||||
<td><input type="checkbox" id="R4" name="R4"></td>
|
||||
<td><input type="checkbox" id="R5" name="R5"></td>
|
||||
<td><input type="checkbox" id="R6" name="R6"></td>
|
||||
<td><input type="checkbox" id="R7" name="R7"></td>
|
||||
<td><input type="checkbox" id="R8" name="R8"></td>
|
||||
</tr>
|
||||
</table><br>
|
||||
Receive: <input type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" name="RX">Effects<br>
|
||||
Send notifications on direct change: <input type="checkbox" name="SD"><br>
|
||||
Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
|
||||
@@ -28,7 +91,7 @@ Send notifications twice: <input type="checkbox" name="S2"><br>
|
||||
<i>Reboot required to apply changes. </i>
|
||||
<h3>Instance List</h3>
|
||||
Enable instance list: <input type="checkbox" name="NL"><br>
|
||||
Make this instance discoverable: <input type="checkbox" name="NB"><br>
|
||||
Make this instance discoverable: <input type="checkbox" name="NB">
|
||||
<h3>Realtime</h3>
|
||||
Receive UDP realtime: <input type="checkbox" name="RD"><br><br>
|
||||
<i>Network DMX input</i><br>
|
||||
@@ -36,7 +99,6 @@ Type:
|
||||
<select name=DI onchange="SP(); adj();">
|
||||
<option value=5568>E1.31 (sACN)</option>
|
||||
<option value=6454>Art-Net</option>
|
||||
<option value=4048>DDP</option>
|
||||
<option value=0 selected>Custom port</option>
|
||||
</select><br>
|
||||
<div id=xp>Port: <input name="EP" type="number" min="1" max="65535" value="5568" class="d5" required><br></div>
|
||||
@@ -83,6 +145,7 @@ Password: <input type="password" name="MQPASS" maxlength="64"><br>
|
||||
Client ID: <input name="MQCID" maxlength="40"><br>
|
||||
Device Topic: <input name="MD" maxlength="32"><br>
|
||||
Group Topic: <input name="MG" maxlength="32"><br>
|
||||
Publish on button press: <input type="checkbox" name="BM"><br>
|
||||
<i>Reboot required to apply changes. </i><a href="https://github.com/Aircoookie/WLED/wiki/MQTT" target="_blank">MQTT info</a>
|
||||
<h3>Philips Hue</h3>
|
||||
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
|
||||
|
||||
@@ -128,6 +128,9 @@
|
||||
<option value="16">ACST</option>
|
||||
<option value="17">ACST/ACDT</option>
|
||||
<option value="18">HST (Hawaii)</option>
|
||||
<option value="19">NOVT (Novosibirsk)</option>
|
||||
<option value="20">AKST/AKDT (Anchorage)</option>
|
||||
<option value="21">MX-CST/CDT</option>
|
||||
</select><br>
|
||||
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
|
||||
Current local time is <span class="times">unknown</span>.<br>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
<title>UI Settings</title>
|
||||
<script>
|
||||
@@ -10,16 +10,19 @@
|
||||
var sett = null;
|
||||
var l = {
|
||||
"comp":{
|
||||
"labels":"Show button labels",
|
||||
"colors":{
|
||||
"LABEL":"Color selection methods",
|
||||
"picker": "Color Wheel",
|
||||
"rgb": "RGB sliders",
|
||||
"quick": "Quick color selectors",
|
||||
"hex": "HEX color input"
|
||||
},
|
||||
"pcmbot": "Show bottom tab bar in PC mode",
|
||||
"pid": "Show preset IDs"
|
||||
"labels":"Show button labels",
|
||||
"colors":{
|
||||
"LABEL":"Color selection methods",
|
||||
"picker": "Color Wheel",
|
||||
"rgb": "RGB sliders",
|
||||
"quick": "Quick color selectors",
|
||||
"hex": "HEX color input"
|
||||
},
|
||||
"pcmbot": "Show bottom tab bar in PC mode",
|
||||
"pid": "Show preset IDs",
|
||||
"seglen": "Set segment length instead of stop LED",
|
||||
"css": "Enable custom CSS",
|
||||
"hdays": "Enable custom Holidays list"
|
||||
},
|
||||
"theme":{
|
||||
"alpha": {
|
||||
@@ -34,7 +37,6 @@
|
||||
"bg":"BG HEX color"
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
function gId(s)
|
||||
{
|
||||
@@ -52,10 +54,18 @@
|
||||
if( !tar[elem] ) tar[elem] = {}
|
||||
tar = tar[elem];
|
||||
}
|
||||
|
||||
tar[pList[len-1]] = val;
|
||||
}
|
||||
|
||||
var timeout;
|
||||
function showToast(text, error = false)
|
||||
{
|
||||
var x = gId("toast");
|
||||
x.innerHTML = text;
|
||||
x.className = error ? "error":"show";
|
||||
clearTimeout(timeout);
|
||||
x.style.animation = 'none';
|
||||
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
|
||||
}
|
||||
function addRec(s, path = "", label = null)
|
||||
{
|
||||
var str = "";
|
||||
@@ -181,16 +191,24 @@
|
||||
gId("theme_bg_random").checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
function uploadFile(fO,name) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
|
||||
req.addEventListener('error', function(e){showToast(e.stack,true);});
|
||||
req.open("POST", "/upload");
|
||||
var formData = new FormData();
|
||||
formData.append("data", fO.files[0], name);
|
||||
req.send(formData);
|
||||
fO.value = '';
|
||||
return false;
|
||||
}
|
||||
function GetV(){var d=document;}
|
||||
</script>
|
||||
<style>
|
||||
@import url("style.css");
|
||||
</style>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div style="position:sticky;top:0;background-color:#222;">
|
||||
<div style="position:sticky;top:0;background-color:#222;z-index:1;">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br>
|
||||
<span id="lssuc" style="color:green; display:none">✔ Local UI settings saved!</span>
|
||||
@@ -198,7 +216,7 @@
|
||||
</div>
|
||||
<h2>Web Setup</h2>
|
||||
Server description: <input name="DS" maxlength="32"><br>
|
||||
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
|
||||
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
|
||||
<i>The following UI customization settings are unique both to the WLED device and this browser.<br>
|
||||
You will need to set them again if using a different browser, device or WLED IP address.<br>
|
||||
Refresh the main UI to apply changes.</i><br>
|
||||
@@ -207,8 +225,9 @@
|
||||
|
||||
<h3>UI Appearance</h3>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_labels" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_pcmbot" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_pid" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_pcmbot" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_pid" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_seglen" class="agi cb"><br>
|
||||
I hate dark mode: <input type="checkbox" id="dm" onchange="UI()"><br>
|
||||
<span id="idonthateyou" style="display:none"><i>Why would you? </i>🥺<br></span>
|
||||
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_tab" class="agi"><br>
|
||||
@@ -217,6 +236,11 @@
|
||||
<span class="l">BG image URL</span>: <input id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br>
|
||||
<span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br>
|
||||
<input id="theme_base" class="agi" style="display:none">
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_css" class="agi cb"><br>
|
||||
<div id="skin">Custom CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/skin.css');"><br></div>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_hdays" class="agi cb"><br>
|
||||
<div id="holidays">Holidays: <input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/holidays.json');"><br></div>
|
||||
<div id="toast"></div>
|
||||
<hr><button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
c += ' max="39" min="-1" style="width:40px;"';
|
||||
t = "int";
|
||||
} else {
|
||||
c += ' step="0.00001" style="width:80px;"';
|
||||
c += ' step="any" style="width:100px;"';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -31,20 +31,20 @@
|
||||
Network name (SSID, empty to not connect): <br><input name="CS" maxlength="32"><br>
|
||||
Network password: <br> <input type="password" name="CP" maxlength="63"><br>
|
||||
Static IP (leave at 0.0.0.0 for DHCP):<br>
|
||||
<input name="I0" type="number" min="0" max="255" required> .
|
||||
<input name="I1" type="number" min="0" max="255" required> .
|
||||
<input name="I2" type="number" min="0" max="255" required> .
|
||||
<input name="I3" type="number" min="0" max="255" required><br>
|
||||
<input name="I0" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="I1" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="I2" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="I3" type="number" class="s" min="0" max="255" required><br>
|
||||
Static gateway:<br>
|
||||
<input name="G0" type="number" min="0" max="255" required> .
|
||||
<input name="G1" type="number" min="0" max="255" required> .
|
||||
<input name="G2" type="number" min="0" max="255" required> .
|
||||
<input name="G3" type="number" min="0" max="255" required><br>
|
||||
<input name="G0" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="G1" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="G2" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="G3" type="number" class="s" min="0" max="255" required><br>
|
||||
Static subnet mask:<br>
|
||||
<input name="S0" type="number" min="0" max="255" required> .
|
||||
<input name="S1" type="number" min="0" max="255" required> .
|
||||
<input name="S2" type="number" min="0" max="255" required> .
|
||||
<input name="S3" type="number" min="0" max="255" required><br>
|
||||
<input name="S0" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="S1" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="S2" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="S3" type="number" class="s" min="0" max="255" required><br>
|
||||
mDNS address (leave empty for no mDNS):<br/>
|
||||
http:// <input name="CM" maxlength="32"> .local<br>
|
||||
Client IP: <span class="sip"> Not connected </span> <br>
|
||||
@@ -52,7 +52,7 @@
|
||||
AP SSID (leave empty for no AP):<br> <input name="AS" maxlength="32"><br>
|
||||
Hide AP name: <input type="checkbox" name="AH"><br>
|
||||
AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63" pattern="(.{8,63})|()" title="Empty or min. 8 characters"><br>
|
||||
Access Point WiFi channel: <input name="AC" type="number" min="1" max="13" required><br>
|
||||
Access Point WiFi channel: <input name="AC" type="number" class="xs" min="1" max="13" required><br>
|
||||
AP opens:
|
||||
<select name="AB">
|
||||
<option value="0">No connection after boot</option>
|
||||
|
||||
@@ -9,16 +9,24 @@ body {
|
||||
hr {
|
||||
border-color: #666;
|
||||
}
|
||||
button {
|
||||
a {
|
||||
color: #28f;
|
||||
text-decoration: none;
|
||||
}
|
||||
button, .btn {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.3ch solid #333;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 8px;
|
||||
margin-top: 12px;
|
||||
margin: 12px 8px 8px;
|
||||
padding: 1px 6px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.lnk {
|
||||
border: 0;
|
||||
}
|
||||
.helpB {
|
||||
text-align: left;
|
||||
@@ -31,6 +39,9 @@ input {
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.5ch solid #333;
|
||||
}
|
||||
input:disabled {
|
||||
color: #888;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 4em;
|
||||
margin: 2px;
|
||||
@@ -38,14 +49,20 @@ input[type="number"] {
|
||||
input[type="number"].xxl {
|
||||
width: 100px;
|
||||
}
|
||||
input[type="number"].big {
|
||||
input[type="number"].xl {
|
||||
width: 85px;
|
||||
}
|
||||
input[type="number"].med {
|
||||
width: 55px;
|
||||
input[type="number"].l {
|
||||
width: 63px;
|
||||
}
|
||||
input[type="number"].small {
|
||||
width: 40px;
|
||||
input[type="number"].m {
|
||||
width: 56px;
|
||||
}
|
||||
input[type="number"].s {
|
||||
width: 49px;
|
||||
}
|
||||
input[type="number"].xs {
|
||||
width: 42px;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.5);
|
||||
@@ -63,3 +80,32 @@ td {
|
||||
.d5 {
|
||||
width: 4.5em !important;
|
||||
}
|
||||
|
||||
#toast {
|
||||
opacity: 0;
|
||||
background-color: #444;
|
||||
border-radius: 5px;
|
||||
bottom: 64px;
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
padding: 16px;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
z-index: 5;
|
||||
transform: translateX(-50%%); /* %% because of AsyncWebServer */
|
||||
max-width: 90%%; /* %% because of AsyncWebServer */
|
||||
left: 50%%; /* %% because of AsyncWebServer */
|
||||
}
|
||||
|
||||
#toast.show {
|
||||
opacity: 1;
|
||||
background-color: #264;
|
||||
animation: fadein 0.5s, fadein 0.5s 2.5s reverse;
|
||||
}
|
||||
|
||||
#toast.error {
|
||||
opacity: 1;
|
||||
background-color: #b21;
|
||||
animation: fadein 0.5s;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@ void handleDMX()
|
||||
|
||||
uint8_t brightness = strip.getBrightness();
|
||||
|
||||
for (int i = DMXStartLED; i < ledCount; i++) { // uses the amount of LEDs as fixture count
|
||||
uint16_t len = strip.getLengthTotal();
|
||||
for (int i = DMXStartLED; i < len; i++) { // uses the amount of LEDs as fixture count
|
||||
|
||||
uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462
|
||||
byte w = in >> 24 & 0xFF;
|
||||
byte r = in >> 16 & 0xFF;
|
||||
byte g = in >> 8 & 0xFF;
|
||||
byte b = in & 0xFF;
|
||||
byte w = W(in);
|
||||
byte r = R(in);
|
||||
byte g = G(in);
|
||||
byte b = B(in);
|
||||
|
||||
int DMXFixtureStart = DMXStart + (DMXGap * (i - DMXStartLED));
|
||||
for (int j = 0; j < DMXChannels; j++) {
|
||||
|
||||
@@ -34,9 +34,11 @@ void handleDDPPacket(e131_packet_t* p) {
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
|
||||
|
||||
for (uint16_t i = start; i < stop; i++) {
|
||||
setRealtimePixel(i, data[c], data[c+1], data[c+2], 0);
|
||||
c+=3;
|
||||
if (!realtimeOverride) {
|
||||
for (uint16_t i = start; i < stop; i++) {
|
||||
setRealtimePixel(i, data[c], data[c+1], data[c+2], 0);
|
||||
c+=3;
|
||||
}
|
||||
}
|
||||
|
||||
bool push = p->flags & DDP_PUSH_FLAG;
|
||||
@@ -102,6 +104,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
// update status info
|
||||
realtimeIP = clientIP;
|
||||
byte wChannel = 0;
|
||||
uint16_t totalLen = strip.getLengthTotal();
|
||||
|
||||
switch (DMXMode) {
|
||||
case DMX_MODE_DISABLED:
|
||||
@@ -114,7 +117,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride) return;
|
||||
wChannel = (dmxChannels-DMXAddress+1 > 3) ? e131_data[DMXAddress+3] : 0;
|
||||
for (uint16_t i = 0; i < ledCount; i++)
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[DMXAddress+0], e131_data[DMXAddress+1], e131_data[DMXAddress+2], wChannel);
|
||||
break;
|
||||
|
||||
@@ -129,7 +132,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
bri = e131_data[DMXAddress+0];
|
||||
strip.setBrightness(bri);
|
||||
}
|
||||
for (uint16_t i = 0; i < ledCount; i++)
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[DMXAddress+1], e131_data[DMXAddress+2], e131_data[DMXAddress+3], wChannel);
|
||||
break;
|
||||
|
||||
@@ -156,9 +159,9 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
col[3] = e131_data[DMXAddress+11]; //white
|
||||
colSec[3] = e131_data[DMXAddress+12];
|
||||
}
|
||||
transitionDelayTemp = 0; // act fast
|
||||
colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP
|
||||
return; // don't activate realtime live mode
|
||||
transitionDelayTemp = 0; // act fast
|
||||
colorUpdated(CALL_MODE_NOTIFICATION); // don't send UDP
|
||||
return; // don't activate realtime live mode
|
||||
break;
|
||||
|
||||
case DMX_MODE_MULTIPLE_DRGB:
|
||||
|
||||
@@ -32,6 +32,27 @@ bool deserializeConfigSec();
|
||||
void serializeConfig();
|
||||
void serializeConfigSec();
|
||||
|
||||
template<typename DestType>
|
||||
bool getJsonValue(const JsonVariant& element, DestType& destination) {
|
||||
if (element.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
destination = element.as<DestType>();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename DestType, typename DefaultType>
|
||||
bool getJsonValue(const JsonVariant& element, DestType& destination, const DefaultType defaultValue) {
|
||||
if(!getJsonValue(element, destination)) {
|
||||
destination = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//colors.cpp
|
||||
void colorFromUint32(uint32_t in, bool secondary = false);
|
||||
void colorFromUint24(uint32_t in, bool secondary = false);
|
||||
@@ -46,7 +67,9 @@ void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TOD
|
||||
|
||||
void colorFromDecOrHexString(byte* rgb, char* in);
|
||||
bool colorFromHexString(byte* rgb, const char* in);
|
||||
void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
|
||||
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb);
|
||||
|
||||
//dmx.cpp
|
||||
void initDMX();
|
||||
@@ -72,6 +95,12 @@ void onHueConnect(void* arg, AsyncClient* client);
|
||||
void sendHuePoll();
|
||||
void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
|
||||
|
||||
//improv.cpp
|
||||
void handleImprovPacket();
|
||||
void sendImprovStateResponse(uint8_t state, bool error = false);
|
||||
void sendImprovInfoResponse();
|
||||
void sendImprovRPCResponse(uint8_t commandId);
|
||||
|
||||
//ir.cpp
|
||||
bool decodeIRCustom(uint32_t code);
|
||||
void applyRepeatActions();
|
||||
@@ -87,6 +116,7 @@ void decodeIR44(uint32_t code);
|
||||
void decodeIR21(uint32_t code);
|
||||
void decodeIR6(uint32_t code);
|
||||
void decodeIR9(uint32_t code);
|
||||
void decodeIRJson(uint32_t code);
|
||||
|
||||
void initIR();
|
||||
void handleIR();
|
||||
@@ -98,7 +128,7 @@ void handleIR();
|
||||
#include "FX.h"
|
||||
|
||||
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
|
||||
bool deserializeState(JsonObject root, byte presetId = 0);
|
||||
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
|
||||
void serializeInfo(JsonObject root);
|
||||
@@ -155,11 +185,11 @@ void _drawOverlayCronixie();
|
||||
//playlist.cpp
|
||||
void shufflePlaylist();
|
||||
void unloadPlaylist();
|
||||
void loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
||||
int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
||||
void handlePlaylist();
|
||||
|
||||
//presets.cpp
|
||||
bool applyPreset(byte index);
|
||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
|
||||
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
||||
void deletePreset(byte index);
|
||||
|
||||
@@ -169,27 +199,42 @@ bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
|
||||
int getNumVal(const String* req, uint16_t pos);
|
||||
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
|
||||
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
||||
|
||||
//udp.cpp
|
||||
void notify(byte callMode, bool followUp=false);
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
|
||||
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
|
||||
void handleNotifications();
|
||||
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
|
||||
void refreshNodeList();
|
||||
void sendSysInfoUDP();
|
||||
|
||||
//util.cpp
|
||||
//bool oappend(const char* txt); // append new c string to temp buffer efficiently
|
||||
//bool oappendi(int i); // append new number to temp buffer efficiently
|
||||
//void sappend(char stype, const char* key, int val);
|
||||
//void sappends(char stype, const char* key, char* val);
|
||||
//void prepareHostname(char* hostname);
|
||||
//void _setRandomColor(bool _sec, bool fromButton);
|
||||
//bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
bool requestJSONBufferLock(uint8_t module=255);
|
||||
void releaseJSONBufferLock();
|
||||
|
||||
//um_manager.cpp
|
||||
class Usermod {
|
||||
public:
|
||||
virtual void loop() {}
|
||||
virtual void handleOverlayDraw() {}
|
||||
virtual bool handleButton(uint8_t b) { return false; }
|
||||
virtual void setup() {}
|
||||
virtual void connected() {}
|
||||
virtual void addToJsonState(JsonObject& obj) {}
|
||||
virtual void addToJsonInfo(JsonObject& obj) {}
|
||||
virtual void readFromJsonState(JsonObject& obj) {}
|
||||
virtual void addToConfig(JsonObject& obj) {}
|
||||
virtual bool readFromConfig(JsonObject& obj) { return true; } //Heads up! readFromConfig() now needs to return a bool
|
||||
virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h
|
||||
virtual void onMqttConnect(bool sessionPresent) {}
|
||||
virtual bool onMqttMessage(char* topic, char* payload) { return false; }
|
||||
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
||||
@@ -202,14 +247,13 @@ class UsermodManager {
|
||||
|
||||
public:
|
||||
void loop();
|
||||
|
||||
void handleOverlayDraw();
|
||||
bool handleButton(uint8_t b);
|
||||
void setup();
|
||||
void connected();
|
||||
|
||||
void addToJsonState(JsonObject& obj);
|
||||
void addToJsonInfo(JsonObject& obj);
|
||||
void readFromJsonState(JsonObject& obj);
|
||||
|
||||
void addToConfig(JsonObject& obj);
|
||||
bool readFromConfig(JsonObject& obj);
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
|
||||
@@ -379,7 +379,7 @@ String getContentType(AsyncWebServerRequest* request, String filename){
|
||||
if(request->hasArg("download")) return "application/octet-stream";
|
||||
else if(filename.endsWith(".htm")) return "text/html";
|
||||
else if(filename.endsWith(".html")) return "text/html";
|
||||
// else if(filename.endsWith(".css")) return "text/css";
|
||||
else if(filename.endsWith(".css")) return "text/css";
|
||||
// else if(filename.endsWith(".js")) return "application/javascript";
|
||||
else if(filename.endsWith(".json")) return "application/json";
|
||||
else if(filename.endsWith(".png")) return "image/png";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user