Compare commits

...

116 Commits

Author SHA1 Message Date
cschwinne
4d4a20e05e 0.13.0-b7
Mitigate Pixel 6 UI issue
2022-02-24 01:16:24 +01:00
cschwinne
c03d4f115f Fixed presets not triggering interface update 2022-02-23 19:42:34 +01:00
cschwinne
ed90b638a9 Main seg replaced by first selected in internal interfaces
Version bump to 0.13.0-b7
Various small improvements
2022-02-23 19:20:07 +01:00
cschwinne
94a0199955 Readme cleanup 2022-02-21 22:26:35 +01:00
cschwinne
44739c5198 Merge effectChanged and colorChanged to stateChanged 2022-02-21 22:12:13 +01:00
cschwinne
5f871bc01f HTTP API: Set segments directly in set.cpp 2022-02-21 20:48:11 +01:00
cschwinne
1f5971f15a Another HTTP API segment improvement 2022-02-21 18:31:19 +01:00
cschwinne
694466a196 Apply segment by Enter key on input field 2022-02-21 17:58:18 +01:00
cschwinne
03311d3776 Do not set main seg before apply
Setting mainseg before applyValuesToSelectedSegs() causes the updated value to not be set to other selected segments
2022-02-21 16:57:18 +01:00
cschwinne
ae0eba866a Improve Stream and fix HTTP segment application 2022-02-21 16:19:11 +01:00
cschwinne
906737bedb Changelog update 2022-02-20 02:15:34 +01:00
Christian Schwinne
7138e891be Add per-segment light capability info (#2552)
* Add per-segment light capability info

* Fix duplication

* Change to more lightweight seglc array

* Added segment capabilities.

* Differs and capabilities refactoring

* Add back selection differs option

Co-authored-by: Blaz Kristan <blaz@kristan-sp.si>
2022-02-20 00:20:22 +01:00
Christian Schwinne
53abe36b83 Merge pull request #2547 from Aircoookie/sync-segbounds2
Sync segment bounds
2022-02-19 23:00:31 +01:00
cschwinne
efbb7a034c Slight websocket reconnection tweaks 2022-02-19 22:47:17 +01:00
Blaz Kristan
05bc81bf4e Add default preset name if none specified. 2022-02-19 11:42:59 +01:00
Blaz Kristan
f8bc0bd2b5 Removed unnecessary if. 2022-02-18 19:23:55 +01:00
Blaz Kristan
cf94cb1092 Allow saving preset from IR
Removed double clolorUpdated() call
2022-02-18 18:35:51 +01:00
Blaz Kristan
02d92e32c7 Parsing IR JSON cmd fix. 2022-02-18 17:01:34 +01:00
Blaž Kristan
7f92607b85 Added WS reconnect on error toast. 2022-02-17 12:51:37 +01:00
Blaž Kristan
3be4b69b44 WS reconnect logic & WS memory leak protection 2022-02-17 12:45:50 +01:00
Tom D'Roza
bb9afcb304 Added: CSS hover effect on buttons within modal dialogs, e.g. Info, Nodes (#2545)
* Added: CSS hover effect on buttons within modal dialogs, e.g. Info,
Nodes

* Update index.css

Co-authored-by: Tom D'Roza <Tom.D'Roza>
Co-authored-by: Christian Schwinne <cschwinne@gmail.com>
2022-02-17 00:38:47 +01:00
cschwinne
e9a05890a5 Return to core 2.7.4 for higher stability 2022-02-17 00:17:00 +01:00
cschwinne
613809c2af Do not turn rpt segments off 2022-02-16 23:54:14 +01:00
cschwinne
7b969bb8c2 Various state changed logic simplifications
Changed main segment, must be selected
2022-02-16 21:12:33 +01:00
cschwinne
7aef551292 Segment bounds sync option 2022-02-16 14:55:35 +01:00
cschwinne
447b811fa0 Remove build flag leading to wdt reset on some boards 2022-02-14 18:49:13 +01:00
dependabot[bot]
435040814d Bump ajv from 6.12.2 to 6.12.6 (#2543)
Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.2 to 6.12.6.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.2...v6.12.6)

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

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-14 15:35:13 +01:00
Blaž Kristan
9987416a4a Allow float array values in usermod config. 2022-02-14 12:19:33 +01:00
Blaž Kristan
31e33e0a8b Fix for creating segments if config set to length. 2022-02-14 08:15:35 +01:00
cschwinne
b211d8b085 Fix SSDR usermod if SN_Photoresistor mod is not included 2022-02-10 19:48:13 +01:00
Blaž Kristan
83416ee2e0 Merge pull request #2530 from Proto-molecule/patch-api
bugs, json remote repeat, cmd &R=
2022-02-10 14:06:34 +01:00
Blaz Kristan
fa981a389f Add transitions to other segments. 2022-02-10 13:59:59 +01:00
Blaz Kristan
55817f31f9 Merge branch 'master' into patch-api 2022-02-10 13:48:48 +01:00
Blaž Kristan
6d2ef4e0bf Merge pull request #2539 from dylan09/multirelay-discovery
Fixed buffer overflow in HA autodiscovery. #2538
2022-02-10 00:33:16 +01:00
ulrich
4d714cf9a4 Fixed buffer overflow in HA autodiscovery. #2538 2022-02-09 23:08:42 +01:00
cschwinne
930ded6767 Fix touch pin 2022-02-09 19:59:17 +01:00
Blaz Kristan
4cdb18907f Fix for Four Line Display usermod. 2022-02-09 19:27:52 +01:00
Henry Gabryjelski
38bc618ee5 Float and better 3rd party library compatibility (#2534)
* define as float (not double)

* Avoid #define of 1 or 2 char symbols

Having this file define 'A' and 'C' pollutes
the global namespace, and causes conflicts
with other libraries that also pollute the
global namespace with short #defines.
It's easier to fix this header.

* unused variable warning
2022-02-09 09:46:54 +01:00
Christian Schwinne
00b0193a43 Fix re-init segment data leak (fixes #2535 ) (#2536) 2022-02-09 08:43:35 +01:00
bole5
f9bce54104 Change Default Relay Pin to -1 (#2531) 2022-02-08 00:15:24 +01:00
bole5
7ee14724fc Improve Pin Manager Debugging (#2532) 2022-02-08 00:03:20 +01:00
Blaz Kristan
4eb0dbb5a4 repeat actions cleanup & fix 2022-02-07 11:13:12 +01:00
Proto-molecule
8c5b3fe23e bugs, json remote repeat, cmd &R= 2022-02-06 19:00:03 -08:00
cschwinne
97f8eea302 Refactored isRgbw to hasWhiteChannel() 2022-02-04 13:28:00 +01:00
cschwinne
04d5932252 Un-F()-ed some strings
(that were either occuring at least 4 times, or were F()-ed in some places and not in others)
2022-02-04 10:10:37 +01:00
cschwinne
b33c5798ee Changelog update 2022-02-03 23:37:30 +01:00
Blaz Kristan
6180c2f948 Fix for overallocated LiveView buffer. 2022-02-03 20:21:09 +01:00
Christian Schwinne
795c515999 Merge pull request #2517 from ChuckMash/serial+
Extend Serial functions. Baudrate changeable during runtime and fast LED data retrieval
2022-02-01 20:07:30 +01:00
cschwinne
00dbdc2267 Baud rate setting 2022-02-01 20:02:46 +01:00
cschwinne
32286888e5 PinManager, cleanup and tmp2 out 2022-02-01 18:21:30 +01:00
Christian Schwinne
565d8d8f04 Binary Websockets for Peek (#2516)
* Binary Websockets for Peek

* No IRAM_ATTR here

* Use builtin LittleFS for all ESP32 builds

* Attempt LittleFS compilation fix

* Use tasmota zip for all ESP32 builds

* Revert to Arduino Core 1 for the time being
2022-02-01 12:02:04 +01:00
Blaž Kristan
0a5a0bef48 Enhanced usermods. (#2522) 2022-02-01 09:33:57 +01:00
Blaz Kristan
6e0e5c102e Added extractModeName() utility function. 2022-01-31 20:43:35 +01:00
Blaz Kristan
be8a9ae73b setPixelSegment() optimization 2022-01-31 17:56:21 +01:00
ChuckMash
22fbb0e35b Update wled_serial.cpp
Adds serial functionality

Can now change baud rate during runtime to be faster
Retrieve LED strip data as JSON blob
Retrieve LED strip data as BYTES (fast!)
2022-01-28 23:35:40 -08:00
Blaž Kristan
e17203ca1b Fix for expand timed presets. 2022-01-28 13:51:52 +01:00
Blaz Kristan
3170fa2208 Playlist bugfix. 2022-01-27 21:00:43 +01:00
Blaz Kristan
07216db864 Merge branch 'master' of https://github.com/aircoookie/WLED 2022-01-27 19:26:56 +01:00
Blaz Kristan
fec870f264 Fix for default action not triggering colorUpdated 2022-01-27 19:26:53 +01:00
cschwinne
2c5eba335f Added white channel to Peek (closes #1716) 2022-01-26 13:26:57 +01:00
Mike Ryan
fb19f1ecbc Allow overriding of color order by LED pixel range. (#2463)
* Overridable color order

- Use `ColorOrderMap` to hold optional color order overrides for ranges
  of LEDs.
- Serialization of config to/from filesystem is complete.
- Back-end configuration is complete.
- TODO: front-end changes to the LED settings page.

* Add Color order override settings

- Adds color order override section to settings page.

* PR Feedback

- Limit max number of color order overrides to 5 on ESP8266
- Only append color overrides if they were provided in the POST of LED
  settings.
2022-01-26 00:42:04 +01:00
Blaž Kristan
e879fe5843 Remove obsolete usermods. (#2510) 2022-01-25 16:42:35 +01:00
Blaž Kristan
0ca7699fe5 Merge pull request #2427 from Aircoookie/sync-seg
Sync segment options.
2022-01-25 12:54:24 +01:00
Blaž Kristan
7f6adfa331 Converted indentation tabs to spaces. 2022-01-25 12:47:14 +01:00
cschwinne
5f0b102671 Send segment ID, start, stop, and cct 2022-01-24 18:31:05 +01:00
cschwinne
d28eb6ae21 Repeat other seg than 0 bugfix 2022-01-24 16:44:47 +01:00
André Klitzing
eca980dfca Add initial support for ESP32-C3 (#2454)
* WIP Add support for ESP32-C3

* Add esp32c3 to default_envs

* Use new platform from tasmota

* Switch back to 2.8.1 as it seems by fixed
2022-01-24 11:34:02 +01:00
Jason2866
742c792ec7 Use latest Arduino ESP32 (#2502)
build with IDF44-rc1 and Arduino from 21.01.2022. Toolchains updated to 8.4.0 2021r2-patch2
The platform can be used for ESP32, ESP32-S2 and ESP32-C3
2022-01-24 11:28:49 +01:00
Blaž Kristan
9b062f33c5 Merge pull request #2450 from frankalicious/patch-1
fix name of image for ST7789 usermod
2022-01-22 23:07:15 +01:00
Blaž Kristan
ea15c2245e Merge pull request #2497 from herm/usermod_multirelay
Add Home Assisant MQTT autodiscovery for usermod multi_relay.
2022-01-22 22:57:24 +01:00
Blaz Kristan
26ae6d3691 Added config option for HA autodiscovery. 2022-01-22 20:49:43 +01:00
cschwinne
f97bc9dba8 Fix DMX menu settings item 2022-01-21 20:35:30 +01:00
Blaz Kristan
fe6b1c13c4 Periodic broadcasts. 2022-01-21 16:08:02 +01:00
Blaz Kristan
5608425a12 Added comments.
Fix for incorrect boot state.
2022-01-21 15:55:25 +01:00
cschwinne
f784b01d20 Update year 2022-01-21 01:48:50 +01:00
Hermann Kraus
2648eba5bf Deprecate usermod mqtt_switch. (#2499) 2022-01-20 23:31:28 +01:00
Blaz Kristan
255347ab77 Minor clenup. 2022-01-20 17:38:18 +01:00
Hermann Kraus
52c36ef6a4 Add Home Assisant MQTT autodiscovery for usermod multi_relay. 2022-01-20 00:30:17 +01:00
Blaz Kristan
e54819e7e5 Merge branch 'master' into sync-seg 2022-01-15 14:08:08 +01:00
Blaz Kristan
7eb029dcb6 Complete segment syncing.
Reduced complexity of colorUpdated regarding effect/color change.
Minor optimizations.
2022-01-15 14:04:16 +01:00
André Klitzing
f8c80283e4 Use arduino-esp32 2.0.2 for ESP32-S2 (#2452)
* Use arduino-esp32 2.0.2 for ESP32-S2

LittleFS is merged into it.

* Fix filesystem error for ESP32-S2

Use platform of tasmota until upstream released it and
use board_build.flash_mode = qio

* Fix empty disk usage

* Add esp32s2_saola to default_envs

* Remove lorol LITTLEFS for esp32dev, too

* Revert "Remove lorol LITTLEFS for esp32dev, too"

This reverts commit 3586d5eef7.
2022-01-14 17:19:33 +01:00
Blaž Kristan
04f5bdb843 Fix for NTP sync on millis() rollover.
AP mode delay on boot (#2242).
2022-01-10 13:53:11 +01:00
Blaz Kristan
aba4dc7c50 Bugfix for analog button read limitation. 2022-01-09 15:13:58 +01:00
cschwinne
7fb46cf982 WIP: Segment loop size byte 2022-01-09 14:16:07 +01:00
Blaz Kristan
ae8281f835 error in udpIn 2022-01-07 21:16:14 +01:00
Blaž Kristan
fa35293618 Full segment syncing. 2022-01-07 19:12:06 +01:00
Blaž Kristan
20ccca0aec Swapped grouping and spacing in UDP packet. 2022-01-07 17:53:07 +01:00
Blaž Kristan
10e216da6b Fix for missing middle segment. 2022-01-07 17:48:46 +01:00
Blaž Kristan
6491353a57 Missing rptSeg() 2022-01-07 17:31:28 +01:00
IgorMarkovic
9f44f989e5 Fix staircase mode segment 1 animation (#2469) 2022-01-05 16:09:25 +01:00
cschwinne
33f72e40da Replace icon unavailable on some mobile browsers 2022-01-02 18:06:42 +01:00
Blaž Kristan
18868a5bd6 Date controlled timed presets. (#2447)
* Date controlled timed presets.

* C/P fix for sunset.

* Fixed % escape character

* Date range support

* Date logic fix

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-12-31 14:09:48 +01:00
Christian Schwinne
754682577c Configurable framerate (#2444)
Updated arduino core versions
Better performance on esp32 core 3.x due to IRAM_ATTR
Fixed analog busses init to full white/on
2021-12-30 01:48:27 +01:00
frankalicious
aef0243b73 fix name of image for ST7789 usermod 2021-12-26 11:06:20 +01:00
akshay rajput
736053e24e Patch to remove preceding zero of hour digit for 7segment usermod (#2445)
* Update usermod_seven_segment_reloaded.h

* Update usermod_seven_segment_reloaded.h
2021-12-26 02:29:56 +01:00
Blaz Kristan
2c14181051 LAT/LON helper for Samsung devices. 2021-12-23 20:32:45 +01:00
Blaz Kristan
296fe4b62e Unload playlist on timed presets. 2021-12-21 22:46:06 +01:00
Blaz Kristan
118f02fd11 Unload playlist on timed presets. 2021-12-21 18:47:21 +01:00
Blaz Kristan
990d0f6e3e Fix for skipping reset of segments when changing spacing. 2021-12-21 18:26:51 +01:00
Blaz Kristan
84624666ce Incorrect delete fix. 2021-12-21 18:16:25 +01:00
Blaz Kristan
8bd716c056 Prevent undefind FX behaviour on millis() rollover 2021-12-20 16:43:47 +01:00
Andy Hofmann
cd95abb2a1 Usermod quinled-an-penta: Updated IOs for v1r1 release (#2429)
* UM QuinLED-An-Penta: First version

* UM QuinLED-An-Penta: Made OLED seconds a setting; small improvements

* UM QuinLED-An-Penta: Fixed unique ID

* Merge branch 'master' of https://github.com/Aircoookie/WLED

* UM QuinLED-An-Penta: Fixed config loading

* UM QuinLED-An-Penta: Replaced ledcRead() with calculating the percentage

* UM QuinLED-An-Penta: Fixed temp sensor readings

* UM QuinLED-An-Penta: Removing OLED bus clk setting

* UM QuinLED-An-Penta: ETH support, lots of OLED improvements

* UM quinled-an-penta: v1r1 adjustments
2021-12-20 01:41:37 +01:00
Blaz Kristan
1270f2d577 Sync segment options.
Freeze effect.
Repeat last segment until end.
2021-12-17 20:33:48 +01:00
Blaž Kristan
c27117e99e Merge pull request #2415 from Aircoookie/i2c-sharing
Pin manager support for sharing multipin buses.
2021-12-16 19:57:08 +01:00
Blaž Kristan
28556790d6 Removed loadInfo() in animated staircase. 2021-12-14 10:35:50 +01:00
Blaž Kristan
41c9bb63a0 Pin manager support for sharing multipin buses. 2021-12-14 09:38:38 +01:00
Roman Reitschmied
7d5e2466f0 add ability get LDR value from other usermods (#2408) 2021-12-12 00:31:54 +01:00
Roman Reitschmied
d3f35955d6 New flexible usermod for seven segment displays (#2409)
* add first version

* added max/min brightness to autoldr functionality

* added more information to the readme
2021-12-12 00:31:21 +01:00
cschwinne
fb338c0728 Button preset call mode
(stops main segment values from being applied to all presets)
2021-12-11 23:44:21 +01:00
cschwinne
2ce8f1ee5d Skip first and apply preset fixes 2021-12-11 19:30:11 +01:00
Blaz Kristan
3f0258e215 Playlist corrupting JSON buffer bugfix. 2021-12-10 20:45:37 +01:00
Christian Schwinne
e72a8d999f Merge pull request #2393 from guardmedia/blends-effect
Improved speed and reduced memory usage for Blends effect
2021-12-10 18:42:46 +01:00
Blaž Kristan
fed16fd14e Fix for disconnects on ESP8266 with static JSON buffer 2021-12-10 17:30:57 +01:00
Blaž Kristan
5dbc45ecb9 Fix for incorrectly placed release JSON buffer. 2021-12-10 17:06:04 +01:00
Blaz Kristan
094bdb29b6 Fix for >10 buttons. 2021-12-08 22:41:16 +01:00
Tyler Walters
eab132ed32 Improved speed and reduced memory usage for Blends effect
color_blend() function only generates 256 colors from the palette before repeating. Improved the function to use either 256 max or the segment length if shorter.
2021-12-06 22:17:35 -05:00
106 changed files with 7166 additions and 5437 deletions

View File

@@ -45,7 +45,7 @@ body:
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)"
placeholder: "e.g. WLED 0.13.0-b7 (build 2202222)"
validations:
required: true
- type: dropdown

View File

@@ -1,7 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -2,28 +2,65 @@
### Builds after release 0.12.0
#### Build 2202222
- Version bump to 0.13.0-b7 "Toki"
- Fixed HTTP API commands not applying to all selected segments in some conditions
- Blynk support is not compiled in by default on ESP32 builds
#### Build 2202210
- Fixed HTTP API commands not applying to all selected segments if called from JSON
- Improved Stream effects, no longer rely on LED state and won't fade out at low brightness
#### Build 2202200
- Added `info.leds.seglc` per-segment light capability info (PR #2552)
- Fixed `info.leds.rgbw` behavior
- Segment bounds sync (PR #2547)
- WebSockets auto reconnection and error handling
- Disable relay pin by default (PR #2531)
- Various fixes (ESP32 touch pin 33, floats, PR #2530, #2534, #2538)
- Deprecated `info.leds.cct`, `info.leds.wv` and `info.leds.rgbw`
- Deprecated `/url` endpoint
#### Build 2202030
- Switched to binary format for WebSockets peek (PR #2516)
- Playlist bugfix
- Added `extractModeName()` utility function
- Added serial out (PR #2517)
- Added configurable baud rate
#### Build 2201260
- Initial ESP32-C3 and ESP32-S2 support (PRs #2452, #2454, #2502)
- Full segment sync (PR #2427)
- Allow overriding of color order by ranges (PR #2463)
- Added white channel to Peek
#### Build 2112080
- Version bump to 0.13.0-b6 "Toki"
- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries
- 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)
- 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
- 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
- Added CCT and white balance correction support (PR #2285)
- Unified UI slider style
- Added LED settings config template upload
#### Build 2111220

8
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.0-b6",
"version": "0.13.0-b7",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -28,9 +28,9 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"ajv": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",

View File

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

View File

@@ -12,10 +12,10 @@
; default_envs = travis_esp8266, travis_esp32
# Release binaries
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32_eth_ota1mapp, esp32s2_saola, esp32c3
# 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
; default_envs = esp32dev, esp8285_4CH_MagicHome, 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_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
# Single binaries (uncomment your board)
; default_envs = elekstube_ips
@@ -30,12 +30,13 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth
; default_envs = d1_mini_ota
; default_envs = esp32dev
; default_envs = esp8285_4CH_MagicHome
; default_envs = esp8285_4CH_H801
; default_envs = esp8285_5CH_H801
; default_envs = esp8285_H801
; default_envs = d1_mini_5CH_Shojo_PCB
; default_envs = wemos_shield_esp32
; default_envs = m5atom
; default_envs = esp32_eth
; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola
src_dir = ./wled00
data_dir = ./wled00/data
@@ -54,6 +55,7 @@ extra_configs =
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
arduino_core_3_0_2 = espressif8266@3.2.0
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
@@ -77,10 +79,8 @@ 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_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?
# ldscript_4m3m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 3040 KB spiffs, 16 KB reserved
#
# Available lwIP variants (macros):
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
@@ -160,10 +160,9 @@ upload_speed = 115200
# ------------------------------------------------------------------------------
lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.4.0
IRremoteESP8266 @ 2.7.18
https://github.com/lorol/LITTLEFS.git
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2
fastled/FastLED @ 3.5.0
IRremoteESP8266 @ 2.8.1
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.4
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
#For use SSD1306 OLED display uncomment following
@@ -177,14 +176,15 @@ lib_deps =
; adafruit/Adafruit CCS811 Library @ 1.0.4
; adafruit/Adafruit Si7021 Library @ 1.4.0
extra_scripts = ${scripts_defaults.extra_scripts}
extra_scripts = ${scripts_defaults.extra_scripts}
[esp8266]
build_flags =
-DESP8266
-DFP_IN_IROM
;-Wno-deprecated-declarations
;-Wno-register
-Wno-register
-Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
@@ -198,27 +198,33 @@ build_flags =
lib_deps =
${env.lib_deps}
#https://github.com/lorol/LITTLEFS.git
# 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]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip
platform = espressif32@3.5.0
build_flags = -g
-DARDUINO_ARCH_ESP32
-DCONFIG_LITTLEFS_FOR_IDF_3_2
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
-D CONFIG_ASYNC_TCP_USE_WDT=0
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps =
${env.lib_deps}
https://github.com/lorol/LITTLEFS.git
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
@@ -229,6 +235,19 @@ lib_deps =
makuna/NeoPixelBus @ 2.6.7
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
[esp32c3]
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3
-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
# ------------------------------------------------------------------------------
@@ -245,7 +264,9 @@ lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps}
@@ -298,33 +319,47 @@ lib_deps = ${esp8266.lib_deps}
[env:esp32dev]
board = esp32dev
platform = espressif32@2.0
platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
[env:esp32_eth]
board = esp32-poe
platform = espressif32@2.0
platform = ${esp32.platform}
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_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions}
# ESP32 ETH build that fits in old 1M app space (disables Blynk, Cronixie, and Hue sync)
[env:esp32_eth_ota1mapp]
extends = env:esp32_eth
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet_OTA -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC
[env:esp32s2_saola]
board = esp32dev
board_build.mcu = esp32s2
platform = espressif32
board = esp32-s2-saola-1
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
platform_packages =
framework = arduino
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.flash_mode = qio
upload_speed = 460800
build_unflags = ${common.build_unflags}
lib_deps = ${esp32s2.lib_deps}
[env:esp32c3]
board = esp32-c3-devkitm-1
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
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}
lib_deps = ${esp32c3.lib_deps}
[env:esp8285_4CH_MagicHome]
board = esp8285
@@ -335,16 +370,7 @@ 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
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:esp8285_5CH_H801]
[env:esp8285_H801]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
@@ -449,6 +475,13 @@ 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}
[env:athom15w]
board = esp_wroom_02
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_IC_CCT -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# travis test board configurations
# ------------------------------------------------------------------------------

View File

@@ -53,39 +53,18 @@ See the [documentation on our official site](https://kno.wled.ge)!
[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
## 🖼️ User interface
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
## 💾 Compatible LED Strips
Type | Voltage | Comments
|---|---|---|
WS2812B | 5v |
WS2813 | 5v |
SK6812 | 5v | RGBW
APA102 | 5v | C/D
WS2801 | 5v | C/D
LPD8806 | 5v | C/D
TM1814 | 12v | RGBW
WS2811 | 12v | 3-LED segments
WS2815 | 12v |
GS8208 | 12v |
Analog/non-addressable | any | Requires additional circuitry
## 🧊 Compatible PC RGB Fans and ARGB accessories
Brand | Model | Comments
|---|---|---|
Corsair | HD120 Fan | Uses WS2812B, data-in only
PCCOOLER | Moonlight 5-pack Fans | Uses WS2812B, includes Data-out connector to keep each fan uniquely addressable if wired in series like traditional LED strips
Any | 5v 3-pin ARGB for PC | Any PC RGB device that supports the 5v 3-pin ARGB motherboard header should work fine with WLED. All the major motherboard vendors support the Corsair HD120 and PCCOOLER fans listed, so we can safely assume any device that supports motherboard ARGB 5V 3-Pin standard will work with WLED.
## 💾 Compatible hardware
See [here](https://kno.wled.ge/basics/compatible-hardware)!
## ✌️ Other
Licensed under the MIT license
Credits [here](https://kno.wled.ge/about/contributors/)!
Uses Linearicons by Perxis!
Join the Discord server to discuss everything about WLED!
<a href="https://discord.gg/KuqP7NE"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>

View File

@@ -217,7 +217,7 @@ writeChunks(
mangle: (str) =>
str
.replace("%", "%%")
.replace(/User Interface\<\/button\>\<\/form\>/gms, "User Interface\<\/button\>\<\/form\>%DMXMENU%"),
.replace(/Usermods\<\/button\>\<\/form\>/gms, "Usermods\<\/button\>\<\/form\>%DMXMENU%"),
},
{
file: "settings_wifi.htm",

View File

@@ -111,17 +111,17 @@ class Animated_Staircase : public Usermod {
}
if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, 1);
segments->setOption(SEG_OPTION_ON, 1, i);
// We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0];
} else {
segments->setOption(SEG_OPTION_ON, 0, 1);
segments->setOption(SEG_OPTION_ON, 0, i);
}
// Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1);
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
}
@@ -296,7 +296,7 @@ class Animated_Staircase : public Usermod {
maxSegmentId = i - 1;
break;
}
segments->setOption(SEG_OPTION_ON, 1, 1);
segments->setOption(SEG_OPTION_ON, 1, i);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));
@@ -508,10 +508,10 @@ class Animated_Staircase : public Usermod {
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
if (enabled) {
btn += F("false}},false,false);loadInfo();\">");
btn += F("false}});\">");
btn += F("enabled");
} else {
btn += F("true}},false,false);loadInfo();\">");
btn += F("true}});\">");
btn += F("disabled");
}
btn += F("</button>");

View File

@@ -1,19 +0,0 @@
# ESP32 Touch Brightness Control
Toggle On/Off with a long press (800ms)
Switch through 5 brightness levels (defined in usermod_touchbrightness.h, values 0-255) with a short (100ms) touch
## Installation
Copy 'usermod_touchbrightness.h' to the wled00 directory.
in 'usermod_list.cpp' add this:
> #include "usermod_touchbrightness.h"
above "void registerUsermods()"
and
> usermods.add(new TouchBrightnessControl());
inside the "registerUsermods()" function

View File

@@ -1,89 +0,0 @@
//
// usermod_touchbrightness.h
// github.com/aircoookie/WLED
//
// Created by Justin Kühner on 14.09.2020.
// Copyright © 2020 NeariX. All rights reserved.
// https://github.com/NeariX67/
// Discord: @NeariX#4799
#pragma once
#include "wled.h"
#define threshold 40 //Increase value if touches falsely accur. Decrease value if actual touches are not recognized
#define touchPin T0 //T0 = D4 / GPIO4
//Define the 5 brightness levels
//Long press to turn off / on
#define brightness1 51
#define brightness2 102
#define brightness3 153
#define brightness4 204
#define brightness5 255
#ifdef ESP32
class TouchBrightnessControl : public Usermod {
private:
unsigned long lastTime = 0; //Interval
unsigned long lastTouch = 0; //Timestamp of last Touch
unsigned long lastRelease = 0; //Timestamp of last Touch release
boolean released = true; //current Touch state (touched/released)
uint16_t touchReading = 0; //sensor reading, maybe use uint8_t???
uint16_t touchDuration = 0; //duration of last touch
public:
void setup() {
lastTouch = millis();
lastRelease = millis();
lastTime = millis();
}
void loop() {
if (millis() - lastTime >= 50) { //Check every 50ms if a touch occurs
lastTime = millis();
touchReading = touchRead(touchPin); //Read touch sensor on pin T0 (GPIO4 / D4)
if(touchReading < threshold && released) { //Touch started
released = false;
lastTouch = millis();
}
else if(touchReading >= threshold && !released) { //Touch released
released = true;
lastRelease = millis();
touchDuration = lastRelease - lastTouch; //Calculate duration
}
//Serial.println(touchDuration);
if(touchDuration >= 800 && released) { //Toggle power if button press is longer than 800ms
touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch
toggleOnOff();
colorUpdated(2); //Refresh values
}
else if(touchDuration >= 100 && released) { //Switch to next brightness if touch is between 100 and 800ms
touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch
if(bri < brightness1) {
bri = brightness1;
} else if(bri >= brightness1 && bri < brightness2) {
bri = brightness2;
} else if(bri >= brightness2 && bri < brightness3) {
bri = brightness3;
} else if(bri >= brightness3 && bri < brightness4) {
bri = brightness4;
} else if(bri >= brightness4 && bri < brightness5) {
bri = brightness5;
} else if(bri >= brightness5) {
bri = brightness1;
}
colorUpdated(2); //Refresh values
}
}
}
};
#endif

View File

@@ -100,9 +100,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -126,8 +126,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -143,9 +143,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -169,8 +169,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -1,9 +0,0 @@
# PIR sensor with MQTT
This simple usermod allows attaching a PIR sensor like the AM312 and publish the readings over MQTT. A message is sent when motion is detected as well as when motion has stopped.
This usermod has only been tested with the AM312 sensor though should work for any other PIR sensor. Note that this does not control the LED strip directly, it only publishes MQTT readings for use with other integrations like Home Assistant.
## Installation
Copy and replace the file `usermod.cpp` in wled00 directory.

View File

@@ -1,55 +0,0 @@
#include "wled.h"
/*
* This v1 usermod file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
* If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
*
* Consider the v2 usermod API if you need a more advanced feature set!
*/
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
// PIR sensor pin
const int MOTION_PIN = 16;
// MQTT topic for sensor values
const char MQTT_TOPIC[] = "/motion";
int prevState = LOW;
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
pinMode(MOTION_PIN, INPUT);
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
void publishMqtt(String state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (mqtt != nullptr){
char subuf[38];
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, MQTT_TOPIC);
mqtt->publish(subuf, 0, true, state.c_str());
}
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
if (digitalRead(MOTION_PIN) == HIGH && prevState == LOW) { // Motion detected
publishMqtt("ON");
prevState = HIGH;
}
if (digitalRead(MOTION_PIN) == LOW && prevState == HIGH) { // Motion stopped
publishMqtt("OFF");
prevState = LOW;
}
}

View File

@@ -61,7 +61,7 @@ class PIRsensorSwitch : public Usermod {
private:
// PIR sensor pin
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
// notification mode for colorUpdated()
// notification mode for stateUpdated()
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;
@@ -127,7 +127,7 @@ class PIRsensorSwitch : public Usermod {
if (bri != briHighlight) {
bri = briHighlight; // set current highlight brightness to last set highlight brightness
}
colorUpdated(NotifyUpdateMode);
stateUpdated(NotifyUpdateMode);
highlightActive = true; // flag highlight is on
}
else { // **pir timer has elapsed**
@@ -157,7 +157,7 @@ class PIRsensorSwitch : public Usermod {
}
applyMacro(macroLongPress); // apply standby lighting without brightness
}
colorUpdated(NotifyUpdateMode);
stateUpdated(NotifyUpdateMode);
highlightActive = false; // flag highlight is off
}
}

View File

@@ -9,16 +9,17 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
## Webinterface
The info page in the web interface shows the remaining time of the off timer.
The info page in the web interface shows the remaining time of the off timer. Usermod can also be temporarily disbled/enabled from the info page by clicking PIR button.
## Sensor connection
My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
My setup uses an HC-SR501 or HC-SR602 sensor, a HC-SR505 should also work.
The usermod uses GPIO13 (D1 mini pin D7) by default for the sensor signal but can be changed in the Usermod settings page.
[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor.
Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above.
You can also use usermod's off timer instead of sensor's. In such case rotate the potentiometer to its shortest time possible (or use SR602 which lacks such potentiometer).
## Usermod installation
@@ -59,6 +60,8 @@ void registerUsermods()
}
```
**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionaly `-D PIR_SENSOR_PIN=16` to override default pin.
## API to enable/disable the PIR sensor from outside. For example from another usermod.
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
@@ -95,8 +98,27 @@ class MyUsermod : public Usermod {
};
```
Have fun - @gegu
### Configuration options
Usermod can be configured in Usermods settings page.
* `PIRenabled` - enable/disable usermod
* `pin` - dynamically change GPIO pin where PIR sensor is attached to ESP
* `PIRoffSec` - number of seconds after PIR sensor deactivates when usermod triggers Off preset (or turns WLED off)
* `on-preset` - preset triggered when PIR activates (if this is 0 it will just turn WLED on)
* `off-preset` - preset triggered when PIR deactivates (if this is 0 it will just turn WLED off)
* `nighttime-only` - enable triggering only between sunset and sunrise (you will need to set up _NTP_, _Lat_ & _Lon_ in Time & Macro settings)
* `mqtt-only` - only send MQTT messages, do not interact with WLED
* `off-only` - only trigger presets or turn WLED on/off in WLED is not already on (displaying effect)
* `notifications` - enable or disable sending notifications to other WLED instances using Sync button
Have fun - @gegu & @blazoncek
## Change log
2021-04
* Adaptation for runtime configuration.
* Adaptation for runtime configuration.
2021-11
* Added information about dynamic configuration options
* Added option to temporary enable/disble usermod from WLED UI (Info dialog)

View File

@@ -55,33 +55,29 @@ public:
bool PIRsensorEnabled() { return enabled; }
private:
// PIR sensor pin
int8_t PIRsensorPin = PIR_SENSOR_PIN;
// notification mode for colorUpdated()
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
uint32_t m_offTimerStart = 0;
// current PIR sensor pin state
byte sensorPinState = LOW;
// PIR sensor enabled
bool enabled = true;
// status of initialisation
bool initDone = false;
// on and off presets
uint8_t m_onPreset = 0;
uint8_t m_offPreset = 0;
// flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_nightTimeOnly = false;
// flag to send MQTT message only (assuming it is enabled)
bool m_mqttOnly = false;
// 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;
byte prevPreset = 0;
byte prevPlaylist = 0;
bool savedState = false;
uint32_t offTimerStart = 0; // off timer start time
byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE
byte sensorPinState = LOW; // current PIR sensor pin state
bool initDone = false; // status of initialization
bool PIRtriggered = false;
unsigned long lastLoop = 0;
// configurable parameters
bool enabled = true; // PIR sensor enabled
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
uint32_t m_switchOffDelay = 600000; // delay before switch off after the sensor state goes LOW (10min)
uint8_t m_onPreset = 0; // on preset
uint8_t m_offPreset = 0; // off preset
bool m_nightTimeOnly = false; // flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled)
// 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;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _switchOffDelay[];
@@ -91,30 +87,30 @@ private:
static const char _nightTime[];
static const char _mqttOnly[];
static const char _offOnly[];
static const char _notify[];
/**
* check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/
bool isDayTime() {
bool isDayTime = false;
updateLocalTime();
uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime);
if (sunrise && sunset) {
if (hour(sunrise)<hr && hour(sunset)>hr) {
isDayTime = true;
return true;
} else {
if (hour(sunrise)==hr && minute(sunrise)<mi) {
isDayTime = true;
return true;
}
if (hour(sunset)==hr && minute(sunset)>mi) {
isDayTime = true;
return true;
}
}
}
return isDayTime;
return false;
}
/**
@@ -124,17 +120,47 @@ private:
{
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
PIRtriggered = switchOn;
if (switchOn && m_onPreset) {
applyPreset(m_onPreset);
} else if (!switchOn && m_offPreset) {
applyPreset(m_offPreset);
} else if (switchOn && bri == 0) {
bri = briLast;
colorUpdated(NotifyUpdateMode);
} else if (!switchOn && bri != 0) {
briLast = bri;
bri = 0;
colorUpdated(NotifyUpdateMode);
if (switchOn) {
if (m_onPreset) {
if (currentPlaylist>0) prevPlaylist = currentPlaylist;
else if (currentPreset>0) prevPreset = currentPreset;
else {
saveTemporaryPreset();
savedState = true;
prevPlaylist = 0;
prevPreset = 0;
}
applyPreset(m_onPreset, NotifyUpdateMode);
return;
}
// preset not assigned
if (bri == 0) {
bri = briLast;
stateUpdated(NotifyUpdateMode);
}
} else {
if (m_offPreset) {
applyPreset(m_offPreset, NotifyUpdateMode);
return;
} else if (prevPlaylist) {
applyPreset(prevPlaylist, NotifyUpdateMode);
prevPlaylist = 0;
return;
} else if (prevPreset) {
applyPreset(prevPreset, NotifyUpdateMode);
prevPreset = 0;
return;
} else if (savedState) {
applyTemporaryPreset();
savedState = false;
return;
}
// preset not assigned
if (bri != 0) {
briLast = bri;
bri = 0;
stateUpdated(NotifyUpdateMode);
}
}
}
@@ -160,12 +186,12 @@ private:
sensorPinState = pinState; // change previous state
if (sensorPinState == HIGH) {
m_offTimerStart = 0;
offTimerStart = 0;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
publishMqtt("on");
} else /*if (bri != 0)*/ {
// start switch off timer
m_offTimerStart = millis();
offTimerStart = millis();
}
return true;
}
@@ -177,14 +203,14 @@ private:
*/
bool handleOffTimer()
{
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay)
{
if (enabled == true)
{
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
publishMqtt("off");
}
m_offTimerStart = 0;
offTimerStart = 0;
return true;
}
return false;
@@ -248,15 +274,25 @@ public:
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
if (enabled)
{
// off timer
String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
if (m_offTimerStart > 0)
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
if (enabled) {
uiDomString += F(":false}});\">");
uiDomString += F("PIR <i class=\"icons\">&#xe325;</i>");
} else {
uiDomString += F(":true}});\">");
uiDomString += F("PIR <i class=\"icons\">&#xe08f;</i>");
}
uiDomString += F("</button>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
if (enabled) {
if (offTimerStart > 0)
{
uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
if (offSeconds >= 3600)
{
uiDomString += (offSeconds / 3600);
@@ -282,8 +318,6 @@ public:
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
}
} else {
String uiDomString = F("PIR sensor");
JsonArray infoArr = user.createNestedArray(uiDomString);
infoArr.add(F("disabled"));
}
}
@@ -302,11 +336,18 @@ public:
* 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()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
if (usermod[FPSTR(_enabled)].is<bool>()) {
enabled = usermod[FPSTR(_enabled)].as<bool>();
}
}
}
*/
/**
* provide the changeable values
@@ -314,14 +355,15 @@ public:
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly;
top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly;
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
DEBUG_PRINTLN(F("PIR config saved."));
}
@@ -336,9 +378,9 @@ public:
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
@@ -351,7 +393,6 @@ public:
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset));
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset));
@@ -359,7 +400,8 @@ public:
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
DEBUG_PRINT(FPSTR(_name));
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
if (!initDone) {
// reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded."));
@@ -385,7 +427,7 @@ public:
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_offOnly)].isNull();
return !top[FPSTR(_notify)].isNull();
}
/**
@@ -407,3 +449,4 @@ 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";
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";

View File

@@ -3,6 +3,14 @@
#include "src/dependencies/time/DS1307RTC.h"
#include "wled.h"
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod {
@@ -12,6 +20,8 @@ class RTCUsermod : public Usermod {
public:
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
@@ -22,12 +32,26 @@ class RTCUsermod : public Usermod {
}
void loop() {
if (strip.isUpdating()) return;
if (!disabled && toki.isTick()) {
time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
}
}
/*
* 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)
* 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("RTC");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
uint16_t getId()
{
return USERMOD_ID_RTC;

View File

@@ -22,12 +22,12 @@
// 10 bits
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0f
#endif
// resistor size 10K hms
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
#endif
// only report if differance grater than offset value
@@ -123,6 +123,11 @@ public:
}
}
uint16_t getLastLDRValue()
{
return lastLDRValue;
}
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];

View File

@@ -12,7 +12,7 @@ This usermod allow to use 240x240 display to display following:
## Hardware
***
![Hardware](images/ST7789_guide.jpg)
![Hardware](images/ST7789_Guide.jpg)
## Library used

View File

@@ -118,11 +118,11 @@ class St7789DisplayUsermod : public Usermod {
{
needRedraw = true;
}
else if (knownMode != strip.getMode())
else if (knownMode != strip.getMainSegment().mode)
{
needRedraw = true;
}
else if (knownPalette != strip.getSegment(0).palette)
else if (knownPalette != strip.getMainSegment().palette)
{
needRedraw = true;
}
@@ -148,8 +148,8 @@ class St7789DisplayUsermod : public Usermod {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);

View File

@@ -110,9 +110,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -136,8 +136,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);

View File

@@ -37,12 +37,12 @@ class UsermodTemperature : public Usermod {
// used to determine when we can read the sensors temperature
// we have to wait at least 93.75 ms after requestTemperatures() is called
unsigned long lastTemperaturesRequest;
float temperature = -100; // default to -100, DS18B20 only goes down to -50C
float temperature;
// indicates requestTemperatures has been called but the sensor measurement is not complete
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;
byte sensorFound;
bool enabled = true;
@@ -54,27 +54,47 @@ class UsermodTemperature : public Usermod {
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas() {
byte i;
byte data[2];
byte data[9];
int16_t result; // raw data from sensor
if (!oneWire->reset()) return -127.0f; // send reset command and fail fast
oneWire->skip(); // skip ROM
oneWire->write(0xBE); // read (temperature) from EEPROM
for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature
for (i=2; i < 8; i++) oneWire->read(); // read unused bytes
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
if (data[1]&0x80) result |= 0xFF00; // fix negative value
oneWire->reset();
oneWire->skip(); // skip ROM
oneWire->write(0x44,parasite); // request new temperature reading (without parasite power)
return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f);
float retVal = -127.0f;
if (oneWire->reset()) { // if reset() fails there are no OneWire devices
oneWire->skip(); // skip ROM
oneWire->write(0xBE); // read (temperature) from EEPROM
oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
#ifdef WLED_DEBUG
if (OneWire::crc8(data,8) != data[8]) {
DEBUG_PRINTLN(F("CRC error reading temperature."));
for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]);
DEBUG_PRINT(F(" => "));
DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
}
#endif
switch(sensorFound) {
case 0x10: // DS18S20 has 9-bit precision
result = (data[1] << 8) | data[0];
retVal = float(result) * 0.5f;
break;
case 0x22: // DS18B20
case 0x28: // DS1822
case 0x3B: // DS1825
case 0x42: // DS28EA00
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
if (data[1] & 0x80) result |= 0xF000; // fix negative value
retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f);
break;
}
}
for (byte i=1; i<9; i++) data[0] &= data[i];
return data[0]==0xFF ? -127.0f : retVal;
}
void requestTemperatures() {
readDallas();
DEBUG_PRINTLN(F("Requesting temperature."));
oneWire->reset();
oneWire->skip(); // skip ROM
oneWire->write(0x44,parasite); // request new temperature reading (TODO: parasite would need special handling)
lastTemperaturesRequest = millis();
waitingForConversion = true;
DEBUG_PRINTLN(F("Requested temperature."));
}
void readTemperature() {
@@ -102,10 +122,13 @@ class UsermodTemperature : public Usermod {
case 0x3B: // DS1825
case 0x42: // DS28EA00
DEBUG_PRINTLN(F("Sensor found."));
sensorFound = deviceAddress[0];
DEBUG_PRINTF("0x%02X\n", sensorFound);
return true;
}
}
}
DEBUG_PRINTLN(F("Sensor NOT found."));
return false;
}
@@ -113,16 +136,16 @@ class UsermodTemperature : public Usermod {
void setup() {
int retries = 10;
sensorFound = 0;
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
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()) {
sensorFound = false; // resetting 1-Wire bus yielded an error
} else {
while ((sensorFound=findSensor()) && retries--) {
if (oneWire->reset()) {
while (!findSensor() && retries--) {
delay(25); // try to find sensor
}
}
@@ -131,7 +154,6 @@ class UsermodTemperature : public Usermod {
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
}
temperaturePin = -1; // allocation failed
sensorFound = false;
}
}
lastMeasurement = millis() - readingInterval + 10000;
@@ -139,8 +161,9 @@ class UsermodTemperature : public Usermod {
}
void loop() {
if (!enabled || strip.isUpdating()) return;
if (!enabled || !sensorFound || strip.isUpdating()) return;
static uint8_t errorCount = 0;
unsigned long now = millis();
// check to see if we are due for taking a measurement
@@ -156,20 +179,26 @@ class UsermodTemperature : public Usermod {
}
// we were waiting for a conversion to complete, have we waited log enough?
if (now - lastTemperaturesRequest >= 100 /* 93.75ms per the datasheet but can be up to 750ms */) {
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
readTemperature();
if (getTemperatureC() < -100.0f) {
if (++errorCount > 10) sensorFound = 0;
lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
return;
}
errorCount = 0;
if (WLED_MQTT_CONNECTED) {
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
if (-100 <= temperature) {
if (temperature > -100.0f) {
// dont publish super low temperature as the graph will get messed up
// the DallasTemperature library returns -127C or -196.6F when problem
// reading the sensor
strcat_P(subuf, PSTR("/temperature"));
mqtt->publish(subuf, 0, false, String(temperature).c_str());
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
strcat_P(subuf, PSTR("_f"));
mqtt->publish(subuf, 0, false, String((float)temperature * 1.8f + 32).c_str());
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
} else {
// publish something else to indicate status?
}
@@ -202,13 +231,13 @@ class UsermodTemperature : public Usermod {
JsonArray temp = user.createNestedArray(FPSTR(_name));
//temp.add(F("Loaded."));
if (temperature <= -100.0 || (!sensorFound && temperature == -1.0)) {
if (temperature <= -100.0f) {
temp.add(0);
temp.add(F(" Sensor Error!"));
return;
}
temp.add(degC ? temperature : (float)temperature * 1.8f + 32);
temp.add(degC ? getTemperatureC() : getTemperatureF());
if (degC) temp.add(F("°C"));
else temp.add(F("°F"));
}
@@ -252,23 +281,21 @@ class UsermodTemperature : public Usermod {
bool readFromConfig(JsonObject &root) {
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
int8_t newTemperaturePin = temperaturePin;
DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
newTemperaturePin = top["pin"] | newTemperaturePin;
// 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
parasite = top[FPSTR(_parasite)] | parasite;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
temperaturePin = newTemperaturePin;

View File

@@ -21,6 +21,14 @@
#include <Wire.h>
#include <VL53L0X.h>
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
#ifndef VL53L0X_MAX_RANGE_MM
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
#endif
@@ -42,6 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
VL53L0X sensor;
bool enabled = true;
bool wasMotionBefore = false;
bool isLongMotion = false;
@@ -50,6 +59,8 @@ class UsermodVL53L0XGestures : public Usermod {
public:
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
Wire.begin();
sensor.setTimeout(150);
@@ -63,6 +74,7 @@ class UsermodVL53L0XGestures : public Usermod {
void loop() {
if (!enabled || strip.isUpdating()) return;
if (millis() - lastTime > VL53L0X_DELAY_MS)
{
lastTime = millis();
@@ -94,7 +106,7 @@ class UsermodVL53L0XGestures : public Usermod {
// set brightness according to range
bri = (VL53L0X_MAX_RANGE_MM - max(range, VL53L0X_MIN_RANGE_OFFSET)) * 255 / (VL53L0X_MAX_RANGE_MM - VL53L0X_MIN_RANGE_OFFSET);
DEBUG_PRINTF(F("new brightness: %d"), bri);
colorUpdated(1);
stateUpdated(1);
}
} else if (wasMotionBefore) { //released
long dur = millis() - motionStartTime;
@@ -110,6 +122,19 @@ class UsermodVL53L0XGestures : public Usermod {
}
}
/*
* 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)
* 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("VL53L0x");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
/*
* 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.

View File

@@ -137,9 +137,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -163,8 +163,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -143,9 +143,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -169,8 +169,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -54,35 +54,27 @@ void userLoop()
switch (myKey) {
case '1':
applyPreset(1);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '2':
applyPreset(2);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '3':
applyPreset(3);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '4':
applyPreset(4);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '5':
applyPreset(5);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '6':
applyPreset(6);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case 'A':
applyPreset(7);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case 'B':
applyPreset(8);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '7':

View File

@@ -21,10 +21,10 @@
#ifndef USERMOD_BATTERY_ADC_PRECISION
#ifdef ARDUINO_ARCH_ESP32
// 12 bits
#define USERMOD_BATTERY_ADC_PRECISION 4095.0
#define USERMOD_BATTERY_ADC_PRECISION 4095.0f
#else
// 10 bits
#define USERMOD_BATTERY_ADC_PRECISION 1024.0
#define USERMOD_BATTERY_ADC_PRECISION 1024.0f
#endif
#endif
@@ -39,11 +39,11 @@
// 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
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
#endif
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
#endif
class UsermodBatteryBasic : public Usermod

View File

@@ -1,515 +0,0 @@
//this code is a modified version of https://github.com/Makuna/NeoPixelBus/issues/103
#ifndef NpbWrapper_h
#define NpbWrapper_h
// make sure we're using esp32 platform
#ifndef ARDUINO_ARCH_ESP32
#error This version of NbpWrapper.h only works with ESP32 hardware.
#endif
#ifndef NUM_STRIPS
#error Need to define number of LED strips using build flag -D NUM_STRIPS=4 for 4 LED strips
#endif
#ifndef PIXEL_COUNTS
#error Need to define pixel counts using build flag -D PIXEL_COUNTS="25, 25, 25, 25" for 4 LED strips with 25 LEDs each
#endif
#ifndef DATA_PINS
#error Need to define data pins using build flag -D DATA_PINS="1, 2, 3, 4" if LED strips are on data pins 1, 2, 3, and 4
#endif
// //PIN CONFIGURATION
#ifndef LEDPIN
#define LEDPIN 1 // Legacy pin def required by some other portions of code. This pin is not used do drive LEDs.
#endif
#ifndef IRPIN
#define IRPIN -1 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
#endif
#ifndef RLYPIN
#define RLYPIN -1 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,...
#endif
#ifndef AUXPIN
#define AUXPIN -1 //debug auxiliary output pin (-1 to disable)
#endif
#ifndef RLYMDE
#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on
#endif
#include <NeoPixelBrightnessBus.h>
#include "const.h"
const uint8_t numStrips = NUM_STRIPS; // max 8 strips allowed on esp32
const uint16_t pixelCounts[numStrips] = {PIXEL_COUNTS}; // number of pixels on each strip
const uint8_t dataPins[numStrips] = {DATA_PINS}; // change these pins based on your board
#define PIXELFEATURE3 NeoGrbFeature
#define PIXELFEATURE4 NeoGrbwFeature
// ESP32 has 8 RMT interfaces available, each of which can drive a strip of pixels
// Convenience #defines for creating NeoPixelBrightnessBus on each RMT interface for both GRB and GRBW LED strips
#define NeoPixelBrightnessBusGrbRmt0 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt0Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt1 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt1Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt2 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt2Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt3 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt3Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt4 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt4Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt5 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt5Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt6 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt6Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt7 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt7Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt0 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt0Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt1 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt1Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt2 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt2Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt3 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt3Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt4 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt4Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt5 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt5Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt6 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt6Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt7 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt7Ws2812xMethod>
enum NeoPixelType
{
NeoPixelType_None = 0,
NeoPixelType_Grb = 1,
NeoPixelType_Grbw = 2,
NeoPixelType_End = 3
};
class NeoPixelWrapper
{
public:
NeoPixelWrapper() :
_type(NeoPixelType_None)
{
// On initialization fill in the pixelStripStartIdx array with the beginning index of each strip
// relative to th entire array.
uint16_t totalPixels = 0;
for (uint8_t idx = 0; idx < numStrips; idx++)
{
pixelStripStartIdx[idx] = totalPixels;
totalPixels += pixelCounts[idx];
}
}
~NeoPixelWrapper()
{
cleanup();
}
void Begin(NeoPixelType type, uint16_t pixelCount)
{
cleanup();
_type = type;
switch (_type)
{
case NeoPixelType_Grb:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: pGrb0 = new NeoPixelBrightnessBusGrbRmt0(pixelCounts[idx], dataPins[idx]); pGrb0->Begin(); break;
case 1: pGrb1 = new NeoPixelBrightnessBusGrbRmt1(pixelCounts[idx], dataPins[idx]); pGrb1->Begin(); break;
case 2: pGrb2 = new NeoPixelBrightnessBusGrbRmt2(pixelCounts[idx], dataPins[idx]); pGrb2->Begin(); break;
case 3: pGrb3 = new NeoPixelBrightnessBusGrbRmt3(pixelCounts[idx], dataPins[idx]); pGrb3->Begin(); break;
case 4: pGrb4 = new NeoPixelBrightnessBusGrbRmt4(pixelCounts[idx], dataPins[idx]); pGrb4->Begin(); break;
case 5: pGrb5 = new NeoPixelBrightnessBusGrbRmt5(pixelCounts[idx], dataPins[idx]); pGrb5->Begin(); break;
case 6: pGrb6 = new NeoPixelBrightnessBusGrbRmt6(pixelCounts[idx], dataPins[idx]); pGrb6->Begin(); break;
case 7: pGrb7 = new NeoPixelBrightnessBusGrbRmt7(pixelCounts[idx], dataPins[idx]); pGrb7->Begin(); break;
}
}
break;
}
case NeoPixelType_Grbw:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: pGrbw0 = new NeoPixelBrightnessBusGrbwRmt0(pixelCounts[idx], dataPins[idx]); pGrbw0->Begin(); break;
case 1: pGrbw1 = new NeoPixelBrightnessBusGrbwRmt1(pixelCounts[idx], dataPins[idx]); pGrbw1->Begin(); break;
case 2: pGrbw2 = new NeoPixelBrightnessBusGrbwRmt2(pixelCounts[idx], dataPins[idx]); pGrbw2->Begin(); break;
case 3: pGrbw3 = new NeoPixelBrightnessBusGrbwRmt3(pixelCounts[idx], dataPins[idx]); pGrbw3->Begin(); break;
case 4: pGrbw4 = new NeoPixelBrightnessBusGrbwRmt4(pixelCounts[idx], dataPins[idx]); pGrbw4->Begin(); break;
case 5: pGrbw5 = new NeoPixelBrightnessBusGrbwRmt5(pixelCounts[idx], dataPins[idx]); pGrbw5->Begin(); break;
case 6: pGrbw6 = new NeoPixelBrightnessBusGrbwRmt6(pixelCounts[idx], dataPins[idx]); pGrbw6->Begin(); break;
case 7: pGrbw7 = new NeoPixelBrightnessBusGrbwRmt7(pixelCounts[idx], dataPins[idx]); pGrbw7->Begin(); break;
}
}
break;
}
}
}
void Show()
{
switch (_type)
{
case NeoPixelType_Grb:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: pGrb0->Show(); break;
case 1: pGrb1->Show(); break;
case 2: pGrb2->Show(); break;
case 3: pGrb3->Show(); break;
case 4: pGrb4->Show(); break;
case 5: pGrb5->Show(); break;
case 6: pGrb6->Show(); break;
case 7: pGrb7->Show(); break;
}
}
break;
}
case NeoPixelType_Grbw:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: pGrbw0->Show(); break;
case 1: pGrbw1->Show(); break;
case 2: pGrbw2->Show(); break;
case 3: pGrbw3->Show(); break;
case 4: pGrbw4->Show(); break;
case 5: pGrbw5->Show(); break;
case 6: pGrbw6->Show(); break;
case 7: pGrbw7->Show(); break;
}
}
break;
}
}
}
bool CanShow()
{
bool canShow = true;
switch (_type)
{
case NeoPixelType_Grb:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: canShow &= pGrb0->CanShow(); break;
case 1: canShow &= pGrb1->CanShow(); break;
case 2: canShow &= pGrb2->CanShow(); break;
case 3: canShow &= pGrb3->CanShow(); break;
case 4: canShow &= pGrb4->CanShow(); break;
case 5: canShow &= pGrb5->CanShow(); break;
case 6: canShow &= pGrb6->CanShow(); break;
case 7: canShow &= pGrb7->CanShow(); break;
}
}
break;
}
case NeoPixelType_Grbw:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: canShow &= pGrbw0->CanShow(); break;
case 1: canShow &= pGrbw1->CanShow(); break;
case 2: canShow &= pGrbw2->CanShow(); break;
case 3: canShow &= pGrbw3->CanShow(); break;
case 4: canShow &= pGrbw4->CanShow(); break;
case 5: canShow &= pGrbw5->CanShow(); break;
case 6: canShow &= pGrbw6->CanShow(); break;
case 7: canShow &= pGrbw7->CanShow(); break;
}
}
break;
}
}
return canShow;
}
void SetPixelColorRaw(uint16_t indexPixel, RgbwColor c)
{
// figure out which strip this pixel index is on
uint8_t stripIdx = 0;
for (uint8_t idx = 0; idx < numStrips; idx++)
{
if (indexPixel >= pixelStripStartIdx[idx])
{
stripIdx = idx;
}
else
{
break;
}
}
// subtract strip start index so we're addressing just this strip instead of all pixels on all strips
indexPixel -= pixelStripStartIdx[stripIdx];
switch (_type)
{
case NeoPixelType_Grb:
{
RgbColor rgb = RgbColor(c.R, c.G, c.B);
switch (stripIdx)
{
case 0: pGrb0->SetPixelColor(indexPixel, rgb); break;
case 1: pGrb1->SetPixelColor(indexPixel, rgb); break;
case 2: pGrb2->SetPixelColor(indexPixel, rgb); break;
case 3: pGrb3->SetPixelColor(indexPixel, rgb); break;
case 4: pGrb4->SetPixelColor(indexPixel, rgb); break;
case 5: pGrb5->SetPixelColor(indexPixel, rgb); break;
case 6: pGrb6->SetPixelColor(indexPixel, rgb); break;
case 7: pGrb7->SetPixelColor(indexPixel, rgb); break;
}
break;
}
case NeoPixelType_Grbw:
{
switch (stripIdx)
{
case 0: pGrbw0->SetPixelColor(indexPixel, c); break;
case 1: pGrbw1->SetPixelColor(indexPixel, c); break;
case 2: pGrbw2->SetPixelColor(indexPixel, c); break;
case 3: pGrbw3->SetPixelColor(indexPixel, c); break;
case 4: pGrbw4->SetPixelColor(indexPixel, c); break;
case 5: pGrbw5->SetPixelColor(indexPixel, c); break;
case 6: pGrbw6->SetPixelColor(indexPixel, c); break;
case 7: pGrbw7->SetPixelColor(indexPixel, c); break;
}
break;
}
}
}
void SetPixelColor(uint16_t indexPixel, RgbwColor c)
{
/*
Set pixel color with necessary color order conversion.
*/
RgbwColor col;
uint8_t co = _colorOrder;
#ifdef COLOR_ORDER_OVERRIDE
if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER;
#endif
//reorder channels to selected order
switch (co)
{
case 0: col.G = c.G; col.R = c.R; col.B = c.B; break; //0 = GRB, default
case 1: col.G = c.R; col.R = c.G; col.B = c.B; break; //1 = RGB, common for WS2811
case 2: col.G = c.B; col.R = c.R; col.B = c.G; break; //2 = BRG
case 3: col.G = c.R; col.R = c.B; col.B = c.G; break; //3 = RBG
case 4: col.G = c.B; col.R = c.G; col.B = c.R; break; //4 = BGR
default: col.G = c.G; col.R = c.B; col.B = c.R; break; //5 = GBR
}
col.W = c.W;
SetPixelColorRaw(indexPixel, col);
}
void SetBrightness(byte b)
{
switch (_type)
{
case NeoPixelType_Grb:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: pGrb0->SetBrightness(b); break;
case 1: pGrb1->SetBrightness(b); break;
case 2: pGrb2->SetBrightness(b); break;
case 3: pGrb3->SetBrightness(b); break;
case 4: pGrb4->SetBrightness(b); break;
case 5: pGrb5->SetBrightness(b); break;
case 6: pGrb6->SetBrightness(b); break;
case 7: pGrb7->SetBrightness(b); break;
}
}
break;
}
case NeoPixelType_Grbw:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: pGrbw0->SetBrightness(b); break;
case 1: pGrbw1->SetBrightness(b); break;
case 2: pGrbw2->SetBrightness(b); break;
case 3: pGrbw3->SetBrightness(b); break;
case 4: pGrbw4->SetBrightness(b); break;
case 5: pGrbw5->SetBrightness(b); break;
case 6: pGrbw6->SetBrightness(b); break;
case 7: pGrbw7->SetBrightness(b); break;
}
}
break;
}
}
}
void SetColorOrder(byte colorOrder)
{
_colorOrder = colorOrder;
}
uint8_t GetColorOrder()
{
return _colorOrder;
}
RgbwColor GetPixelColorRaw(uint16_t indexPixel) const
{
// figure out which strip this pixel index is on
uint8_t stripIdx = 0;
for (uint8_t idx = 0; idx < numStrips; idx++)
{
if (indexPixel >= pixelStripStartIdx[idx])
{
stripIdx = idx;
}
else
{
break;
}
}
// subtract strip start index so we're addressing just this strip instead of all pixels on all strips
indexPixel -= pixelStripStartIdx[stripIdx];
switch (_type)
{
case NeoPixelType_Grb:
{
switch (stripIdx)
{
case 0: return pGrb0->GetPixelColor(indexPixel);
case 1: return pGrb1->GetPixelColor(indexPixel);
case 2: return pGrb2->GetPixelColor(indexPixel);
case 3: return pGrb3->GetPixelColor(indexPixel);
case 4: return pGrb4->GetPixelColor(indexPixel);
case 5: return pGrb5->GetPixelColor(indexPixel);
case 6: return pGrb6->GetPixelColor(indexPixel);
case 7: return pGrb7->GetPixelColor(indexPixel);
}
break;
}
case NeoPixelType_Grbw:
switch (stripIdx)
{
case 0: return pGrbw0->GetPixelColor(indexPixel);
case 1: return pGrbw1->GetPixelColor(indexPixel);
case 2: return pGrbw2->GetPixelColor(indexPixel);
case 3: return pGrbw3->GetPixelColor(indexPixel);
case 4: return pGrbw4->GetPixelColor(indexPixel);
case 5: return pGrbw5->GetPixelColor(indexPixel);
case 6: return pGrbw6->GetPixelColor(indexPixel);
case 7: return pGrbw7->GetPixelColor(indexPixel);
}
break;
}
return 0;
}
// NOTE: Due to feature differences, some support RGBW but the method name
// here needs to be unique, thus GetPixeColorRgbw
uint32_t GetPixelColorRgbw(uint16_t indexPixel) const
{
RgbwColor col = GetPixelColorRaw(indexPixel);
uint8_t co = _colorOrder;
#ifdef COLOR_ORDER_OVERRIDE
if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER;
#endif
switch (co)
{
// W G R B
case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default
case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811
case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG
case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG
case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR
case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR
}
return 0;
}
private:
NeoPixelType _type;
byte _colorOrder = 0;
uint16_t pixelStripStartIdx[numStrips];
// pointers for every possible type for up to 8 strips
NeoPixelBrightnessBusGrbRmt0 *pGrb0;
NeoPixelBrightnessBusGrbRmt1 *pGrb1;
NeoPixelBrightnessBusGrbRmt2 *pGrb2;
NeoPixelBrightnessBusGrbRmt3 *pGrb3;
NeoPixelBrightnessBusGrbRmt4 *pGrb4;
NeoPixelBrightnessBusGrbRmt5 *pGrb5;
NeoPixelBrightnessBusGrbRmt6 *pGrb6;
NeoPixelBrightnessBusGrbRmt7 *pGrb7;
NeoPixelBrightnessBusGrbwRmt0 *pGrbw0;
NeoPixelBrightnessBusGrbwRmt1 *pGrbw1;
NeoPixelBrightnessBusGrbwRmt2 *pGrbw2;
NeoPixelBrightnessBusGrbwRmt3 *pGrbw3;
NeoPixelBrightnessBusGrbwRmt4 *pGrbw4;
NeoPixelBrightnessBusGrbwRmt5 *pGrbw5;
NeoPixelBrightnessBusGrbwRmt6 *pGrbw6;
NeoPixelBrightnessBusGrbwRmt7 *pGrbw7;
void cleanup()
{
switch (_type)
{
case NeoPixelType_Grb:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: delete pGrb0; pGrb0 = NULL; break;
case 1: delete pGrb1; pGrb1 = NULL; break;
case 2: delete pGrb2; pGrb2 = NULL; break;
case 3: delete pGrb3; pGrb3 = NULL; break;
case 4: delete pGrb4; pGrb4 = NULL; break;
case 5: delete pGrb5; pGrb5 = NULL; break;
case 6: delete pGrb6; pGrb6 = NULL; break;
case 7: delete pGrb7; pGrb7 = NULL; break;
}
}
break;
}
case NeoPixelType_Grbw:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: delete pGrbw0; pGrbw0 = NULL; break;
case 1: delete pGrbw1; pGrbw1 = NULL; break;
case 2: delete pGrbw2; pGrbw2 = NULL; break;
case 3: delete pGrbw3; pGrbw3 = NULL; break;
case 4: delete pGrbw4; pGrbw4 = NULL; break;
case 5: delete pGrbw5; pGrbw5 = NULL; break;
case 6: delete pGrbw6; pGrbw6 = NULL; break;
case 7: delete pGrbw7; pGrbw7 = NULL; break;
}
}
}
}
}
};
#endif

View File

@@ -1,22 +0,0 @@
# esp32_multistrip
This usermod enables up to 8 data pins to be used from an esp32 module to drive separate LED strands. This only works with one-wire LEDs like the WS2812.
The esp32 RMT hardware is used for data output. See here for hardware driver implementation details: https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
Pass the following variables to the compiler as build flags:
- `ESP32_MULTISTRIP`
- Define this to use usermod NpbWrapper.h instead of default one in WLED.
- `NUM_STRIPS`
- Number of strips in use
- `PIXEL_COUNTS`
- List of pixel counts in each strip
- `DATA_PINS`
- List of data pins each strip is attached to. There may be board-specific restrictions on which pins can be used for RTM.
From the perspective of WLED software, the LEDs are addressed as one long strand. The modified NbpWrapper.h file addresses the appropriate strand from the overall LED index based on the number of LEDs defined in each strand.
See `platformio_override.ini` for example configuration.
Tested on low cost ESP-WROOM-32 dev boards from Amazon, such as those sold by KeeYees.

View File

@@ -1,16 +0,0 @@
; Example platformio_override.ini that shows how to configure your environment to use the multistrip usermod.
; Copy this file to the base wled directory that contains platformio.ini.
; Multistrip requires ESP32 because it has many more pins that can be used as LED outputs.
; Need to define NUM_STRIPS, PIXEL_COUNTS, and DATA_PINS as shown below.
[platformio]
default_envs = esp32_multistrip
[env:esp32_multistrip]
extends=env:esp32dev
build_flags = ${env:esp32dev.build_flags}
-D ESP32_MULTISTRIP ; define this variable to use ESP32_MULTISTRIP usermod
-D NUM_STRIPS=4 ; number of pixel strips in use
-D PIXEL_COUNTS="50, 50, 50, 50" ; number of pixels in each strip
-D DATA_PINS="25, 26, 32, 33" ; esp32 pins used for each pixel strip. available pins depends on esp32 module.

View File

@@ -42,6 +42,14 @@
#include "Wire.h"
#endif
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
@@ -55,7 +63,8 @@ void IRAM_ATTR dmpDataReady() {
class MPU6050Driver : public Usermod {
private:
MPU6050 mpu;
bool enabled = true;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
@@ -84,6 +93,8 @@ class MPU6050Driver : public Usermod {
* setup() is called once at boot. WiFi is not yet connected at this point.
*/
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
@@ -93,16 +104,16 @@ class MPU6050Driver : public Usermod {
#endif
// initialize device
Serial.println(F("Initializing I2C devices..."));
DEBUG_PRINTLN(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
DEBUG_PRINTLN(F("Testing device connections..."));
DEBUG_PRINTLN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
DEBUG_PRINTLN(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
@@ -114,16 +125,16 @@ class MPU6050Driver : public Usermod {
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
DEBUG_PRINTLN(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
DEBUG_PRINTLN(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
@@ -133,9 +144,9 @@ class MPU6050Driver : public Usermod {
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
DEBUG_PRINT(F("DMP Initialization failed (code "));
DEBUG_PRINT(devStatus);
DEBUG_PRINTLN(F(")"));
}
}
@@ -144,7 +155,7 @@ class MPU6050Driver : public Usermod {
* Use it to initialize network interfaces
*/
void connected() {
//Serial.println("Connected to WiFi!");
//DEBUG_PRINTLN("Connected to WiFi!");
}
@@ -153,7 +164,7 @@ class MPU6050Driver : public Usermod {
*/
void loop() {
// if programming failed, don't try to do anything
if (!dmpReady) return;
if (!enabled || !dmpReady || strip.isUpdating()) return;
// wait for MPU interrupt or extra packet(s) available
if (!mpuInterrupt && fifoCount < packetSize) return;
@@ -169,7 +180,7 @@ class MPU6050Driver : public Usermod {
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
DEBUG_PRINTLN(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
@@ -259,10 +270,23 @@ class MPU6050Driver : public Usermod {
*/
void readFromJsonState(JsonObject& root)
{
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
//if (root["bri"] == 255) DEBUG_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)
* 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("MPU6050_IMU");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
*/

View File

@@ -1,3 +1,7 @@
# DEPRECATION NOTICE
This usermod is deprecated and no longer maintained. It will be removed in a future WLED release. Please use usermod multi_relay which has more features.
# MQTT controllable switches
This usermod allows controlling switches (e.g. relays) via MQTT.

View File

@@ -1,5 +1,7 @@
#pragma once
#warning "This usermod is deprecated and no longer maintained. It will be removed in a future WLED release. Please use usermod multi_relay which has more features."
#include "wled.h"
#ifndef WLED_ENABLE_MQTT
#error "This user mod requires MQTT to be enabled."

View File

@@ -5,38 +5,42 @@ This usermod-v2 modification allows the connection of multiple relays each with
## HTTP API
All responses are returned as JSON.
Status Request: `http://[device-ip]/relays`
Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
* Status Request: `http://[device-ip]/relays`
* Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
The number of numbers behind the switch parameter must correspond to the number of relays. The number 1 switches the relay on. The number 0 switches the relay off.
Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
* Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
The number of numbers behind the parameter switch must correspond to the number of relays. The number 1 causes a toggling of the relay. The number 0 leaves the state of the device.
Examples
1. 4 relays at all, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
2. 3 relays at all, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
1. total of 4 relays, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
2. total of 3 relays, 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
wled/deviceMAC/relay/1/command on|off|toggle
* `wled`/_deviceMAC_/`relay`/`0`/`command` `on`|`off`|`toggle`
* `wled`/_deviceMAC_/`relay`/`1`/`command` `on`|`off`|`toggle`
When relay is switched it will publish a message:
wled/deviceMAC/relay/0 on|off
* `wled`/_deviceMAC_/`relay`/`0` `on`|`off`
## Usermod installation
1. Register the usermod by adding `#include "../usermods/multi_relay/usermod_multi_relay.h"` at the top and `usermods.add(new MultiRelay());` at the bottom of `usermods_list.cpp`.
or
2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY`in your platformio.ini
2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY` in your platformio.ini
You can override the default maximum number (4) of relays by defining MULTI_RELAY_MAX_RELAYS.
@@ -76,10 +80,21 @@ void registerUsermods()
Usermod can be configured in Usermods settings page.
* `enabled` - enable/disable usermod
* `pin` - GPIO pin where relay is attached to ESP
* `delay-s` - delay in seconds after on/off command is received
* `active-high` - toggle high/low activation of relay (can be used to reverse relay states)
* `external` - if enabled WLED does not control relay, it can only be triggered by external command (MQTT, HTTP, JSON or button)
* `button` - button (from LED Settings) that controls this relay
If there is no MultiRelay section, just save current configuration and re-open Usermods settings page.
Have fun - @blazoncek
## Change log
2021-04
* First implementation.
* First implementation.
2021-11
* Added information about dynamic configuration options
* Added button support.

View File

@@ -45,6 +45,11 @@ class MultiRelay : public Usermod {
// status of initialisation
bool initDone = false;
bool HAautodiscovery = false;
uint16_t periodicBroadcastSec = 60;
unsigned long lastBroadcast = 0;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
@@ -53,14 +58,15 @@ class MultiRelay : public Usermod {
static const char _activeHigh[];
static const char _external[];
static const char _button[];
static const char _broadcast[];
static const char _HAautodiscovery[];
void publishMqtt(const char* state, int relay) {
void publishMqtt(int relay) {
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay);
mqtt->publish(subuf, 0, false, state);
mqtt->publish(subuf, 0, false, _relay[relay].state ? "on" : "off");
}
}
@@ -68,15 +74,19 @@ class MultiRelay : public Usermod {
* switch off the strip if the delay has elapsed
*/
void handleOffTimer() {
unsigned long now = millis();
bool activeRelays = false;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].active && _switchTimerStart > 0 && millis() - _switchTimerStart > (_relay[i].delay*1000)) {
if (_relay[i].active && _switchTimerStart > 0 && now - _switchTimerStart > (_relay[i].delay*1000)) {
if (!_relay[i].external) toggleRelay(i);
_relay[i].active = false;
} else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) {
if (_relay[i].pin>=0) publishMqtt(i);
}
activeRelays = activeRelays || _relay[i].active;
}
if (!activeRelays) _switchTimerStart = 0;
if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) lastBroadcast = now;
}
/**
@@ -105,7 +115,7 @@ class MultiRelay : public Usermod {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
int value = getValue(p->value(), ',', i);
if (value==-1) {
error = F("There must be as much arugments as relays");
error = F("There must be as many arguments as relays");
} else {
// Switch
if (_relay[i].external) switchRelay(i, (bool)value);
@@ -118,7 +128,7 @@ class MultiRelay : public Usermod {
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
int value = getValue(p->value(), ',', i);
if (value==-1) {
error = F("There must be as mutch arugments as relays");
error = F("There must be as many arguments as relays");
} else {
// Toggle
if (value && _relay[i].external) toggleRelay(i);
@@ -199,7 +209,7 @@ class MultiRelay : public Usermod {
_relay[relay].state = mode;
pinMode(_relay[relay].pin, OUTPUT);
digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode);
publishMqtt(mode ? "on" : "off", relay);
publishMqtt(relay);
}
/**
@@ -252,6 +262,50 @@ class MultiRelay : public Usermod {
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/relay/#"));
mqtt->subscribe(subuf, 0);
if (HAautodiscovery) publishHomeAssistantAutodiscovery();
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue;
publishMqtt(i); //publish current state
}
}
}
void publishHomeAssistantAutodiscovery() {
for (uint8_t i = 0; i < MULTI_RELAY_MAX_RELAYS; i++) {
char uid[24], json_str[1024], buf[128];
size_t payload_size;
sprintf_P(uid, PSTR("%s_sw%d"), escapedMac.c_str(), i);
if (_relay[i].pin >= 0 && _relay[i].external) {
StaticJsonDocument<1024> json;
sprintf_P(buf, PSTR("%s Switch %d"), serverDescription, i); //max length: 33 + 8 + 3 = 44
json[F("name")] = buf;
sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43
json["~"] = buf;
strcat_P(buf, PSTR("/command"));
mqtt->subscribe(buf, 0);
json[F("stat_t")] = "~";
json[F("cmd_t")] = F("~/command");
json[F("pl_off")] = F("off");
json[F("pl_on")] = F("on");
json[F("uniq_id")] = uid;
strcpy(buf, mqttDeviceTopic); //max length: 33 + 7 = 40
strcat_P(buf, PSTR("/status"));
json[F("avty_t")] = buf;
json[F("pl_avail")] = F("online");
json[F("pl_not_avail")] = F("offline");
//TODO: dev
payload_size = serializeJson(json, json_str);
} else {
//Unpublish disabled or internal relays
json_str[0] = 0;
payload_size = 0;
}
sprintf_P(buf, PSTR("homeassistant/switch/%s/config"), uid);
mqtt->publish(buf, 0, true, json_str, payload_size);
}
}
@@ -266,7 +320,7 @@ class MultiRelay : public Usermod {
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
_relay[i].pin = -1; // allocation failed
} else {
if (!_relay[i].external) _relay[i].state = offMode;
if (!_relay[i].external) _relay[i].state = !offMode;
switchRelay(i, _relay[i].state);
_relay[i].active = false;
}
@@ -313,13 +367,18 @@ class MultiRelay : public Usermod {
*/
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) {
if (!enabled
|| buttonType[b] == BTN_TYPE_NONE
|| buttonType[b] == BTN_TYPE_RESERVED
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|| buttonType[b] == BTN_TYPE_ANALOG
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
return false;
}
bool handled = false;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].button == b) {
if (_relay[i].button == b && _relay[i].external) {
handled = true;
}
}
@@ -355,6 +414,8 @@ class MultiRelay : public Usermod {
buttonPressedBefore[b] = true;
if (now - buttonPressedTime[b] > 600) { //long press
//longPressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
buttonLongPressed[b] = true;
}
@@ -371,7 +432,8 @@ class MultiRelay : public Usermod {
if (!buttonLongPressed[b]) { //short press
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
if (doublePress) {
//doublePressAction(b);
//doublePressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
} else {
buttonWaitTime[b] = now;
}
@@ -379,9 +441,10 @@ class MultiRelay : public Usermod {
buttonPressedBefore[b] = false;
buttonLongPressed[b] = false;
}
// if 450ms elapsed since last press/release it is a short press
// if 350ms elapsed since last press/release it is a short press
if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
buttonWaitTime[b] = 0;
//shortPressAction(b); //not exposed
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && _relay[i].button == b) {
toggleRelay(i);
@@ -477,6 +540,7 @@ class MultiRelay : public Usermod {
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_broadcast)] = periodicBroadcastSec;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
JsonObject relay = top.createNestedObject(parName);
@@ -486,6 +550,7 @@ class MultiRelay : public Usermod {
relay[FPSTR(_external)] = _relay[i].external;
relay[FPSTR(_button)] = _relay[i].button;
}
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
DEBUG_PRINTLN(F("MultiRelay config saved."));
}
@@ -506,6 +571,9 @@ class MultiRelay : public Usermod {
}
enabled = top[FPSTR(_enabled)] | enabled;
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
@@ -539,7 +607,9 @@ class MultiRelay : public Usermod {
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
if (!_relay[i].external) {
switchRelay(i, offMode);
_relay[i].state = !offMode;
switchRelay(i, _relay[i].state);
_oldMode = offMode;
}
} else {
_relay[i].pin = -1;
@@ -549,7 +619,7 @@ class MultiRelay : public Usermod {
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[F("relay-0")][FPSTR(_button)].isNull();
return !top[FPSTR(_broadcast)].isNull();
}
/**
@@ -570,3 +640,5 @@ 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";
const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";

View File

@@ -61,10 +61,10 @@ class QuinLEDAnPentaUsermod : public Usermod
float shtLastKnownHumidity = 0;
// Pin/IO vars
const int8_t anPentaPins[5] = {14, 13, 12, 4, 2};
const int8_t anPentaLEDPins[5] = {14, 13, 12, 4, 2};
int8_t oledSpiClk = 15;
int8_t oledSpiData = 16;
int8_t oledSpiCs = 0;
int8_t oledSpiCs = 27;
int8_t oledSpiDc = 32;
int8_t oledSpiRst = 33;
int8_t shtSda = 1;
@@ -75,7 +75,7 @@ class QuinLEDAnPentaUsermod : public Usermod
{
for(int8_t i = 0; i <= 4; i++)
{
if(anPentaPins[i] == pin)
if(anPentaLEDPins[i] == pin)
return true;
}
return false;
@@ -313,7 +313,7 @@ class QuinLEDAnPentaUsermod : public Usermod
byte drawnLines = 0;
for (int8_t app = 0; app <= 4; app++) {
for (int8_t clp = 0; clp <= 4; clp++) {
if (anPentaPins[app] == currentLedPins[clp]) {
if (anPentaLEDPins[app] == currentLedPins[clp]) {
char charCurrentLedcReads[17];
sprintf(charCurrentLedcReads, "LED %d:", app+1);
if (oledUseProgressBars) {

View File

@@ -1,12 +1,12 @@
# 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.
The (un)official usermod to get the best out of the QuinLED-An-Penta (https://quinled.info/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`.
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, add the buildflag `-D QUINLED_AN_PENTA` and the below library dependencies.
ESP32 (**without** ethernet):
```
@@ -33,14 +33,19 @@ This mod has been optimized for an SSD1306 driven 128x64 OLED. Using a smaller O
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**!
### Limitations combined with Ethernet
The initial development of this mod had been done with a beta version of the QuinLED-An-Penta, which had a different IO layout for the OLED: The CS pin used to be IO_0, but has been changed to IO27 with the first v1 public release. Unfortunately, IO27 is used by the Ethernet boards, so WLED will not let you enable the OLED screen, if you're using it with Ethernet. This unfortunately makes the development I've done to support/show Ethernet information void, as it cannot be used.
However (and I've not tried this, as I don't own a v1 board): You can try to modify this mod and try to use IO27 for the OLED and share it with the Ethernet board. It is "just" the chip select pin, so there is a chance that both can coexist and use the same IO. You need to skip WLEDs PinManager for the CS pin, so WLED will not block using it. If you don't know how this works: Leave it. If you know what I'm talking about: Try it and please let me know on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG
### 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.
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 its 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
* What it does: Enables the optional SPI driven OLED that can be mounted to the 7-pin female header. Won't work with Ethernet, read above.
* Possible values: Enabled/Disabled
* Default: Disabled
* OLED-Use-Progress-Bars:
@@ -60,10 +65,15 @@ Navigate to the "Config" and then to the "Usermods" section. If you compiled WLE
* Possible values: Enabled/Disabled
* Default: Disabled
* Enable-SHT30-Temp-Humidity-Sensor:
* What it does: Enabled the onboard SHT30 temperature and humidity sensor
* What it does: Enables the onboard SHT30 temperature and humidity sensor
* Possible values: Enabled/Disabled
* Default: Disabled
## Change log
2021-12
* Adjusted IO layout to match An-Penta v1r1
2021-10
* First implementation.
* First implementation.
## Credits
ezcGman | Andy: Find me on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 KiB

View File

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

View File

@@ -0,0 +1,129 @@
# Seven Segment Display Reloaded
Usermod that uses the overlay feature to create a configurable seven segment display.
Optimized for maximum configurability and use with seven segment clocks by parallyze (https://www.instructables.com/member/parallyze/instructables/)
Very loosely based on the existing usermod "seven segment display".
## Installation
Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`.
For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions.
## Settings
All settings can be controlled the usermod setting page.
Part of the settings can be controlled through MQTT with a raw payload or through a json request to /json/state.
### enabled
Enables/disables this overlay usermod
### inverted
Enables the inverted mode in which the background should be enabled and the digits should be black (leds off)
### Colon-blinking
Enables the blinking colon(s) if they are defined
### enable-auto-brightness
Enables the auto brightness feature. Can be only used with the usermod SN_Photoresistor installed.
### auto-brightness-min / auto-brightness-max
The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here.
The mapping is 0 - 1000 lux will be mapped to auto-brightness-min - auto-brightness-max
The mA current protection of WLED will override the calculated value if it is too high.
### Display-Mask
Defines the type of the time/date display.
For example "H:m" (default)
- H - 00-23 hours
- h - 01-12 hours
- k - 01-24 hours
- m - 00-59 minutes
- s - 00-59 seconds
- d - 01-31 day of month
- M - 01-12 month
- y - 21 last two positions of year
- Y - 2021 year
- : for a colon
### LED-Numbers
- LED-Numbers-Hours
- LED-Numbers-Minutes
- LED-Numbers-Seconds
- LED-Numbers-Colons
- LED-Numbers-Day
- LED-Numbers-Month
- LED-Numbers-Year
See following example for usage.
## Example
Example for Leds definition
```
< A >
/\ /\
F B
\/ \/
< G >
/\ /\
E C
\/ \/
< D >
```
Leds or Range of Leds are seperated by a comma ","
Segments are seperated by a semicolon ";" and are read as A;B;C;D;E;F;G
Digits are seperated by colon ":" -> A;B;C;D;E;F;G:A;B;C;D;E;F;G
Ranges are defined as lower to higher (lower first)
For example, an clock definition for the following clock (https://www.instructables.com/Lazy-7-Quick-Build-Edition/) is
- hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10"
- minute "37-38;39-40;42-43;44,31;32-33;35-36;34,41:21-22;23-24;26-27;28,15;16-17;19-20;18,25"
or
- hour "6,7;8,9;11,12;13,0;1,2;4,5;3,10:52,53;54,55;57,58;59,46;47,48;50,51;49,56"
- minute "15,28;16,17;19,20;21,22;23,24;26,27;18,25:31,44;32,33;35,36;37,38;39,40;42,43;34,41"
depending on the orientation.
# The example detailed:
hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10"
there are two digits seperated by ":"
- 59,46;47-48;50-51;52-53;54-55;57-58;49,56
- 0,13;1-2;4-5;6-7;8-9;11-12;3,10
In the first digit,
the **segment A** consists of the leds number **59 and 46**., **segment B** consists of the leds number **47, 48** and so on
The second digit starts again with **segment A** and leds **0 and 13**, **segment B** consists of the leds number **1 and 2** and so on
### first digit of the hour
- Segment A: 59, 46
- Segment B: 47, 48
- Segment C: 50, 51
- Segment D: 52, 53
- Segment E: 54, 55
- Segment F: 57, 58
- Segment G: 49, 56
### second digit of the hour
- Segment A: 0, 13
- Segment B: 1, 2
- Segment C: 4, 5
- Segment D: 6, 7
- Segment E: 8, 9
- Segment F: 11, 12
- Segment G: 3, 10

View File

@@ -0,0 +1,555 @@
#pragma once
#include "wled.h"
class UsermodSSDR : public Usermod {
//#define REFRESHTIME 497
private:
//Runtime variables.
unsigned long umSSDRLastRefresh = 0;
unsigned long umSSDRResfreshTime = 3000;
bool umSSDRDisplayTime = false;
bool umSSDRInverted = false;
bool umSSDRColonblink = true;
bool umSSDREnableLDR = false;
String umSSDRHours = "";
String umSSDRMinutes = "";
String umSSDRSeconds = "";
String umSSDRColons = "";
String umSSDRDays = "";
String umSSDRMonths = "";
String umSSDRYears = "";
uint16_t umSSDRLength = 0;
uint16_t umSSDRBrightnessMin = 0;
uint16_t umSSDRBrightnessMax = 255;
bool* umSSDRMask = 0;
/*// H - 00-23 hours
// h - 01-12 hours
// k - 01-24 hours
// m - 00-59 minutes
// s - 00-59 seconds
// d - 01-31 day of month
// M - 01-12 month
// y - 21 last two positions of year
// Y - 2021 year
// : for a colon
*/
String umSSDRDisplayMask = "H:m"; //This should reflect physical equipment.
/* Segment order, seen from the front:
< A >
/\ /\
F B
\/ \/
< G >
/\ /\
E C
\/ \/
< D >
*/
uint8_t umSSDRNumbers[11][7] = {
// A B C D E F G
{ 1, 1, 1, 1, 1, 1, 0 }, // 0
{ 0, 1, 1, 0, 0, 0, 0 }, // 1
{ 1, 1, 0, 1, 1, 0, 1 }, // 2
{ 1, 1, 1, 1, 0, 0, 1 }, // 3
{ 0, 1, 1, 0, 0, 1, 1 }, // 4
{ 1, 0, 1, 1, 0, 1, 1 }, // 5
{ 1, 0, 1, 1, 1, 1, 1 }, // 6
{ 1, 1, 1, 0, 0, 0, 0 }, // 7
{ 1, 1, 1, 1, 1, 1, 1 }, // 8
{ 1, 1, 1, 1, 0, 1, 1 }, // 9
{ 0, 0, 0, 0, 0, 0, 0 } // blank
};
//String to reduce flash memory usage
static const char _str_name[];
static const char _str_ldrEnabled[];
static const char _str_timeEnabled[];
static const char _str_inverted[];
static const char _str_colonblink[];
static const char _str_displayMask[];
static const char _str_hours[];
static const char _str_minutes[];
static const char _str_seconds[];
static const char _str_colons[];
static const char _str_days[];
static const char _str_months[];
static const char _str_years[];
static const char _str_minBrightness[];
static const char _str_maxBrightness[];
#ifdef USERMOD_SN_PHOTORESISTOR
Usermod_SN_Photoresistor *ptr;
#else
void* ptr = nullptr;
#endif
void _overlaySevenSegmentDraw() {
int displayMaskLen = static_cast<int>(umSSDRDisplayMask.length());
bool colonsDone = false;
_setAllFalse();
for (int index = 0; index < displayMaskLen; index++) {
int timeVar = 0;
switch (umSSDRDisplayMask[index]) {
case 'h':
timeVar = hourFormat12(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
break;
case 'H':
timeVar = hour(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
break;
case 'k':
timeVar = hour(localTime) + 1;
_showElements(&umSSDRHours, timeVar, 0, 0);
break;
case 'm':
timeVar = minute(localTime);
_showElements(&umSSDRMinutes, timeVar, 0, 0);
break;
case 's':
timeVar = second(localTime);
_showElements(&umSSDRSeconds, timeVar, 0, 0);
break;
case 'd':
timeVar = day(localTime);
_showElements(&umSSDRDays, timeVar, 0, 0);
break;
case 'M':
timeVar = month(localTime);
_showElements(&umSSDRMonths, timeVar, 0, 0);
break;
case 'y':
timeVar = second(localTime);
_showElements(&umSSDRYears, timeVar, 0, 0);
break;
case 'Y':
timeVar = year(localTime);
_showElements(&umSSDRYears, timeVar, 0, 0);
break;
case ':':
if (!colonsDone) { // only call _setColons once as all colons are printed when the first colon is found
_setColons();
colonsDone = true;
}
break;
}
}
_setMaskToLeds();
}
void _setColons() {
if ( umSSDRColonblink ) {
if ( second(localTime) % 2 == 0 ) {
_showElements(&umSSDRColons, 0, 1, 0);
}
} else {
_showElements(&umSSDRColons, 0, 1, 0);
}
}
void _showElements(String *map, int timevar, bool isColon, bool removeZero
) {
if (!(*map).equals("") && !(*map) == NULL) {
int length = String(timevar).length();
bool addZero = false;
if (length == 1) {
length = 2;
addZero = true;
}
int timeArr[length];
if(addZero) {
if(removeZero)
{
timeArr[1] = 10;
timeArr[0] = timevar;
}
else
{
timeArr[1] = 0;
timeArr[0] = timevar;
}
} else {
int count = 0;
while (timevar) {
timeArr[count] = timevar%10;
timevar /= 10;
count++;
};
}
int colonsLen = static_cast<int>((*map).length());
int count = 0;
int countSegments = 0;
int countDigit = 0;
bool range = false;
int lastSeenLedNr = 0;
for (int index = 0; index < colonsLen; index++) {
switch ((*map)[index]) {
case '-':
lastSeenLedNr = _checkForNumber(count, index, map);
count = 0;
range = true;
break;
case ':':
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
count = 0;
range = false;
countDigit++;
countSegments = 0;
break;
case ';':
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
count = 0;
range = false;
countSegments++;
break;
case ',':
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
count = 0;
range = false;
break;
default:
count++;
break;
}
}
_setLeds(_checkForNumber(count, colonsLen, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
}
}
void _setLeds(int lednr, int lastSeenLedNr, bool range, int countSegments, int number, bool colon) {
if ((colon && umSSDRColonblink) || umSSDRNumbers[number][countSegments]) {
if (range) {
for(int i = lastSeenLedNr; i <= lednr; i++) {
umSSDRMask[i] = true;
}
} else {
umSSDRMask[lednr] = true;
}
}
}
void _setMaskToLeds() {
for(int i = 0; i <= umSSDRLength; i++) {
if ((!umSSDRInverted && !umSSDRMask[i]) || (umSSDRInverted && umSSDRMask[i])) {
strip.setPixelColor(i, 0x000000);
}
}
}
void _setAllFalse() {
for(int i = 0; i <= umSSDRLength; i++) {
umSSDRMask[i] = false;
}
}
int _checkForNumber(int count, int index, String *map) {
String number = (*map).substring(index - count, index);
return number.toInt();
}
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_name, 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_name, subTopic);
mqtt->publish(buffer, 2, true, Value.c_str(), Value.length());
}
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_timeEnabled, &umSSDRDisplayTime)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_ldrEnabled, &umSSDREnableLDR)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_inverted, &umSSDRInverted)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
return true;
}
if (strcmp_P(topic, _str_displayMask) == 0) {
umSSDRDisplayMask = String(payload);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
return true;
}
return false;
}
void _updateMQTT()
{
_publishMQTTint_P(_str_timeEnabled, umSSDRDisplayTime);
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
_publishMQTTint_P(_str_inverted, umSSDRInverted);
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
_publishMQTTstr_P(_str_hours, umSSDRHours);
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
_publishMQTTstr_P(_str_seconds, umSSDRSeconds);
_publishMQTTstr_P(_str_colons, umSSDRColons);
_publishMQTTstr_P(_str_days, umSSDRDays);
_publishMQTTstr_P(_str_months, umSSDRMonths);
_publishMQTTstr_P(_str_years, umSSDRYears);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
_publishMQTTint_P(_str_minBrightness, umSSDRBrightnessMin);
_publishMQTTint_P(_str_maxBrightness, umSSDRBrightnessMax);
}
void _addJSONObject(JsonObject& root) {
JsonObject ssdrObj = root[FPSTR(_str_name)];
if (ssdrObj.isNull()) {
ssdrObj = root.createNestedObject(FPSTR(_str_name));
}
ssdrObj[FPSTR(_str_timeEnabled)] = umSSDRDisplayTime;
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
ssdrObj[FPSTR(_str_seconds)] = umSSDRSeconds;
ssdrObj[FPSTR(_str_colons)] = umSSDRColons;
ssdrObj[FPSTR(_str_days)] = umSSDRDays;
ssdrObj[FPSTR(_str_months)] = umSSDRMonths;
ssdrObj[FPSTR(_str_years)] = umSSDRYears;
ssdrObj[FPSTR(_str_minBrightness)] = umSSDRBrightnessMin;
ssdrObj[FPSTR(_str_maxBrightness)] = umSSDRBrightnessMax;
}
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() {
umSSDRLength = strip.getLengthTotal();
if (umSSDRMask != 0) {
umSSDRMask = (bool*) realloc(umSSDRMask, umSSDRLength * sizeof(bool));
} else {
umSSDRMask = (bool*) malloc(umSSDRLength * sizeof(bool));
}
_setAllFalse();
#ifdef USERMOD_SN_PHOTORESISTOR
ptr = (Usermod_SN_Photoresistor*) usermods.lookup(USERMOD_ID_SN_PHOTORESISTOR);
#endif
DEBUG_PRINTLN(F("Setup done"));
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop() {
if (!umSSDRDisplayTime || strip.isUpdating()) {
return;
}
#ifdef USERMOD_ID_SN_PHOTORESISTOR
if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
if (ptr != nullptr) {
uint16_t lux = ptr->getLastLDRValue();
uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax);
if (bri != brightness) {
bri = brightness;
stateUpdated(1);
}
}
umSSDRLastRefresh = millis();
}
#endif
}
void handleOverlayDraw() {
if (umSSDRDisplayTime) {
_overlaySevenSegmentDraw();
}
}
/*
* 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[F("u")];
if (user.isNull()) {
user = root.createNestedObject(F("u"));
}
JsonArray enabled = user.createNestedArray("Time enabled");
enabled.add(umSSDRDisplayTime);
JsonArray invert = user.createNestedArray("Time inverted");
invert.add(umSSDRInverted);
JsonArray blink = user.createNestedArray("Blinking colon");
blink.add(umSSDRColonblink);
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
ldrEnable.add(umSSDREnableLDR);
}
/*
* 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) {
JsonObject user = root[F("u")];
if (user.isNull()) {
user = root.createNestedObject(F("u"));
}
_addJSONObject(user);
}
/*
* 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) {
JsonObject user = root[F("u")];
if (!user.isNull()) {
JsonObject ssdrObj = user[FPSTR(_str_name)];
umSSDRDisplayTime = ssdrObj[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime;
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
}
}
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_name);
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_name);
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("/wledSS/"));
if (strncmp_P(topic, PSTR("/wledSS/"), 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) {
_addJSONObject(root);
}
bool readFromConfig(JsonObject &root) {
JsonObject top = root[FPSTR(_str_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_str_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
umSSDRDisplayTime = (top[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime);
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
umSSDRMinutes = top[FPSTR(_str_minutes)] | umSSDRMinutes;
umSSDRSeconds = top[FPSTR(_str_seconds)] | umSSDRSeconds;
umSSDRColons = top[FPSTR(_str_colons)] | umSSDRColons;
umSSDRDays = top[FPSTR(_str_days)] | umSSDRDays;
umSSDRMonths = top[FPSTR(_str_months)] | umSSDRMonths;
umSSDRYears = top[FPSTR(_str_years)] | umSSDRYears;
umSSDRBrightnessMin = top[FPSTR(_str_minBrightness)] | umSSDRBrightnessMin;
umSSDRBrightnessMax = top[FPSTR(_str_maxBrightness)] | umSSDRBrightnessMax;
DEBUG_PRINT(FPSTR(_str_name));
DEBUG_PRINTLN(F(" config (re)loaded."));
return true;
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() {
return USERMOD_ID_SSDR;
}
};
const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";
const char UsermodSSDR::_str_seconds[] PROGMEM = "LED-Numbers-Seconds";
const char UsermodSSDR::_str_colons[] PROGMEM = "LED-Numbers-Colons";
const char UsermodSSDR::_str_days[] PROGMEM = "LED-Numbers-Day";
const char UsermodSSDR::_str_months[] PROGMEM = "LED-Numbers-Month";
const char UsermodSSDR::_str_years[] PROGMEM = "LED-Numbers-Year";
const char UsermodSSDR::_str_ldrEnabled[] PROGMEM = "enable-auto-brightness";
const char UsermodSSDR::_str_minBrightness[] PROGMEM = "auto-brightness-min";
const char UsermodSSDR::_str_maxBrightness[] PROGMEM = "auto-brightness-max";

View File

@@ -1,35 +0,0 @@
# SSD1306 128x32 OLED via I2C with u8g2
This usermod allows to connect 128x32 Oled display to WLED controlled and show
the next information:
- Current SSID
- IP address if obtained
* in AP mode and turned off lightning AP password is shown
- Current effect
- Current palette
- On/Off icon (sun/moon)
## Hardware
![Hardware connection](assets/hw_connection.png)
## Requirements
Functionality checked with:
- commit 095429a7df4f9e2b34dd464f7bbfd068df6558eb
- Wemos d1 mini
- PlatformIO
- Generic SSD1306 128x32 I2C OLED display from aliexpress
### Platformio
Add `U8g2@~2.27.2` dependency to `lib_deps_external` under `[common]` section in `platformio.ini`:
```ini
# platformio.ini
...
[common]
...
lib_deps_external =
...
U8g2@~2.27.2
...
```
### Arduino IDE
Install library `U8g2 by oliver` in `Tools | Include Library | Manage libraries` menu.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -1,175 +0,0 @@
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
//The SCL and SDA pins are defined here.
//Lolin32 boards use SCL=5 SDA=4
#define U8X8_PIN_SCL 5
#define U8X8_PIN_SDA 4
// 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
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL,
U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void userSetup() {
u8x8.begin();
u8x8.setPowerSave(0);
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.drawString(0, 0, "Loading...");
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void userConnected() {}
// 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;
long lastUpdate = 0;
long lastRedraw = 0;
bool displayTurnedOff = false;
// How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 5000
void userLoop() {
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
return;
}
lastUpdate = millis();
// Turn off display after 3 minutes with no change.
if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) {
u8x8.setPowerSave(1);
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)
{
u8x8.setPowerSave(0);
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;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
// Print `~` char to indicate that SSID is longer, than owr dicplay
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
// Second row with IP or Psssword
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
u8x8.print(apPass);
else
u8x8.print(knownIp);
// Third row with mode name
u8x8.setCursor(2, 2);
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;
u8x8.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
break;
}
// Fourth row with palette name
u8x8.setCursor(2, 3);
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;
u8x8.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
break;
}
u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
u8x8.drawGlyph(0, 0, 80); // wifi icon
u8x8.drawGlyph(0, 1, 68); // home icon
u8x8.setFont(u8x8_font_open_iconic_weather_2x2);
u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon
}

View File

@@ -111,7 +111,7 @@ class StairwayWipeUsermod : public Usermod {
transitionDelayTemp = 4000; //fade out slowly
#endif
bri = 0;
colorUpdated(CALL_MODE_NOTIFICATION);
stateUpdated(CALL_MODE_NOTIFICATION);
wipeState = 0;
userVar0 = 0;
previousUserVar0 = 0;

View File

@@ -104,7 +104,7 @@ void turnOff()
transitionDelayTemp = 4000; //fade out slowly
#endif
bri = 0;
colorUpdated(CALL_MODE_NOTIFICATION);
stateUpdated(CALL_MODE_NOTIFICATION);
wipeState = 0;
userVar0 = 0;
previousUserVar0 = 0;

View File

@@ -91,8 +91,8 @@ class AutoSaveUsermod : public Usermod {
knownBrightness = bri;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
}
// gets called every time WiFi is (re-)connected. Initialize own network
@@ -106,8 +106,8 @@ class AutoSaveUsermod : public Usermod {
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;
uint8_t currentMode = strip.getMainSegment().mode;
uint8_t currentPalette = strip.getMainSegment().palette;
unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
if (knownBrightness != bri) {

View File

@@ -31,9 +31,33 @@ This usermod requires the `U8g2` and `Wire` libraries. See the
`platformio_override.ini.sample` found in the Rotary Encoder
UI usermod folder for how to include these using `platformio_override.ini`.
## Configuration
* `enabled` - enable/disable usermod
* `pin` - GPIO pins used for display; I2C displays use Clk & Data; SPI displays can use SCK, MOSI, CS, DC & RST
* `type` - display type in numeric format
* 1 = I2C SSD1306 128x32
* 2 = I2C SH1106 128x32
* 3 = I2C SSD1306 128x64 (4 double-height lines)
* 4 = I2C SSD1305 128x32
* 5 = I2C SSD1305 128x64 (4 double-height lines)
* 6 = SPI SSD1306 128x32
* 7 = SPI SSD1306 128x64 (4 double-height lines)
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
* `refreshRateSec` - time in seconds for display refresh
* `screenTimeOutSec` - screen saver time-out in seconds
* `flip` - flip/rotate display 180°
* `sleepMode` - enable/disable screen saver
* `clockMode` - enable/disable clock display in screen saver mode
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
## Change Log
2021-02
* First public release
2021-04
* Adaptation for runtime configuration.
* Adaptation for runtime configuration.
2021-11
* Added configuration option description.

View File

@@ -25,6 +25,10 @@
//The SCL and SDA pins are defined here.
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#define HW_PIN_CLOCKSPI 18
#define HW_PIN_DATASPI 23
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 22
#endif
@@ -47,6 +51,10 @@
#define FLD_PIN_RESET 26
#endif
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#define HW_PIN_CLOCKSPI 14
#define HW_PIN_DATASPI 13
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5
#endif
@@ -70,6 +78,14 @@
#endif
#endif
#ifndef FLD_TYPE
#ifndef FLD_SPI_DEFAULT
#define FLD_TYPE SSD1306
#else
#define FLD_TYPE SSD1306_SPI
#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
@@ -115,11 +131,11 @@ class FourLineDisplayUsermod : public Usermod {
#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
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
#endif
DisplayType type = FLD_TYPE; // display type
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows
@@ -127,6 +143,7 @@ class FourLineDisplayUsermod : public Usermod {
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode = true; // allow screen sleep?
bool clockMode = false; // display clock
bool enabled = true;
// Next variables hold the previous known values to determine if redraw is
// required.
@@ -150,6 +167,7 @@ class FourLineDisplayUsermod : public Usermod {
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _contrast[];
static const char _refreshRate[];
static const char _screenTimeOut[];
@@ -169,88 +187,72 @@ class FourLineDisplayUsermod : public Usermod {
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
if (type == NONE) return;
if (type == NONE || !enabled) return;
bool isHW;
PinOwner po = PinOwner::UM_FourLineDisplay;
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; }
isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI);
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
} else {
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA);
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) { 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
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else 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
if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else 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
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else 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
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else 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
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else 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
if (!isHW) 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
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
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);
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
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
/*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
@@ -266,7 +268,7 @@ class FourLineDisplayUsermod : public Usermod {
* Da loop.
*/
void loop() {
if (millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
if (!enabled || millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
lastUpdate = millis();
redraw(false);
@@ -276,40 +278,40 @@ class FourLineDisplayUsermod : public Usermod {
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
if (type==NONE) return;
if (type == NONE || !enabled) return;
u8x8->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
if (type==NONE) return;
if (type == NONE || !enabled) return;
u8x8->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (type==NONE) return;
if (type == NONE || !enabled) 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;
if (type == NONE || !enabled) 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;
if (type == NONE || !enabled) 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;
if (type==NONE || !enabled) return 0;
return u8x8->getCols();
}
void clear() {
if (type==NONE) return;
if (type == NONE || !enabled) return;
u8x8->clear();
}
void setPowerSave(uint8_t save) {
if (type==NONE) return;
if (type == NONE || !enabled) return;
u8x8->setPowerSave(save);
}
@@ -327,7 +329,7 @@ class FourLineDisplayUsermod : public Usermod {
static bool showName = false;
unsigned long now = millis();
if (type==NONE) return;
if (type == NONE || !enabled) return;
if (overlayUntil > 0) {
if (now >= overlayUntil) {
// Time to display the overlay has elapsed.
@@ -347,8 +349,8 @@ class FourLineDisplayUsermod : public Usermod {
(knownBrightness != bri) ||
(knownEffectSpeed != effectSpeed) ||
(knownEffectIntensity != effectIntensity) ||
(knownMode != strip.getMode()) ||
(knownPalette != strip.getSegment(0).palette)) {
(knownMode != strip.getMainSegment().mode) ||
(knownPalette != strip.getMainSegment().palette)) {
knownHour = 99; // force time update
lastRedraw = now; // update lastRedraw marker
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
@@ -396,8 +398,8 @@ class FourLineDisplayUsermod : public Usermod {
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
@@ -438,6 +440,7 @@ class FourLineDisplayUsermod : public Usermod {
void drawLine(uint8_t line, Line4Type lineType) {
char lineBuffer[LINE_BUFFER_SIZE];
uint8_t printedChars;
switch(lineType) {
case FLD_LINE_BRIGHTNESS:
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
@@ -452,10 +455,16 @@ class FourLineDisplayUsermod : public Usermod {
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_MODE:
showCurrentEffectOrPalette(knownMode, JSON_mode_names, line);
printedChars = extractModeName(knownMode, JSON_mode_names, lineBuffer, LINE_BUFFER_SIZE-1);
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_PALETTE:
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, line);
printedChars = extractModeName(knownPalette, JSON_palette_names, lineBuffer, LINE_BUFFER_SIZE-1);
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_TIME:
default:
@@ -464,41 +473,6 @@ class FourLineDisplayUsermod : public Usermod {
}
}
/**
* Display the current effect or palette (desiredEntry)
* on the appropriate line (row).
*/
void showCurrentEffectOrPalette(int knownMode, const char *qstring, uint8_t row) {
char lineBuffer[LINE_BUFFER_SIZE];
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(qstring); i++) {
singleJsonSymbol = pgm_read_byte_near(qstring + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode)) break;
lineBuffer[printedChars++] = singleJsonSymbol;
}
if ((qComma > knownMode) || (printedChars >= getCols()-2) || printedChars >= sizeof(lineBuffer)-2) break;
}
for (;printedChars < getCols()-2 && printedChars < sizeof(lineBuffer)-2; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(2, row*lineHeight, lineBuffer);
}
/**
* If there screen is off or in clock is displayed,
* this will return true. This allows us to throw away
@@ -506,6 +480,7 @@ class FourLineDisplayUsermod : public Usermod {
* to wake up the screen.
*/
bool wakeDisplay() {
if (type == NONE || !enabled) return false;
knownHour = 99;
if (displayTurnedOff) {
// Turn the display back on
@@ -522,6 +497,8 @@ class FourLineDisplayUsermod : public Usermod {
* Clears the screen and prints on the middle two lines.
*/
void overlay(const char* line1, const char *line2, long showHowLong) {
if (type == NONE || !enabled) return;
if (displayTurnedOff) {
// Turn the display back on (includes clear())
sleepOrClock(false);
@@ -583,6 +560,7 @@ class FourLineDisplayUsermod : public Usermod {
* the useAMPM configuration.
*/
void showTime(bool fullScreen = true) {
if (type == NONE || !enabled) return;
char lineBuffer[LINE_BUFFER_SIZE];
updateLocalTime();
@@ -676,10 +654,12 @@ class FourLineDisplayUsermod : public Usermod {
*/
void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
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["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
top["type"] = type;
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_refreshRate)] = refreshRate/1000;
@@ -710,6 +690,7 @@ class FourLineDisplayUsermod : public Usermod {
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
newType = top["type"] | newType;
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip;
@@ -718,7 +699,10 @@ class FourLineDisplayUsermod : public Usermod {
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
if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
else
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
@@ -733,10 +717,10 @@ class FourLineDisplayUsermod : public Usermod {
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];
}
PinOwner po = PinOwner::UM_FourLineDisplay;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE;
return true;
@@ -750,7 +734,7 @@ class FourLineDisplayUsermod : public Usermod {
if (needsRedraw && !wakeDisplay()) redraw(true);
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !(top[_busClkFrequency]).isNull();
return !top[FPSTR(_enabled)].isNull();
}
/*
@@ -764,6 +748,7 @@ class FourLineDisplayUsermod : public Usermod {
// strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";

View File

@@ -0,0 +1,477 @@
#pragma once
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
/*
Fontname: wled_logo_akemi_4x4
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 3/3
BBX Build Mode: 3
* this logo ...WLED/images/wled_logo_akemi.png
* encode map = 1, 2, 3
*/
const uint8_t u8x8_wled_logo_akemi_4x4[388] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_4x4") =
"\1\3\4\4\0\0\0\0\0\0\0\0\0\340\360\10\350\10\350\210\270\210\350\210\270\350\10\360\340\0\0\0"
"\0\0\200\200\0\0@\340\300\340@\0\0\377\377\377\377\377\377\37\37\207\207\371\371\371\377\377\377\0\0\374"
"\374\7\7\371\0\0\6\4\15\34x\340\200\177\177\377\351yy\376\356\357\217\177\177\177o\377\377\0\70\77"
"\277\376~\71\0\0\0\0\0\0\0\1\3\3\3\1\0\0\37\77\353\365\77\37\0\0\0\0\5\7\2\3"
"\7\4\0\0\300\300\300\300\200\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0"
"\0\200\200\300\371\37\37\371\371\7\7\377\374\0\0\0\374\377\377\37\37\341\341\377\377\377\377\374\0\0\0\374"
"\377\7\7\231\371\376>\371\371>~\377\277\70\0\270\377\177\77\376\376\71\371\371\71\177\377\277\70\0\70\377"
"\177>\376\371\377\377\0\77\77\0\0\4\7\2\7\5\0\0\0\377\377\0\77\77\0\0\0\5\7\2\7\5"
"\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\231\231\231\371\377\377\374\0\0\0\374\377\347\347\371\1\1\371\371\7\7\377\374\0\0\0@\340"
"\300\340@\0\71\371\371\71\177\377\277\70\0\70\277\377\177\71\371\370\70\371\371~\376\377\77\70\200\340x\34"
"\15\4\6\0\0\77\77\0\0\0\5\7\2\7\5\0\0\0\377\377\0\77\77\0\0\1\3\3\1\1\0\0"
"\0\0\0";
/*
Fontname: wled_logo_akemi_5x5
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 3/3
BBX Build Mode: 3
* this logo ...WLED/images/wled_logo_akemi.png
* encoded = 1, 2, 3
*/
/*
const uint8_t u8x8_wled_logo_akemi_5x5[604] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_5x5") =
"\1\3\5\5\0\0\0\0\0\0\0\0\0\0\0\0\340\340\374\14\354\14\354\14|\14\354\14||\14\354"
"\14\374\340\340\0\0\0\0\0\0\0\200\0\0\0\200\200\0\200\200\0\0\0\0\377\377\377\376\377\376\377\377"
"\377\377\77\77\307\307\307\307\306\377\377\377\0\0\0\360\374>\77\307\0\0\61cg\357\347\303\301\200\0\0"
"\377\377\377\317\317\317\317\360\360\360\374\374\377\377\377\377\377\377\377\377\0\0\200\377\377\340\340\37\0\0\0\0"
"\0\0\1\3\17\77\374\360\357\357\177\36\14\17\357\377\376\376>\376\360\357\17\17\14>\177o\340\300\343c"
"{\77\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\37\37\362\375\37\37\17\0\0"
"\0\0\1\1\1\0\1\1\1\0\0\0\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\200\200"
"\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\200\200\307\307\377\377\307\307\307\77>\374\360\0"
"\0\0\360\374\376\377\377\377\7\7\7\377\377\377\377\376\374\360\0\0\0\0\360\374\36\37\37\343\37\37\340\340"
"\37\37\37\340\340\377\377\200\0\200\377\377\377\340\340\340\37\37\37\37\37\37\37\377\377\377\200\0\0\200\377\377"
"\340\340\340\34\377\377\3\3\377\377\3\17\77{\343\303\300\303\343s\77\37\3\377\377\3\3\377\377\3\17\77"
"{\343\303\300\300\343{\37\17\3\377\377\377\377\0\0\37\37\0\0\1\1\1\1\0\1\1\1\1\0\0\377"
"\377\0\0\37\37\0\0\1\1\1\1\0\0\1\1\1\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\343\343\343\343"
"\343\377\376\374\360\0\0\0\360\374\376\77\77\307\307\7\7\307\307\307\77>\374\360\0\0\0\0\0\200\200\0"
"\200\200\0\0\34\34\34\37\37\377\377\377\377\200\0\200\377\377\377\377\37\37\37\0\0\37\37\37\340\340\377\377"
"\200\0\0\0\1\303\347\357gc\61\0\3\3\377\377\3\7\37\177s\343\300\303s{\37\17\7\3\377\377"
"\3\3\377\377\3\37\77scp<\36\17\3\1\0\0\0\0\0\0\0\37\37\0\0\0\1\1\1\0\1"
"\1\1\0\0\0\0\377\377\0\0\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
*/
/*
Fontname: wled_logo_2x2
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 4/4
BBX Build Mode: 3
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
* encode map = 1, 2, 3, 4
*/
const uint8_t u8x8_wled_logo_2x2[133] U8X8_FONT_SECTION("u8x8_wled_logo_2x2") =
"\1\4\2\2\0\0\0\0\0\200\200\360\360\16\16\16\16\0\0\0\340\340\340\340\340\37\37\1\1\0\0\0"
"\0\0\0\0\360\360\16\16\16\200\200\16\16\16\360\360\0\0\0\200\37\37\340\340\340\37\37\340\340\340\37\37"
"\0\0\0\37\200~~\0\0\0\0\0\0\0\360\360\216\216\216\216\37\340\340\340\340\340\340\340\0\0\37\37"
"\343\343\343\343\16\16\0\0ppp\16\16\376\376\16\16\16\360\360\340\340\0\0\0\0\0\340\340\377\377\340"
"\340\340\37\37";
/*
Fontname: wled_logo_4x4
Copyright: Created with Fony 1.4.7
Glyphs: 4/4
BBX Build Mode: 3
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
* encode map = 1, 2, 3, 4
*/
/*
const uint8_t u8x8_wled_logo_4x4[517] U8X8_FONT_SECTION("u8x8_wled_logo_4x4") =
"\1\4\4\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\300\300\377\377\377\377\377\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\377\377\17\17\17\17\17\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\370\370\370\370\370\370\370\370\370\7\7\7\7\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\374\374\374\374\374\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0"
"\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\300\300\300\300\300\0\0\0\0\0\377\377\377\377\377\0\0"
"\0\0\300\300\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0"
"\0\0\377\377\0\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\0\0"
"\0\0\7\7\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374"
"\374\374\374\374\300\300\300\77\77\77\77\77\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\300\300\300"
"\300\300\300\300\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\37\37\37"
"\37\37\37\37\7\7\7\370\370\370\370\370\370\370\370\370\370\370\370\370\0\0\0\0\7\7\7\7\7\370\370\370"
"\370\370\370\370\374\374\374\374\374\374\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374\374\374\374\374\374"
"\0\0\0\0\300\300\0\0\0\0\0\0\0\77\77\77\77\77\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
"\377\377\377\377\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
"\377\377\377\377\370\370\370\370\370\370\0\0\0\0\0\0\0\0\370\370\370\370\377\377\377\377\377\370\370\370\370\377"
"\7\7\7\7";
*/
/*
Fontname: 4LineDisplay_WLED_icons_1x
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 13/13
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*-----------
* 20 = wifi
* 21 = media-play
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_1x1[172] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_1x1") =
"\1\25\1\1\0B\30<<\30B\0~<\30\0~<\30\0p\374\77\216\340\370\360\0<n\372\377"
"\275\277\26\34\374\374\77\77\374\374\60\60<~~\360\340``\0\200\340\360p\14\16\6\1<~\377\377"
"\201\201B<\70D\200\217\200D\70\0\0\10x<<x\10\0\14\36>||>\36\14\64 \336\67"
";\336 \64\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\1\11\311"
"\311\1\2\0\0~<<\30\30\0";
/*
Fontname: 4LineDisplay_WLED_icons_2x1
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_2x1[196] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x1") =
"\1\14\2\1\20\20BB\30\30<\275\275<\30\30BB\20\20\377~<<\70\30\20\0\377~<<"
"\70\30\20\0\60p\370\374\77>\236\214\300\340\370\360\360\340\0\0\34<v\326\336\375\375\377\277\275=>"
"\66\66<\34\374\374\374\374~\77\77~\374\374\374\374 pp \30<~~\377\370\360\360\340\340\340\340"
"@@ \0\200\300\340\360\360p`\10\34\34\16\6\6\3\0\0\70|~\376\376\377\377\377\201\201\203\202"
"\302Fl\70\70xL\204\200\200\217\217\200\200\204Lx\70\0\0\10\10\30\330x|\77\77|x\330\30"
"\10\10\0\0\14\36\37\77\77\177~\374\374~\177\77\77\37\36\14\24\64 \60>\26\367\33\375\36>\60"
" \64\24";
/*
Fontname: 4LineDisplay_WLED_icons_2x
Copyright:
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_2x2[389] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x2") =
"\1\14\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\340\370\374\376\376\377\377\377\3\3\7\6\16<\370\340\7\37\77\177\177\377\377\377\300\300\340`"
"p<\37\7\300\340p\30\0\0\377\377\0\0\30p\340\300\0\0\17\37\70`\340\300\300\300\300\340`\70"
"\37\17\0\0\0@\300\300\300\300\340\374\374\340\300\300\300\300@\0\0\0\0\1s\77\37\17\17\37\77s"
"\1\0\0\0\360\370\374\374\374\374\370\360\360\370\374\374\374\374\370\360\0\1\3\7\17\37\77\177\177\77\37\17"
"\7\3\1\0\200\200\0\0\0\360\370\374<\334\330\360\0\0\200\200\2\2\14\30\24\37\6~\7\177\7\37"
"\24\30\16\2";
/*
Fontname: 4LineDisplay_WLED_icons_3x
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_3x3[868] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_3x3") =
"\1\14\3\3\0\0\34\34\34\0\200\300\300\340\347\347\347\340\300\300\200\0\34\34\34\0\0\0\34\34\34\0"
"\0>\377\377\377\377\377\377\377\377\377\377\377>\0\0\34\34\34\0\0\0\16\16\16\0\0\1\1\3ss"
"s\3\1\1\0\0\34\34\34\0\0\0\370\360\340\300\300\200\0\0\0\0\0\0\370\360\340\300\300\200\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\376~<\70\20\377\377\377\377\377\377\377\376~<\70\20\37\17\17\7"
"\3\1\1\0\0\0\0\0\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\300\361\376\374\370\360\300"
"\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376\377\377\377\377\377\177\77\17\6\0\200\342\374\370\360\340"
"\200\0\0\0\1\17\37\77\177\377\7\3\0\200\360\370\374\376\377\377\377\377\377\377\77\0\0\0\0\200\340\360"
"\370\370\374\316\206\206\317\377\377\377\317\206\206\316\374\374\370\360\340\200<\377\377\371\360py\377\377\377\377\377"
"\377\377\377\377\377\377\363\341\341\363\377\177\0\1\7\17\34\70x|\377\377\377\377\367\363c\3\3\3\3\1"
"\1\1\0\0\300\300\300\300\300\300\300\316\377\377\377\316\300\300\300\300\300\300\0\0\0\0\0\0\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\300\300\340\340\340\300\377\377\377\377\377\377\377\307\3\3\3\307"
"\377\377\377\377\377\377\1\1\3\3\3\1\0\300\340\370\374\374\376\377\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0>\377\377\377\377\377\377\377\377\374\360\340\300\300\200\200\0\0\0\0\0\0\200\200\0\1\7\17"
"\37\37\77\177\177\177\177\377\377\377\177\177\177\77\77\37\17\7\3\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\200\200\300\340\340\360\370\374|>\17\6\0\0\0\0\0\340\340\360\360\360\342\303\7\17\37\77\37\7\3\1"
"\0\0\0\0\0\200\340\360\377\377\377\377\177\77\37\17\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360"
"\370\374\374\376\376\376\377\377\7\7\7\6\16\16\34\70\360\340\300\0|\377\377\377\377\377\377\377\377\377\377\377"
"\0\0\0\0\0\0\0\0\0\377\377\377\0\3\7\17\37\77\177\177\377\377\377\377\340\340\340\340pp\70<"
"\37\17\3\0\0\0\200\300\340\340\300\0\0\377\377\377\0\0\300\340\340\300\200\0\0\0\0\0\370\376\377\17"
"\3\0\0\0\0\17\17\17\0\0\0\0\0\3\17\377\376\370\0\0\0\7\17\37<xp\340\340\340\340\340"
"\340\340\340px<\37\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\376\370\200\0\0\0\0\0"
"\0\0\0\0\0\2\6\16\36\36>~\376\376\377\377\377\377\377\376\376~>\36\16\6\6\2\0\0\0\0"
"\0\300x<\37\17\17\7\3\7\17\17\37<x\300\0\0\0\0\200\300\340\360\360\370\370\370\360\360\340\300"
"\200\300\340\360\360\370\370\370\360\360\340\200\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\177\77\37\17\0\0\0\0\0\1\3\7\17\37\77\177\177\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0"
"\0\0\0\200\300\340\360\370\370\370p`\300\200\0\0\0\0\0\0&,f\300\0\0\300\377\377\357\357\357"
"\363\370\377\377\377\377\300\0\0\306l&\0\0\0\1\7\16\14\6\7\1\177\177\0\177\177\1\7\6\14\16"
"\7\1\0";
/*
Fontname: 4LineDisplay_WLED_icons_4x
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
/*
const uint8_t u8x8_4LineDisplay_WLED_icons_4x4[1540] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_4x4") =
"\1\14\4\4\0\0\0\0`\360\360`\0\0\0\0\0\0\6\17\17\6\0\0\0\0\0\0`\360\360`"
"\0\0\0\0\200\300\300\200\0\0\0\0\340\370\374\376\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0"
"\200\300\300\200\1\3\3\1\0\0\0\0\7\37\77\177\177\377\377\377\377\377\377\177\177\77\37\7\0\0\0\0"
"\1\3\3\1\0\0\0\0\6\17\17\6\0\0\0\0\0\0`\360\360`\0\0\0\0\0\0\6\17\17\6"
"\0\0\0\0\360\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\360\340\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\376\374\374\370\360\340"
"\340\300\200\0\377\377\377\377\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\177\77\77\37\17\7"
"\7\3\1\0\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\17\7\3\1\1\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\341\376\374\370\360\340\300\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\200\300\360\370\374\376\377\377\377\377\377\377\377\377~\0\0\0\0\20\340\300\200\0\0\0\0"
"\0\0\0\0~\377\377\377\377\377\377\377\377\177\77\37\17\3\1\0\200\300\340\370\376\377\377\377\377\376\374\340"
"\0\0\0\0\0\3\7\17\37\77\177\207\1\0\0\0\340\370\374\377\377\377\377\377\377\377\377\377\377\177\77\17"
"\0\0\0\0\0\0\0\200\300\360\370\370\374\34\16\16\16\36\377\377\377\377\37\16\16\16\36\374\374\370\370\360"
"\340\300\0\0\340\374\377\377\217\7\7\7\217\377\376\376\376\377\377\377\377\377\377\376\376\376\377\377\217\7\7\7"
"\217\377\377\374\17\177\377\377\377\37\17\17\17\37\377\377\377\377\377\377\377\377\377\377\377\377\177\177\177\177\77\77"
"\77\37\17\7\0\0\0\3\7\17\36>>\177\177\377\377\377\377\377\377\371p\60\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0<\376\377\377\377\377\376<\0\0\0\0\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\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\303\1\0\0\0\0\1\303\377\377\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\0\0\200\340\360\370\374\374\376\376\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\370\377\377\377\377\377\377\377\377\377\376\360\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\7\77\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\370\370\360\360\360\340\340\340\340\340\340"
"\340\340\60\0\0\0\0\1\3\7\17\37\37\77\77\77\177\177\177\177\177\177\177\177\77\77\77\37\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\200\200\300\340\340\360\370\374\374"
"~\77\16\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\30\34>~\377\377\377\377\177\77\37\7\3\0"
"\0\0\0\0\0\0\0\0\0\360\374\376\377\377\377\377\377\376\374\370\0\0\0\3\3\1\0\0\0\0\0\0"
"\0\0\0\0@@\340\370\374\377\377\377\177\177\177\77\37\17\7\1\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\200\300\340\360\370\374\374\376\376\376\377\377\377\377\17\17\17\37\36\36>|\374\370\360\340"
"\300\200\0\0\360\376\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\37"
"\377\377\376\360\17\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\200\300\370"
"\377\377\177\17\0\0\1\3\7\17\37\77\77\177\177\177\377\377\377\377\360\360\360\370xx|>\77\37\17\7"
"\3\1\0\0\0\0\0\0\0\200\300\200\0\0\0\0\377\377\377\377\0\0\0\0\200\300\200\0\0\0\0\0"
"\0\0\0\0\300\360\374\376\177\37\7\3\3\0\0\0\377\377\377\377\0\0\0\3\3\7\37\177\376\374\360\300"
"\0\0\0\0\77\377\377\377\340\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\377\377\377\77"
"\0\0\0\0\0\0\3\7\17\37><|x\370\360\360\360\360\360\360\370x|<>\37\17\7\3\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\374\374\340\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\20\60p\360\360\360\360\360\360\360\360\370\377\377\377\377\377\377\370\360\360\360\360\360\360\360\360"
"p\60\20\0\0\0\0\0\0\0\1\3\7\317\377\377\377\377\377\377\377\377\377\377\377\377\317\7\3\1\0\0"
"\0\0\0\0\0\0\0\0\0\0\0p>\37\17\17\7\3\1\0\0\1\3\7\17\17\37>p\0\0\0"
"\0\0\0\0\0\200\300\340\340\360\360\360\360\360\360\340\340\300\200\0\0\200\300\340\340\360\360\360\360\360\360\340"
"\340\300\200\0~\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377~\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17"
"\7\3\1\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\177\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\200\300\340\340\360\360\360\360\340\340\300\200\0\0\0\0\0\0"
"\0\0\0\0\0@\340\300\340@\0\0\0\376\377\377\177\177\177\237\207\347\371\371\371\377\376\0\0\0\0@"
"\340\300\340@\2\4\4\35x\340\200\0\30\237\377\177\36\376\376\37\37\377\377\37\177\377\237\30\0\200\340x"
"\34\5\4\2\0\0\0\0\0\1\3\3\3\1\0\0\0\17\17\0\0\17\17\0\0\0\1\3\3\3\1\0"
"\0\0\0";
*/
/*
Fontname: 4LineDisplay_WLED_icons_6x
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
// you can replace this (wasteful) font by using 3x3 variant with draw2x2Glyph()
const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_6x6") =
"\1\14\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\340\360\360\370\374\374\374\376\376\376\377\377\377\377\377\77\77\77\77"
"\177~~\376\374\374\374\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\340\360\374\376\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\1\1\3\7\17\37\177\377\377\376\374"
"\360\340\0\0\370\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\0\0\0\0\1\17\377\377\377\377\377\370\37\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\0\0\0\0\200\360\377\377"
"\377\377\377\37\0\0\7\17\77\177\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\200\200\300\340\360\370\376\377\377\177\77\17\7\0\0\0\0\0\0\0\0\0\1\3\7\17\17"
"\37\77\77\77\177\177\177\377\377\377\377\377\374\374\374\374\376~~\177\77\77\77\37\17\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\377\377\377\377\377\377\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\340\360\370\374\376\376|"
"x \0\0\0\0\377\377\377\377\377\377\0\0\0\0 x|\376\376\374\370\360\340\300\200\0\0\0\0\0"
"\0\0\0\0\300\370\376\377\377\377\177\17\7\1\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\1\7\37\177\377\377\377\376\370\200\0\0\0\0\0\0\177\377\377\377\377\377\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\200\377\377\377\377\377\177\0\0"
"\0\0\0\0\0\7\37\177\377\377\377\374\370\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\200\200\300\340\370\374\377\377\377\177\37\7\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\37\77"
"\77\177~~~\374\374\374\374\374\374\374\374~~~\177\77\77\37\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\340\374\374\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\0\0"
"\0\0\0\0\0\0\0\300\370\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\4\14\34<<|\374\374\374\374\374\374\374\374\374\374\374\376\377\377\377\377\377\377\377\377\377"
"\377\376\374\374\374\374\374\374\374\374\374\374\374|<<\34\14\4\0\0\0\0\0\0\0\0\0\1\3\3\7"
"\17\37\77\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\77\37\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\300\370\377\377\377\377\377\377\177\77\37\17\17\37\77\177"
"\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0p>"
"\37\17\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\17\37>p\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\0\0\0\0"
"\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\200\360\370\374\376\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\376\374\370\360\200\200\360\370\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376"
"\374\370\360\200\37\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\377\377\377\377\377\377\377\377\377\377\37\0\0\1\3\7\17\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\177\77\37\17\7"
"\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\177\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\1\3\7\17\37\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\0\0\0\0\0\0\300\300\300\300\300\300\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\0\0\0\0\0\0"
"\0\0\340\370\370\376\376\377\377\377\377\377\377\377\377\77\77\77>\376\370\370\340\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0 p\360\340\360p \0\0\0\0\0\0\377\377\377\377\177\177\177\177\177\207\207\340\340\377"
"\377\377\377\377\377\377\377\0\0\0\0\0 p\360\340\360p \0\6\4\14\14\15|x\360\200\200\0\0"
"pp\177\177\377\377\374|\374\374\374\177\177\177\377\377\377\177\377\377\377\377\177pp\0\0\200\200\360x}"
"\14\14\4\6\0\0\0\0\0\0\0\3\37\37|ppp\34\34\37\3\3\0\377\377\377\0\0\0\377\377"
"\377\0\3\3\37\37\34ppp~\37\37\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\0\0\7\7\7\0\0\0\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0";
/*
Fontname: akemi_8x8
Copyright: Benji (https://github.com/proto-molecule)
Glyphs: 1/1
BBX Build Mode: 3
* 12 = Akemi
*/
/*
const uint8_t u8x8_akemi_8x8[516] U8X8_FONT_SECTION("u8x8_akemi_8x8") =
"\14\14\10\10\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\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\0\0\0\0\0\0\0\0"
"\200\200\200\200\200\200\200\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\0\0\0\0\0\0\0\0\0\0\0\0\0\340\340\370\370\376\376\376\376"
"\377\377\377\377\377\377\377\377\376\376\376\376\370\370\340\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\0\0\0\0\0\0\376\376\377\377\377\377\377\377\377\377"
"\377\377\377\377\37\37\37\343\343\343\343\343\343\377\377\377\376\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\30\30~~\370\370~~\30\30\0\0\0\0\0\0\0\377\377\377\377\377\77\77\77\77\77"
"\77\300\300\300\370\370\370\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\30\0f\0\200\0\0"
"\0\0\0\0\6\6\30\30\30\31\371\370\370\340\340\0\0\0\0\0\340\340\377\377\377\377\377\376\376\376\376\376"
"\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\371\346\346\6\6\6\6\6\0\340\340\340\341\0\0"
"\0\0\0\0\0\0\0\0\0\0\1\1\37\37\377\376\376\340\340\200\201\201\341\341\177\177\37\37\1\1\377\377"
"\377\377\1\1\1\1\377\377\377\377\1\1\37\37\177\177\341\341\201\201\200\200\370\370\376\376\37\37\1\1\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0\377\377"
"\377\377\0\0\0\0\377\377\377\377\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0"
"\0\0\0";
*/

View File

@@ -290,12 +290,8 @@ public:
}
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);
colorUpdated(CALL_MODE_BUTTON);
updateInterfaces(CALL_MODE_BUTTON);
}
void changeBrightness(bool increase) {

View File

@@ -19,17 +19,20 @@
// 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
// If FourLineDisplayUsermod is used the folowing options are also enabled
//
// * main color
// * saturation of main color
// * display network (long press buttion)
//
#ifdef USERMOD_MODE_SORT
#error "Usermod Mode Sort is no longer required. Remove -D USERMOD_MODE_SORT from platformio.ini"
#endif
#ifndef ENCODER_DT_PIN
#define ENCODER_DT_PIN 18
#endif
@@ -44,27 +47,90 @@
// 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
#define LAST_UI_STATE 8
#else
#define LAST_UI_STATE 4
#endif
// Number of modes at the start of the list to not sort
#define MODE_SORT_SKIP_COUNT 1
// Which list is being sorted
static char **listBeingSorted;
/**
* Modes and palettes are stored as strings that
* end in a quote character. Compare two of them.
* We are comparing directly within either
* JSON_mode_names or JSON_palette_names.
*/
static int re_qstringCmp(const void *ap, const void *bp) {
char *a = listBeingSorted[*((byte *)ap)];
char *b = listBeingSorted[*((byte *)bp)];
int i = 0;
do {
char aVal = pgm_read_byte_near(a + i);
if (aVal >= 97 && aVal <= 122) {
// Lowercase
aVal -= 32;
}
char bVal = pgm_read_byte_near(b + i);
if (bVal >= 97 && bVal <= 122) {
// Lowercase
bVal -= 32;
}
// Relly we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
if (aVal == bVal) {
// Same value, probably shouldn't happen
// with this dataset
return 0;
}
else if (aVal == '"' || aVal == '\0') {
return -1;
}
else {
return 1;
}
}
if (aVal == bVal) {
// Same characters. Move to the next.
i++;
continue;
}
// We're done
if (aVal < bVal) {
return -1;
}
else {
return 1;
}
} while (true);
// We shouldn't get here.
return 0;
}
class RotaryEncoderUIUsermod : public Usermod {
private:
int fadeAmount = 5; // Amount to change every step (brightness)
unsigned long currentTime;
int8_t fadeAmount = 5; // Amount to change every step (brightness)
unsigned long loopTime;
unsigned long buttonHoldTIme;
unsigned long buttonPressedTime = 0;
unsigned long buttonWaitTime = 0;
bool buttonPressedBefore = false;
bool buttonLongPressed = false;
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;
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed, ...
uint16_t currentHue1 = 16; // default boot color
byte currentSat1 = 255;
#ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod *display;
@@ -72,7 +138,16 @@ private:
void* display = nullptr;
#endif
// Pointers the start of the mode names within JSON_mode_names
char **modes_qstrings = nullptr;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
// Pointers the start of the palette names within JSON_palette_names
char **palettes_qstrings = nullptr;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
unsigned char Enc_A;
@@ -85,6 +160,14 @@ private:
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t currentCCT = 128;
bool isRgbw = false;
byte presetHigh = 0;
byte presetLow = 0;
bool applyToAll = true;
bool initDone = false;
bool enabled = true;
@@ -94,14 +177,94 @@ private:
static const char _DT_pin[];
static const char _CLK_pin[];
static const char _SW_pin[];
static const char _presetHigh[];
static const char _presetLow[];
static const char _applyToAll[];
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
int skipPaletteCount = 1;
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ;
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
}
byte *re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
return indexes;
}
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
char **re_findModeStrings(const char json[], int numModes) {
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
char singleJsonSymbol;
// Find the mode name in JSON
bool complete = false;
for (size_t i = 0; i < strlen_P(json); i++) {
singleJsonSymbol = pgm_read_byte_near(json + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
if (insideQuotes) {
// We have a new mode or palette
modeStrings[modeIndex] = (char *)(json + i + 1);
}
break;
case '[':
break;
case ']':
if (!insideQuotes) complete = true;
break;
case ',':
if (!insideQuotes) modeIndex++;
default:
if (!insideQuotes) break;
}
if (complete) break;
}
return modeStrings;
}
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr;
}
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.
*/
* 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()
{
DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
// BUG: configuring this usermod with conflicting pins
@@ -117,12 +280,17 @@ public:
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinC, INPUT_PULLUP);
currentTime = millis();
loopTime = currentTime;
loopTime = millis();
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
Bus *bus = busses.getBus(s);
if (!bus || bus->getLength()==0) break;
isRgbw |= bus->isRgbw();
}
currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5;
if (!initDone) sortModesAndPalettes();
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
@@ -140,91 +308,87 @@ public:
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
* 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.
*/
* 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 (!enabled || strip.isUpdating()) return;
unsigned long 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();}
findCurrentEffectAndPalette();
}
if(modes_alpha_indexes[effectCurrentIndex] != effectCurrent
|| palettes_alpha_indexes[effectPaletteIndex] != effectPalette){
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;
loopTime = currentTime; // Updates loopTime
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;
bool buttonPressed = !digitalRead(pinC); //0=pressed, 1=released
if (buttonPressed) {
if (!buttonPressedBefore) buttonPressedTime = currentTime;
buttonPressedBefore = true;
if (currentTime-buttonPressedTime > 3000) {
if (!buttonLongPressed) displayNetworkInfo(); //long press for network info
buttonLongPressed = true;
}
} else if (!buttonPressed && buttonPressedBefore) {
bool doublePress = buttonWaitTime;
buttonWaitTime = 0;
if (!buttonLongPressed) {
if (doublePress) {
toggleOnOff();
lampUdated();
} else {
buttonWaitTime = currentTime;
}
}
else
{
prev_button_state = button_state;
networkShown = false;
if(!prev_button_state)buttonHoldTIme = millis();
}
buttonLongPressed = false;
buttonPressedBefore = false;
}
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { //same speed as in button.cpp
buttonWaitTime = 0;
char newState = select_state + 1;
bool changedState = true;
if (newState > LAST_UI_STATE || (newState == 8 && presetHigh==0 && presetLow == 0)) newState = 0;
if (display != nullptr) {
switch (newState) {
case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun
case 1: changedState = changeState(PSTR("Speed"), 1, 4, 2); break; //2 = skip forward
case 2: changedState = changeState(PSTR("Intensity"), 1, 8, 3); break; //3 = fire
case 3: changedState = changeState(PSTR("Color Palette"), 2, 0, 4); break; //4 = custom palette
case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece
case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush
case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast
case 7: changedState = changeState(PSTR("CCT"), 255, 255, 10); break; //10 = star
case 8: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart
}
}
if (changedState) select_state = newState;
}
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);
@@ -233,65 +397,39 @@ public:
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;
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;
case 7: changeCCT(true); break;
case 8: changePreset(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;
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;
case 7: changeCCT(false); break;
case 8: changePreset(false); break;
}
}
}
Enc_A_prev = Enc_A; // Store value of A for next time
loopTime = currentTime; // Updates loopTime
}
}
void displayNetworkInfo(){
void displayNetworkInfo() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->networkOverlay(" NETWORK INFO", 15000);
networkShown = true;
display->networkOverlay(PSTR("NETWORK INFO"), 10000);
#endif
}
@@ -313,180 +451,292 @@ public:
}
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;
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) {
if (display->wakeDisplay()) {
// Throw away wake up input
display->redraw(true);
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);
//setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required)
stateUpdated(CALL_MODE_BUTTON);
updateInterfaces(CALL_MODE_BUTTON);
}
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
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 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
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0);
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
strip.setMode(i, effectCurrent);
}
} else {
//WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
strip.setMode(strip.getMainSegmentId(), effectCurrent);
}
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
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.speed = effectSpeed;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.speed = effectSpeed;
}
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
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.intensity = effectIntensity;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.intensity = effectIntensity;
}
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
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.palette = effectPalette;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.palette = effectPalette;
}
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
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
}
void changeSat(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
}
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
void changePreset(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
if (presetHigh && presetLow && presetHigh > presetLow) {
String apireq = F("win&PL=~");
if (!increase) apireq += '-';
apireq += F("&P1=");
apireq += presetLow;
apireq += F("&P2=");
apireq += presetHigh;
handleSet(nullptr, apireq, false);
lampUdated();
}
}
void changeCCT(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
// if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.setCCT(currentCCT, i);
}
// } else {
// WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
// seg.setCCT(currentCCT, strip.getMainSegmentId());
// }
lampUdated();
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
* 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
}
*/
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
*/
* 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
*/
* 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
@@ -498,6 +748,9 @@ public:
top[FPSTR(_DT_pin)] = pinA;
top[FPSTR(_CLK_pin)] = pinB;
top[FPSTR(_SW_pin)] = pinC;
top[FPSTR(_presetLow)] = presetLow;
top[FPSTR(_presetHigh)] = presetHigh;
top[FPSTR(_applyToAll)] = applyToAll;
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
}
@@ -514,14 +767,17 @@ public:
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
int8_t newDTpin = pinA;
int8_t newCLKpin = pinB;
int8_t newSWpin = pinC;
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
enabled = top[FPSTR(_enabled)] | enabled;
newDTpin = top[FPSTR(_DT_pin)] | newDTpin;
newCLKpin = top[FPSTR(_CLK_pin)] | newCLKpin;
newSWpin = top[FPSTR(_SW_pin)] | newSWpin;
presetHigh = top[FPSTR(_presetHigh)] | presetHigh;
presetLow = top[FPSTR(_presetLow)] | presetLow;
presetHigh = MIN(250,MAX(0,presetHigh));
presetLow = MIN(250,MAX(0,presetLow));
enabled = top[FPSTR(_enabled)] | enabled;
applyToAll = top[FPSTR(_applyToAll)] | applyToAll;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
@@ -548,7 +804,7 @@ public:
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_enabled)].isNull();
return !top[FPSTR(_applyToAll)].isNull();
}
/*
@@ -562,8 +818,11 @@ public:
};
// 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";
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";
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";
const char RotaryEncoderUIUsermod::_applyToAll[] PROGMEM = "apply-2-all-seg";

View File

@@ -25,6 +25,7 @@
*/
#include "FX.h"
#include "wled.h"
#define IBN 5100
#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
@@ -936,7 +937,7 @@ uint16_t WS2812FX::mode_chase_flash_random(void) {
} else {
SEGENV.step = (SEGENV.step + 1) % SEGLEN;
if(SEGENV.step == 0) {
if (SEGENV.step == 0) {
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
}
}
@@ -965,8 +966,7 @@ uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
setPixelColor(i,col);
}
if (it != SEGENV.step )
{
if (it != SEGENV.step) {
SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
SEGENV.step = it;
}
@@ -996,26 +996,34 @@ uint16_t WS2812FX::mode_halloween(void) {
/*
* Random colored pixels running.
* Random colored pixels running. ("Stream")
*/
uint16_t WS2812FX::mode_running_random(void) {
uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed));
uint32_t it = now / cycleTime;
if (SEGENV.aux1 == it) return FRAMETIME;
if (SEGENV.call == 0) SEGENV.aux0 = random16(); // random seed for PRNG on start
for(uint16_t i=SEGLEN-1; i > 0; i--) {
setPixelColor( i, getPixelColor( i - 1));
}
uint8_t zoneSize = ((255-SEGMENT.intensity) >> 4) +1;
uint16_t PRNG16 = SEGENV.aux0;
if(SEGENV.step == 0) {
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
setPixelColor(0, color_wheel(SEGENV.aux0));
}
SEGENV.step++;
if (SEGENV.step > (uint8_t)((255-SEGMENT.intensity) >> 4))
{
SEGENV.step = 0;
uint8_t z = it % zoneSize;
bool nzone = (!z && it != SEGENV.aux1);
for (uint16_t i=SEGLEN-1; i > 0; i--) {
if (nzone || z >= zoneSize) {
uint8_t lastrand = PRNG16 >> 8;
int16_t diff = 0;
while (abs(diff) < 42) { // make sure the difference between adjacent colors is big enough
PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next zone, next 'random' number
diff = (PRNG16 >> 8) - lastrand;
}
if (nzone) {
SEGENV.aux0 = PRNG16; // save next starting seed
nzone = false;
}
z = 0;
}
setPixelColor(i, color_wheel(PRNG16 >> 8));
z++;
}
SEGENV.aux1 = it;
@@ -1589,26 +1597,36 @@ uint16_t WS2812FX::mode_dual_larson_scanner(void){
/*
* Running random pixels
* Running random pixels ("Stream 2")
* Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h
*/
uint16_t WS2812FX::mode_random_chase(void)
{
if (SEGENV.call == 0) {
SEGENV.step = RGBW32(random8(), random8(), random8(), 0);
SEGENV.aux0 = random16();
}
uint16_t prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function
uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed));
uint32_t it = now / cycleTime;
if (SEGENV.step == it) return FRAMETIME;
uint32_t color = SEGENV.step;
random16_set_seed(SEGENV.aux0);
for(uint16_t i = SEGLEN -1; i > 0; i--) {
setPixelColor(i, getPixelColor(i-1));
uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8();
uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8();
uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8();
color = RGBW32(r, g, b, 0);
setPixelColor(i, r, g, b);
if (i == SEGLEN -1 && SEGENV.aux1 != (it & 0xFFFF)) { //new first color in next frame
SEGENV.step = color;
SEGENV.aux0 = random16_get_seed();
}
}
uint32_t color = getPixelColor(0);
if (SEGLEN > 1) color = getPixelColor( 1);
uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8();
uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8();
uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8();
setPixelColor(0, r, g, b);
SEGENV.step = it;
SEGENV.aux1 = it & 0xFFFF;
random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG
return FRAMETIME;
}
@@ -2091,7 +2109,7 @@ uint16_t WS2812FX::mode_colortwinkle()
}
}
}
return FRAMETIME;
return FRAMETIME_FIXED;
}
@@ -2876,7 +2894,7 @@ uint16_t WS2812FX::candle(bool multi)
}
}
return FRAMETIME;
return FRAMETIME_FIXED;
}
uint16_t WS2812FX::mode_candle()
@@ -3904,18 +3922,24 @@ 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; // max segment length of 56 pixels on 16 segment ESP8266
uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN;
uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // 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);
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
for (int i = 0; i < SEGLEN; i++) {
for (int i = 0; i < pixelLen; i++) {
pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed);
setPixelColor(i, pixels[i]);
shift += 3;
}
uint16_t offset = 0;
for (int i = 0; i < SEGLEN; i++) {
setPixelColor(i, pixels[offset++]);
if (offset > pixelLen) offset = 0;
}
return FRAMETIME;
}

View File

@@ -48,7 +48,8 @@
/* Not used in all effects yet */
#define WLED_FPS 42
#define FRAMETIME (1000/WLED_FPS)
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME _frametime
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
@@ -71,7 +72,7 @@
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
#define LED_SKIP_AMOUNT 1
#define MIN_SHOW_DELAY 15
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT _segments[_segment_index]
@@ -80,7 +81,6 @@
#define SEGLEN _virtualSegmentLength
#define SEGACT SEGMENT.stop
#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN
#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes))
// some common colors
#define RED (uint32_t)0xFF0000
@@ -334,27 +334,8 @@ 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;
}
uint8_t differs(Segment& b);
uint8_t getLightCapabilities();
} segment;
// segment runtime parameters
@@ -408,8 +389,9 @@ class WS2812FX {
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests.
*/
inline void reset() { _requiresReset = true; }
inline void markForReset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
@@ -640,6 +622,7 @@ class WS2812FX {
setMode(uint8_t segid, uint8_t m),
setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
setColor(uint8_t slot, uint32_t c),
setCCT(uint16_t k),
setBrightness(uint8_t b),
setRange(uint16_t i, uint16_t i2, uint32_t col),
setShowCallback(show_callback cb),
@@ -648,42 +631,41 @@ class WS2812FX {
calcGammaTable(float),
trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX),
setMainSegmentId(uint8_t n),
restartRuntime(),
resetSegments(),
makeAutoSegments(),
makeAutoSegments(bool forceReset = false),
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),
setPixelSegment(uint8_t n),
setTargetFps(uint8_t fps),
deserializeMap(uint8_t n=0);
bool
isRgbw = false,
isOffRefreshRequred = false, //periodic refresh is required for the strip to remain off.
gammaCorrectBri = false,
gammaCorrectCol = true,
applyToAllSelected = true,
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p),
checkSegmentAlignment(void),
hasCCTBus(void),
hasRGBWBus(void),
hasCCTBus(void),
// return true if the strip is being sent pixel updates
isUpdating(void);
uint8_t
mainSegment = 0,
paletteFade = 0,
paletteBlend = 0,
milliampsPerLed = 55,
cctBlending = 0,
getBrightness(void),
getMode(void),
getSpeed(void),
getModeCount(void),
getPaletteCount(void),
getMaxSegments(void),
getActiveSegmentsNum(void),
//getFirstSelectedSegment(void),
getFirstSelectedSegId(void),
getMainSegmentId(void),
getLastActiveSegmentId(void),
getTargetFps(void),
setPixelSegment(uint8_t n),
gamma8(uint8_t),
gamma8_cal(uint8_t, float),
sin_gap(uint16_t),
@@ -709,14 +691,12 @@ class WS2812FX {
currentColor(uint32_t colorNew, uint8_t tNr),
gamma32(uint32_t),
getLastShow(void),
getPixelColor(uint16_t),
getColor(void);
getPixelColor(uint16_t);
WS2812FX::Segment&
getSegment(uint8_t n);
WS2812FX::Segment_runtime
getSegmentRuntime(void);
WS2812FX::Segment
&getSegment(uint8_t n),
&getFirstSelectedSeg(void),
&getMainSegment(void);
WS2812FX::Segment*
getSegments(void);
@@ -855,9 +835,13 @@ class WS2812FX {
uint16_t _usedSegmentData = 0;
uint16_t _transitionDur = 750;
uint8_t _targetFps = 42;
uint16_t _frametime = (1000/42);
uint16_t _cumulativeFps = 2;
bool
_isOffRefreshRequired = false, //periodic refresh is required for the strip to remain off.
_hasWhiteChannel = false,
_triggered;
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
@@ -906,6 +890,8 @@ class WS2812FX {
uint8_t _segment_index = 0;
uint8_t _segment_index_palette_last = 99;
uint8_t _mainSegment;
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[]
{0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}}
@@ -919,6 +905,10 @@ class WS2812FX {
uint16_t
realPixelIndex(uint16_t i),
transitionProgress(uint8_t tNr);
public:
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;}
};
//10 names per line

View File

@@ -67,8 +67,13 @@
//do not call this method from system context (network callback)
void WS2812FX::finalizeInit(void)
{
RESET_RUNTIME;
isRgbw = isOffRefreshRequred = false;
//reset segment runtimes
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
_segment_runtimes[i].markForReset();
_segment_runtimes[i].resetIfRequired();
}
_hasWhiteChannel = _isOffRefreshRequired = false;
//if busses failed to load, add default (fresh install, FS issue, ...)
if (busses.getNumBusses() == 0) {
@@ -93,9 +98,9 @@ void WS2812FX::finalizeInit(void)
if (bus == nullptr) continue;
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
//RGBW mode is enabled if at least one of the strips is RGBW
isRgbw |= bus->isRgbw();
_hasWhiteChannel |= bus->isRgbw();
//refresh is required to remain off if at least one of the strips requires the refresh.
isOffRefreshRequred |= bus->isOffRefreshRequired();
_isOffRefreshRequired |= bus->isOffRefreshRequired();
uint16_t busEnd = bus->getStart() + bus->getLength();
if (busEnd > _length) _length = busEnd;
#ifdef ESP8266
@@ -165,12 +170,12 @@ void WS2812FX::service() {
_triggered = false;
}
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
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
uint16_t WS2812FX::realPixelIndex(uint16_t i) {
uint16_t IRAM_ATTR WS2812FX::realPixelIndex(uint16_t i) {
int16_t iGroup = i * SEGMENT.groupLength();
/* reverse just an individual segment */
@@ -187,7 +192,7 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
return realIndex;
}
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
{
if (SEGLEN) {//from segment
uint16_t realIndex = realPixelIndex(i);
@@ -350,6 +355,15 @@ uint16_t WS2812FX::getFps() {
return _cumulativeFps +1;
}
uint8_t WS2812FX::getTargetFps() {
return _targetFps;
}
void WS2812FX::setTargetFps(uint8_t fps) {
if (fps > 0 && fps <= 120) _targetFps = fps;
_frametime = 1000 / _targetFps;
}
/**
* Forces the next frame to be computed on all active segments.
*/
@@ -364,7 +378,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (_segments[segid].mode != m)
{
_segment_runtimes[segid].reset();
_segment_runtimes[segid].markForReset();
_segments[segid].mode = m;
}
}
@@ -379,62 +393,28 @@ uint8_t WS2812FX::getPaletteCount()
return 13 + GRADIENT_PALETTE_COUNT;
}
//TODO effect transitions
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
Segment& seg = _segments[getMainSegmentId()];
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
bool applied = false;
if (applyToAllSelected) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isSelected())
{
_segments[i].speed = s;
_segments[i].intensity = in;
_segments[i].palette = p;
setMode(i, m);
applied = true;
}
}
}
if (!applyToAllSelected || !applied) {
seg.speed = s;
seg.intensity = in;
seg.palette = p;
setMode(mainSegment, m);
}
if (seg.mode != modePrev || seg.speed != speedPrev || seg.intensity != intensityPrev || seg.palette != palettePrev) return true;
return false;
}
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
setColor(slot, RGBW32(r, g, b, w));
}
//applies to all active and selected segments
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (slot >= NUM_COLORS) return;
bool applied = false;
if (applyToAllSelected) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isSelected()) {
_segments[i].setColor(slot, c, i);
applied = true;
}
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isActive() && _segments[i].isSelected()) {
_segments[i].setColor(slot, c, i);
}
}
}
if (!applyToAllSelected || !applied) {
uint8_t mainseg = getMainSegmentId();
_segments[mainseg].setColor(slot, c, mainseg);
void WS2812FX::setCCT(uint16_t k) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isActive() && _segments[i].isSelected()) {
_segments[i].setCCT(k, i);
}
}
}
@@ -452,14 +432,6 @@ void WS2812FX::setBrightness(uint8_t b) {
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) {
return _segments[getMainSegmentId()].mode;
}
uint8_t WS2812FX::getSpeed(void) {
return _segments[getMainSegmentId()].speed;
}
uint8_t WS2812FX::getBrightness(void) {
return _brightness;
}
@@ -468,24 +440,38 @@ uint8_t WS2812FX::getMaxSegments(void) {
return MAX_NUM_SEGMENTS;
}
/*uint8_t WS2812FX::getFirstSelectedSegment(void)
uint8_t WS2812FX::getFirstSelectedSegId(void)
{
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isActive() && _segments[i].isSelected()) return i;
}
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) //if none selected, get first active
{
if (_segments[i].isActive()) return i;
// if none selected, use the main segment
return getMainSegmentId();
}
void WS2812FX::setMainSegmentId(uint8_t n) {
if (n >= MAX_NUM_SEGMENTS) return;
//use supplied n if active, or first active
if (_segments[n].isActive()) {
_mainSegment = n; return;
}
return 0;
}*/
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isActive()) {
_mainSegment = i; return;
}
}
_mainSegment = 0;
return;
}
uint8_t WS2812FX::getMainSegmentId(void) {
if (mainSegment >= MAX_NUM_SEGMENTS) return 0;
if (_segments[mainSegment].isActive()) return mainSegment;
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) //get first active
{
return _mainSegment;
}
uint8_t WS2812FX::getLastActiveSegmentId(void) {
for (uint8_t i = MAX_NUM_SEGMENTS -1; i > 0; i--) {
if (_segments[i].isActive()) return i;
}
return 0;
@@ -500,10 +486,6 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
return c;
}
uint32_t WS2812FX::getColor(void) {
return _segments[getMainSegmentId()].colors[0];
}
uint32_t WS2812FX::getPixelColor(uint16_t i)
{
i = realPixelIndex(i);
@@ -525,8 +507,12 @@ WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) {
return _segments[id];
}
WS2812FX::Segment_runtime WS2812FX::getSegmentRuntime(void) {
return SEGENV;
WS2812FX::Segment& WS2812FX::getFirstSelectedSeg(void) {
return _segments[getFirstSelectedSegId()];
}
WS2812FX::Segment& WS2812FX::getMainSegment(void) {
return _segments[getMainSegmentId()];
}
WS2812FX::Segment* WS2812FX::getSegments(void) {
@@ -551,6 +537,77 @@ uint16_t WS2812FX::getLengthPhysical(void) {
return len;
}
uint8_t WS2812FX::Segment::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 & 0b00101110) != (b.options & 0b00101110)) d |= SEG_DIFFERS_OPT;
if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++)
{
if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
}
return d;
}
uint8_t WS2812FX::Segment::getLightCapabilities() {
if (!isActive()) return 0;
uint8_t capabilities = 0;
uint8_t awm = Bus::getAutoWhiteMode();
bool whiteSlider = (awm == RGBW_MODE_DUAL || awm == RGBW_MODE_MANUAL_ONLY);
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (bus->getStart() >= stop) continue;
if (bus->getStart() + bus->getLength() <= start) continue;
uint8_t type = bus->getType();
if (!whiteSlider || (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH)))
{
capabilities |= 0x01; //segment supports RGB (full color)
}
if (bus->isRgbw() && whiteSlider) capabilities |= 0x02; //segment supports white channel
if (!cctFromRgb) {
switch (type) {
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
capabilities |= 0x04; //segment supports white CCT
}
}
if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider)
}
return capabilities;
}
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
//returns if there is an RGBW bus (supports RGB and White, not only white)
//not influenced by auto-white mode, also true if white slider does not affect output white channel
bool WS2812FX::hasRGBWBus(void) {
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_SK6812_RGBW:
case TYPE_TM1814:
case TYPE_ANALOG_4CH:
return true;
}
}
return false;
}
bool WS2812FX::hasCCTBus(void) {
if (cctFromRgb && !correctWB) return false;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
@@ -582,17 +639,8 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
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++)
{
if (_segments[i].isActive()) {
mainSegment = i;
return;
}
}
mainSegment = 0; //should not happen (always at least one active segment)
}
// if main segment is deleted, set first active as main segment
if (n == _mainSegment) setMainSegmentId(0);
return;
}
if (i1 < _length) seg.start = i1;
@@ -603,12 +651,18 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
seg.spacing = spacing;
}
if (offset < UINT16_MAX) seg.offset = offset;
_segment_runtimes[n].reset();
_segment_runtimes[n].markForReset();
}
void WS2812FX::restartRuntime() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
_segment_runtimes[i].markForReset();
}
}
void WS2812FX::resetSegments() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
mainSegment = 0;
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));
_segment_index = 0;
@@ -633,12 +687,12 @@ void WS2812FX::resetSegments() {
_segments[i].cct = 127;
_segments[i].speed = DEFAULT_SPEED;
_segments[i].intensity = DEFAULT_INTENSITY;
_segment_runtimes[i].reset();
_segment_runtimes[i].markForReset();
}
_segment_runtimes[0].reset();
_segment_runtimes[0].markForReset();
}
void WS2812FX::makeAutoSegments() {
void WS2812FX::makeAutoSegments(bool forceReset) {
if (autoSegments) { //make one segment per bus
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
@@ -664,8 +718,14 @@ void WS2812FX::makeAutoSegments() {
setSegment(i, segStarts[i], segStops[i]);
}
} else {
//expand the main seg to the entire length, but only if there are no other segments
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced
uint8_t mainSeg = getMainSegmentId();
if (forceReset) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
setSegment(i, 0, 0);
}
}
if (getActiveSegmentsNum() < 2) {
setSegment(mainSeg, 0, _length);
@@ -701,36 +761,16 @@ bool WS2812FX::checkSegmentAlignment() {
}
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
//Note: If called in an interrupt (e.g. JSON API), it must be reset with "setPixelColor(255)",
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
#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)
uint8_t WS2812FX::setPixelSegment(uint8_t n)
{
uint8_t prevSegId = _segment_index;
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.virtualLength();
} else {
_virtualSegmentLength = 0;
#ifdef ARDUINO_ARCH_ESP32
if (_ps_set) {
_segment_index = _segment_index_prev;
_virtualSegmentLength = _virtualSegmentLength_prev;
_ps_set = false;
}
#endif
}
return prevSegId;
}
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
@@ -769,7 +809,7 @@ void WS2812FX::setTransitionMode(bool t)
/*
* color blend function
*/
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
@@ -872,13 +912,13 @@ void WS2812FX::blur(uint8_t blur_amount)
}
}
uint16_t WS2812FX::triwave16(uint16_t in)
uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in)
{
if (in < 0x8000) return in *2;
return 0xFFFF - (in - 0x8000)*2;
}
uint8_t WS2812FX::sin_gap(uint16_t in) {
uint8_t IRAM_ATTR WS2812FX::sin_gap(uint16_t in) {
if (in & 0x100) return 0;
//if (in > 255) return 0;
return sin8(in + 192); //correct phase shift of sine so that it starts and stops at 0
@@ -945,13 +985,13 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
}
uint32_t WS2812FX::crgb_to_col(CRGB fastled)
uint32_t IRAM_ATTR WS2812FX::crgb_to_col(CRGB fastled)
{
return RGBW32(fastled.red, fastled.green, fastled.blue, 0);
}
CRGB WS2812FX::col_to_crgb(uint32_t color)
CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color)
{
CRGB fastled_col;
fastled_col.red = R(color);
@@ -1074,7 +1114,7 @@ void WS2812FX::handle_palette(void)
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
* @returns Single color from palette
*/
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
{
if (SEGMENT.palette == 0 && mcol < 3) {
uint32_t color = SEGCOLOR(mcol);

View File

@@ -44,7 +44,7 @@ void onAlexaChange(EspalexaDevice* dev)
if (bri == 0)
{
bri = briLast;
colorUpdated(CALL_MODE_ALEXA);
stateUpdated(CALL_MODE_ALEXA);
}
} else {
applyPreset(macroAlexaOn, CALL_MODE_ALEXA);
@@ -58,7 +58,7 @@ void onAlexaChange(EspalexaDevice* dev)
{
briLast = bri;
bri = 0;
colorUpdated(CALL_MODE_ALEXA);
stateUpdated(CALL_MODE_ALEXA);
}
} else {
applyPreset(macroAlexaOff, CALL_MODE_ALEXA);
@@ -67,43 +67,37 @@ void onAlexaChange(EspalexaDevice* dev)
} else if (m == EspalexaDeviceProperty::bri)
{
bri = espalexaDevice->getValue();
colorUpdated(CALL_MODE_ALEXA);
stateUpdated(CALL_MODE_ALEXA);
} else //color
{
if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white
{
byte rgbw[4];
uint16_t ct = espalexaDevice->getCt();
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) {
strip.setCCT(k);
rgbw[0]= 0; rgbw[1]= 0; rgbw[2]= 0; rgbw[3]= 255;
} else if (strip.hasWhiteChannel()) {
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);
case 199: rgbw[0]=255; rgbw[1]=255; rgbw[2]=255; rgbw[3]=255; break;
case 234: rgbw[0]=127; rgbw[1]=127; rgbw[2]=127; rgbw[3]=255; break;
case 284: rgbw[0]= 0; rgbw[1]= 0; rgbw[2]= 0; rgbw[3]=255; break;
case 350: rgbw[0]=130; rgbw[1]= 90; rgbw[2]= 0; rgbw[3]=255; break;
case 383: rgbw[0]=255; rgbw[1]=153; rgbw[2]= 0; rgbw[3]=255; break;
default : colorKtoRGB(k, rgbw);
}
} else {
colorKtoRGB(k, col);
colorKtoRGB(k, rgbw);
}
strip.setColor(0, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
} else {
uint32_t color = espalexaDevice->getRGB();
col[0] = ((color >> 16) & 0xFF);
col[1] = ((color >> 8) & 0xFF);
col[2] = ( color & 0xFF);
col[3] = 0;
strip.setColor(0, color);
}
colorUpdated(CALL_MODE_ALEXA);
stateUpdated(CALL_MODE_ALEXA);
}
}

View File

@@ -44,27 +44,27 @@ void updateBlynk()
BLYNK_WRITE(V0)
{
bri = param.asInt();//bri
colorUpdated(CALL_MODE_BLYNK);
stateUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V1)
{
blHue = param.asInt();//hue
colorHStoRGB(blHue*10,blSat,(false)? colSec:col);
colorHStoRGB(blHue*10,blSat,col);
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V2)
{
blSat = param.asInt();//sat
colorHStoRGB(blHue*10,blSat,(false)? colSec:col);
colorHStoRGB(blHue*10,blSat,col);
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V3)
{
bool on = (param.asInt()>0);
if (!on != !bri) {toggleOnOff(); colorUpdated(CALL_MODE_BLYNK);}
if (!on != !bri) {toggleOnOff(); stateUpdated(CALL_MODE_BLYNK);}
}
BLYNK_WRITE(V4)

View File

@@ -73,6 +73,62 @@ struct BusConfig {
}
};
// Defines an LED Strip and its color ordering.
struct ColorOrderMapEntry {
uint16_t start;
uint16_t len;
uint8_t colorOrder;
};
struct ColorOrderMap {
void add(uint16_t start, uint16_t len, uint8_t colorOrder) {
if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
return;
}
if (len == 0) {
return;
}
if (colorOrder > COL_ORDER_MAX) {
return;
}
_mappings[_count].start = start;
_mappings[_count].len = len;
_mappings[_count].colorOrder = colorOrder;
_count++;
}
uint8_t count() const {
return _count;
}
void reset() {
_count = 0;
memset(_mappings, 0, sizeof(_mappings));
}
const ColorOrderMapEntry* get(uint8_t n) const {
if (n > _count) {
return nullptr;
}
return &(_mappings[n]);
}
inline uint8_t IRAM_ATTR getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
if (_count == 0) return defaultColorOrder;
for (uint8_t i = 0; i < _count; i++) {
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
return _mappings[i].colorOrder;
}
}
return defaultColorOrder;
}
private:
uint8_t _count;
ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
};
//parent class of BusDigital, BusPwm, and BusNetwork
class Bus {
public:
@@ -91,7 +147,7 @@ class Bus {
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 uint16_t getLength() { return _len; }
virtual void setColorOrder() {}
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; }
@@ -152,7 +208,7 @@ class Bus {
class BusDigital : public Bus {
public:
BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) {
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start), _colorOrderMap(com) {
if (!IS_DIGITAL(bc.type) || !bc.count) return;
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_pins[0] = bc.pins[0];
@@ -197,7 +253,7 @@ class BusDigital : public Bus {
//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::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
PolyBus::show(_busPtr, _iType);
}
}
@@ -207,20 +263,20 @@ class BusDigital : public Bus {
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);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
}
uint32_t getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1;
else pix += _skip;
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrder);
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
}
inline uint8_t getColorOrder() {
return _colorOrder;
}
inline uint16_t getLength() {
uint16_t getLength() {
return _len - _skip;
}
@@ -263,6 +319,7 @@ class BusDigital : public Bus {
uint8_t _iType = I_NONE;
uint8_t _skip = 0;
void * _busPtr = nullptr;
const ColorOrderMap &_colorOrderMap;
};
@@ -288,7 +345,7 @@ class BusPwm : public Bus {
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return;
}
_pins[i] = currentPin; // store only after allocatePin() succeeds
_pins[i] = currentPin; //store only after allocatePin() succeeds
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
@@ -397,7 +454,7 @@ class BusPwm : public Bus {
private:
uint8_t _pins[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {0};
#ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255;
#endif
@@ -555,7 +612,7 @@ class BusManager {
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);
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
} else {
busses[numBusses] = new BusPwm(bc);
}
@@ -583,7 +640,7 @@ class BusManager {
}
}
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
void IRAM_ATTR setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
@@ -640,8 +697,17 @@ class BusManager {
return len;
}
void updateColorOrderMap(const ColorOrderMap &com) {
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
}
const ColorOrderMap& getColorOrderMap() const {
return colorOrderMap;
}
private:
uint8_t numBusses = 0;
Bus* busses[WLED_MAX_BUSSES];
ColorOrderMap colorOrderMap;
};
#endif

View File

@@ -98,26 +98,34 @@
#ifdef ARDUINO_ARCH_ESP32
//RGB
#define B_32_RN_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod>
#ifndef CONFIG_IDF_TARGET_ESP32C3
#define B_32_I0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod>
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
#define B_32_I1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod>
#endif
//RGBW
#define B_32_RN_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod>
#ifndef CONFIG_IDF_TARGET_ESP32C3
#define B_32_I0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod>
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
#define B_32_I1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod>
#endif
//400Kbps
#define B_32_RN_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod>
#ifndef CONFIG_IDF_TARGET_ESP32C3
#define B_32_I0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod>
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
#define B_32_I1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod>
#endif
//TM1814 (RGBW)
#define B_32_RN_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method>
#ifndef CONFIG_IDF_TARGET_ESP32C3
#define B_32_I0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method>
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
#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)
@@ -181,23 +189,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break;
#endif
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break;
#endif
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
#endif
case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
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()
@@ -236,23 +252,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
#endif
case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
#endif
case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break;
#endif
case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
#endif
#endif
@@ -292,23 +316,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(); break;
#endif
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(); break;
#endif
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break;
#endif
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
#endif
#endif
@@ -345,23 +377,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
#endif
case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
#endif
case I_32_RN_400_3: return (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
#endif
case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
#endif
#endif
@@ -422,23 +462,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
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
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#endif
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); 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;
#ifndef CONFIG_IDF_TARGET_ESP32C3
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
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#endif
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
#endif
#endif
@@ -475,23 +523,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetBrightness(b); break;
#endif
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetBrightness(b); break;
#endif
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetBrightness(b); break;
#endif
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
#endif
#endif
@@ -529,23 +585,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: col = (static_cast<B_32_I0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: col = (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_32_RN_NEO_4: col = (static_cast<B_32_RN_NEO_4*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: col = (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_32_RN_400_3: col = (static_cast<B_32_RN_400_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
#endif
#endif
@@ -600,23 +664,31 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_3: delete (static_cast<B_32_I0_NEO_3*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_3: delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break;
#endif
case I_32_RN_NEO_4: delete (static_cast<B_32_RN_NEO_4*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_NEO_4: delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break;
#endif
case I_32_RN_400_3: delete (static_cast<B_32_RN_400_3*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
#endif
case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32S2
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
#endif
#endif
@@ -692,4 +764,4 @@ class PolyBus {
}
};
#endif
#endif

View File

@@ -16,11 +16,11 @@ void shortPressAction(uint8_t b)
{
if (!macroButton[b]) {
switch (b) {
case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
case 0: toggleOnOff(); stateUpdated(CALL_MODE_BUTTON); break;
case 1: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
applyPreset(macroButton[b], CALL_MODE_BUTTON);
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
}
// publish MQTT message
@@ -35,11 +35,11 @@ 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
case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break;
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
}
} else {
applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
}
// publish MQTT message
@@ -55,10 +55,10 @@ 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;
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
}
// publish MQTT message
@@ -72,21 +72,23 @@ void doublePressAction(uint8_t b)
bool isButtonPressed(uint8_t i)
{
if (btnPin[i]<0) return false;
uint8_t pin = btnPin[i];
switch (buttonType[i]) {
case BTN_TYPE_NONE:
case BTN_TYPE_RESERVED:
break;
case BTN_TYPE_PUSH:
case BTN_TYPE_SWITCH:
if (digitalRead(btnPin[i]) == LOW) return true;
if (digitalRead(pin) == LOW) return true;
break;
case BTN_TYPE_PUSH_ACT_HIGH:
case BTN_TYPE_PIR_SENSOR:
if (digitalRead(btnPin[i]) == HIGH) return true;
if (digitalRead(pin) == HIGH) return true;
break;
case BTN_TYPE_TOUCH:
#ifdef ARDUINO_ARCH_ESP32
if (touchRead(btnPin[i]) <= touchThreshold) return true;
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if (touchRead(pin) <= touchThreshold) return true;
#endif
break;
}
@@ -105,14 +107,14 @@ void handleSwitch(uint8_t b)
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (!buttonPressedBefore[b]) { // on -> off
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON);
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
else { //turn on
if (!bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
}
} else { // off -> on
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
else { //turn off
if (bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
}
}
@@ -159,36 +161,17 @@ void handleAnalog(uint8_t b)
} else if (macroDoublePress[b] == 249) {
// effect speed
effectSpeed = aRead;
effectChanged = true;
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
seg.speed = effectSpeed;
}
} else if (macroDoublePress[b] == 248) {
// effect intensity
effectIntensity = aRead;
effectChanged = true;
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
seg.intensity = effectIntensity;
}
} else if (macroDoublePress[b] == 247) {
// selected palette
effectPalette = map(aRead, 0, 252, 0, strip.getPaletteCount()-1);
effectChanged = true;
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
seg.palette = effectPalette;
}
} else if (macroDoublePress[b] == 200) {
// primary color, hue, full saturation
colorHStoRGB(aRead*256,255,col);
} else {
// otherwise use "double press" for segment selection
//uint8_t mainSeg = strip.getMainSegmentId();
WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]);
if (aRead == 0) {
seg.setOption(SEG_OPTION_ON, 0); // off
@@ -210,6 +193,7 @@ void handleAnalog(uint8_t b)
void handleButton()
{
static unsigned long lastRead = 0UL;
bool analog = false;
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
#ifdef ESP8266
@@ -221,7 +205,7 @@ void handleButton()
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();
analog = true;
handleAnalog(b); continue;
}
@@ -255,7 +239,7 @@ void handleButton()
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 (b == 0 && !macroDoublePress[b]) { //don't wait for double press on button 0 if no double press macro set
if (b != 1 && !macroDoublePress[b]) { //don't wait for double press on buttons without a default action 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) {
@@ -275,6 +259,7 @@ void handleButton()
shortPressAction(b);
}
}
if (analog) lastRead = millis();
}
void handleIO()
@@ -300,7 +285,7 @@ void handleIO()
// turn off built-in LED if strip is turned off
// this will break digital bus so will need to be reinitialised on On
PinOwner ledPinOwner = pinManager.getPinOwner(LED_BUILTIN);
if (!strip.isOffRefreshRequred && (ledPinOwner == PinOwner::None || ledPinOwner == PinOwner::BusDigital)) {
if (!strip.isOffRefreshRequired() && (ledPinOwner == PinOwner::None || ledPinOwner == PinOwner::BusDigital)) {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
}
@@ -312,4 +297,4 @@ void handleIO()
}
offMode = true;
}
}
}

View File

@@ -76,7 +76,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject hw = doc[F("hw")];
// initialize LED pins and lengths prior to other HW (except for ethernet)
JsonObject hw_led = hw[F("led")];
JsonObject hw_led = hw["led"];
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
@@ -85,6 +85,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
JsonArray ins = hw_led["ins"];
@@ -104,7 +105,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (i>4) break;
}
uint16_t length = elm[F("len")] | 1;
uint16_t length = elm["len"] | 1;
uint8_t colorOrder = (int)elm[F("order")];
uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm["start"] | 0;
@@ -122,6 +123,22 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
}
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
// read color order map configuration
JsonArray hw_com = hw[F("com")];
if (!hw_com.isNull()) {
ColorOrderMap com = {};
uint8_t s = 0;
for (JsonObject entry : hw_com) {
if (s > WLED_MAX_COLOR_ORDER_MAPPINGS) break;
uint16_t start = entry["start"] | 0;
uint16_t len = entry["len"] | 0;
uint8_t colorOrder = (int)entry[F("order")];
com.add(start, len, colorOrder);
s++;
}
busses.updateColorOrderMap(com);
}
// read multiple button configuration
JsonObject btn_obj = hw["btn"];
JsonArray hw_btn_ins = btn_obj[F("ins")];
@@ -194,6 +211,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
rlyMde = !relay["rev"];
}
CJSON(serialBaud, hw[F("baud")]);
if (serialBaud < 96 || serialBaud > 15000) serialBaud = 1152;
updateBaudRate(serialBaud *100);
//int hw_status_pin = hw[F("status")]["pin"]; // -1
JsonObject light = doc[F("light")];
@@ -208,29 +229,29 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (light_gc_col > 1.5) strip.gammaCorrectCol = true;
else if (light_gc_col > 0.5) strip.gammaCorrectCol = false;
JsonObject light_tr = light[F("tr")];
CJSON(fadeTransition, light_tr[F("mode")]);
JsonObject light_tr = light["tr"];
CJSON(fadeTransition, light_tr["mode"]);
int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr["pal"]);
JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl[F("mode")]);
CJSON(nightlightMode, light_nl["mode"]);
byte prev = nightlightDelayMinsDefault;
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
CJSON(nightlightDelayMinsDefault, light_nl["dur"]);
if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;
CJSON(nightlightTargetBri, light_nl[F("tbri")]);
CJSON(macroNl, light_nl["macro"]);
JsonObject def = doc[F("def")];
JsonObject def = doc["def"];
CJSON(bootPreset, def["ps"]);
CJSON(turnOnAtBoot, def["on"]); // true
CJSON(briS, def["bri"]); // 128
JsonObject interfaces = doc["if"];
JsonObject if_sync = interfaces[F("sync")];
JsonObject if_sync = interfaces["sync"];
CJSON(udpPort, if_sync[F("port0")]); // 21324
CJSON(udpPort2, if_sync[F("port1")]); // 65506
@@ -239,8 +260,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv["fx"]);
CJSON(receiveGroups, if_sync_recv["grp"]);
CJSON(receiveSegmentOptions, if_sync_recv["seg"]);
CJSON(receiveSegmentBounds, if_sync_recv["sb"]);
//! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault;
@@ -267,7 +290,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(e131Universe, if_live_dmx[F("uni")]);
CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]);
CJSON(DMXAddress, if_live_dmx[F("addr")]);
CJSON(DMXMode, if_live_dmx[F("mode")]);
CJSON(DMXMode, if_live_dmx["mode"]);
tdd = if_live[F("timeout")] | -1;
if (tdd >= 0) realtimeTimeoutMs = tdd * 100;
@@ -365,7 +388,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(timerMinutes[it], timer["min"]);
CJSON(timerMacro[it], timer["macro"]);
byte dowPrev = timerWeekday[it];
byte dowPrev = timerWeekday[it];
//note: act is currently only 0 or 1.
//the reason we are not using bool is that the on-disk type in 0.11.0 was already int
int actPrev = timerWeekday[it] & 0x01;
@@ -375,7 +398,17 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
int act = timer["en"] | actPrev;
if (act) timerWeekday[it]++;
}
if (it<8) {
JsonObject start = timer["start"];
byte startm = start["mon"];
if (startm) timerMonth[it] = (startm << 4);
CJSON(timerDay[it], start["day"]);
JsonObject end = timer["end"];
CJSON(timerDayEnd[it], end["day"]);
byte endm = end["mon"];
if (startm) timerMonth[it] += endm & 0x0F;
if (!(timerMonth[it] & 0x0F)) timerMonth[it] += 12; //default end month to 12
}
it++;
}
@@ -539,6 +572,7 @@ void serializeConfig() {
hw_led["cct"] = correctWB;
hw_led[F("cr")] = cctFromRgb;
hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
@@ -548,7 +582,7 @@ void serializeConfig() {
if (!bus || bus->getLength()==0) break;
JsonObject ins = hw_led_ins.createNestedObject();
ins["start"] = bus->getStart();
ins[F("len")] = bus->getLength();
ins["len"] = bus->getLength();
JsonArray ins_pin = ins.createNestedArray("pin");
uint8_t pins[5];
uint8_t nPins = bus->getPins(pins);
@@ -558,7 +592,19 @@ void serializeConfig() {
ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbw")] = bus->isRgbw();
//ins[F("rgbw")] = bus->isRgbw();
}
JsonArray hw_com = hw.createNestedArray(F("com"));
const ColorOrderMap& com = busses.getColorOrderMap();
for (uint8_t s = 0; s < com.count(); s++) {
const ColorOrderMapEntry *entry = com.get(s);
if (!entry) break;
JsonObject co = hw_com.createNestedObject();
co["start"] = entry->start;
co["len"] = entry->len;
co[F("order")] = entry->colorOrder;
}
// button(s)
@@ -589,6 +635,8 @@ void serializeConfig() {
hw_relay["pin"] = rlyPin;
hw_relay["rev"] = !rlyMde;
hw[F("baud")] = serialBaud;
//JsonObject hw_status = hw.createNestedObject("status");
//hw_status["pin"] = -1;
@@ -602,12 +650,12 @@ void serializeConfig() {
light_gc["col"] = (strip.gammaCorrectCol) ? 2.8 : 1.0;
JsonObject light_tr = light.createNestedObject("tr");
light_tr[F("mode")] = fadeTransition;
light_tr["mode"] = fadeTransition;
light_tr["dur"] = transitionDelayDefault / 100;
light_tr["pal"] = strip.paletteFade;
JsonObject light_nl = light.createNestedObject("nl");
light_nl[F("mode")] = nightlightMode;
light_nl["mode"] = nightlightMode;
light_nl["dur"] = nightlightDelayMinsDefault;
light_nl[F("tbri")] = nightlightTargetBri;
light_nl["macro"] = macroNl;
@@ -626,8 +674,10 @@ void serializeConfig() {
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
if_sync_recv["bri"] = receiveNotificationBrightness;
if_sync_recv["col"] = receiveNotificationColor;
if_sync_recv["fx"] = receiveNotificationEffects;
if_sync_recv["fx"] = receiveNotificationEffects;
if_sync_recv["grp"] = receiveGroups;
if_sync_recv["seg"] = receiveSegmentOptions;
if_sync_recv["sb"] = receiveSegmentBounds;
JsonObject if_sync_send = if_sync.createNestedObject("send");
if_sync_send[F("dir")] = notifyDirect;
@@ -651,7 +701,7 @@ void serializeConfig() {
if_live_dmx[F("uni")] = e131Universe;
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
if_live_dmx[F("addr")] = DMXAddress;
if_live_dmx[F("mode")] = DMXMode;
if_live_dmx["mode"] = DMXMode;
if_live[F("timeout")] = realtimeTimeoutMs / 100;
if_live[F("maxbri")] = arlsForceMaxBri;
@@ -740,6 +790,14 @@ void serializeConfig() {
timers_ins0["min"] = timerMinutes[i];
timers_ins0["macro"] = timerMacro[i];
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
if (i<8) {
JsonObject start = timers_ins0.createNestedObject("start");
start["mon"] = (timerMonth[i] >> 4) & 0xF;
start["day"] = timerDay[i];
JsonObject end = timers_ins0.createNestedObject("end");
end["mon"] = timerMonth[i] & 0xF;
end["day"] = timerDayEnd[i];
}
}
JsonObject ota = doc.createNestedObject("ota");

View File

@@ -4,6 +4,12 @@
* Color conversion methods
*/
void setRandomColor(byte* rgb)
{
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb);
}
void colorFromUint32(uint32_t in, bool secondary)
{
byte *_col = secondary ? colSec : col;
@@ -22,11 +28,6 @@ void colorFromUint24(uint32_t in, bool secondary)
_col[2] = B(in);
}
//store color components in uint32_t
uint32_t colorFromRgbw(byte* rgbw) {
return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
}
//relatively change white brightness, minumum A=5
void relativeChangeWhite(int8_t amount, byte lowerBoundary)
{
@@ -259,7 +260,7 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
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);
return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
}
//approximates a Kelvin color temperature from an RGB color.
@@ -299,4 +300,4 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
uint16_t k = 8080 + (225-r) *86;
return (k > 10091) ? 10091 : k;
}
}
}

View File

@@ -39,6 +39,12 @@
#endif
#endif
#ifdef ESP8266
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5
#else
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
#endif
//Usermod IDs
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
@@ -64,6 +70,7 @@
#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"
#define USERMOD_ID_SSDR 24 //Usermod "usermod_v2_seven_segment_display_reloaded.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@@ -74,7 +81,7 @@
//Notifier callMode
#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_BUTTON 2 //default button actions applied to selected segments
#define CALL_MODE_NOTIFICATION 3
#define CALL_MODE_NIGHTLIGHT 4
#define CALL_MODE_NO_NOTIFY 5
@@ -84,6 +91,7 @@
#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
#define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro
//RGB to RGBW conversion mode
#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider
@@ -168,6 +176,7 @@
#define COL_ORDER_RBG 3
#define COL_ORDER_BGR 4
#define COL_ORDER_GBR 5
#define COL_ORDER_MAX 5
//Button type
@@ -217,6 +226,7 @@
#define SEG_DIFFERS_FX 0x08
#define SEG_DIFFERS_BOUNDS 0x10
#define SEG_DIFFERS_GSO 0x20
#define SEG_DIFFERS_SEL 0x80
//Playlist option byte
#define PL_OPTION_SHUFFLE 0x01
@@ -296,11 +306,17 @@
// Size of buffer for API JSON object (increase for more segments)
#ifdef ESP8266
#define JSON_BUFFER_SIZE 9216
#define JSON_BUFFER_SIZE 10240
#else
#define JSON_BUFFER_SIZE 20480
#endif
#ifdef WLED_USE_DYNAMIC_JSON
#define MIN_HEAP_SIZE JSON_BUFFER_SIZE+512
#else
#define MIN_HEAP_SIZE 4096
#endif
// Maximum size of node map (list of other WLED instances)
#ifdef ESP8266
#define WLED_MAX_NODES 24
@@ -329,4 +345,6 @@
#define DEFAULT_LED_COUNT 30
#endif
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates
#endif

View File

@@ -381,6 +381,10 @@ button {
overflow: auto;
}
.modal button:hover {
background-color: var(--c-4);
}
#info {
z-index: 3;
}
@@ -587,8 +591,8 @@ input[type=range]:active + .sliderbubble {
border: 0px solid white;
border-radius: 25px;
transition-duration: 0.5s;
-webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0);
/*-webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0);*/
}
.btn-s {
@@ -609,11 +613,12 @@ input[type=range]:active + .sliderbubble {
width: 216px;
}
.btn-xs {
width: 39px;
width: 42px;
height: 42px;
margin: 2px 0 0 0;
}
.btn-pl-add {
margin-left: 9px;
margin-left: 5px;
}
@@ -645,7 +650,7 @@ input[type=range]:active + .sliderbubble {
.sel-pl {
width: 192px;
background-position: 168px 16px;
margin: 8px 7px 0 0;
margin: 8px 3px 0 0;
}
.sel-ple {

View File

@@ -105,7 +105,7 @@
<button class="xxs cl btn" onclick="selectSlot(2);">3</button>
</div>
<div id="hexw">
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter(this)" autocomplete="off" maxlength="8" />
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter()" autocomplete="off" maxlength="8" />
<button id="hexcnf" class="xxs btn" onclick="fromHex();"><i class="icons btna-icon">&#xe390;</i></button>
</div>
<p class="labels">
@@ -141,7 +141,7 @@
<div id="Effects" class="tabcontent">
<p class="labels">Effect speed</p>
<div class="staytop">
<i class="icons slider-icon">&#xe325;</i>
<i class="icons slider-icon" onclick="tglFreeze()">&#xe325;</i>
<div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<output class="sliderbubble hidden"></output>

View File

@@ -215,7 +215,7 @@ function onLoad() {
//TODO: do some parsing first
})
.catch(function (error) {
console.log("holidays.json does not contain array of holidays. Defaults loaded.");
console.log("No array of holidays in holidays.json. Defaults loaded.");
})
.finally(function(){
loadBg(cfg.theme.bg.url);
@@ -280,6 +280,8 @@ function showToast(text, error = false) {
}
function showErrorToast() {
// if we received a timeout force WS reconnect
reconnectWS();
showToast('Connection to light failed!', true);
}
function clearErrorToast() {
@@ -585,8 +587,8 @@ function populateSegments(s)
<td class="segtd">Offset</td>
</tr>
<tr>
<td class="segtd"><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr>
</table>
@@ -597,13 +599,12 @@ function populateSegments(s)
<td class="segtd">Apply</td>
</tr>
<tr>
<td class="segtd"><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td class="segtd"><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td class="segtd"><i class="icons e-icon cnf cnf-s" id="segc${i}" onclick="setSeg(${i})">&#xe390;</i></td>
</tr>
</table>
<div class="h" id="seg${i}len"></div>
<button class="btn btn-i btn-xs del" id="segd${i}" onclick="delSeg(${i})"><i class="icons btn-icon">&#xe037;</i></button>
<label class="check revchkl">
Reverse direction
<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}>
@@ -614,6 +615,10 @@ function populateSegments(s)
<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi ? "checked":""}>
<span class="checkmark schk"></span>
</label>
<div class="del">
<button class="btn btn-i btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon">&#xe22d;</i></button>
<button class="btn btn-i btn-xs" id="segd${i}" title="Delete" onclick="delSeg(${i})"><i class="icons btn-icon">&#xe037;</i></button>
</div>
</div>
</div><br>`;
}
@@ -627,10 +632,13 @@ function populateSegments(s)
noNewSegs = false;
}
for (var i = 0; i <= lSeg; i++) {
updateLen(i);
updateTrail(d.getElementById(`seg${i}bri`));
if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none";
updateLen(i);
updateTrail(d.getElementById(`seg${i}bri`));
let segr = d.getElementById(`segr${i}`);
if (segr) segr.style.display = "none";
}
if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none";
if (!noNewSegs && (cfg.comp.seglen?parseInt(d.getElementById(`seg${lSeg}s`).value):0)+parseInt(d.getElementById(`seg${lSeg}e`).value)<ledCount) d.getElementById(`segr${lSeg}`).style.display = "inline";
d.getElementById('rsbtn').style.display = (segCount > 1) ? "inline":"none";
}
@@ -872,7 +880,7 @@ function updateLen(s)
{
if (!d.getElementById(`seg${s}s`)) return;
var start = parseInt(d.getElementById(`seg${s}s`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value);
var len = stop - (cfg.comp.seglen?0:start);
var out = "(delete)";
if (len > 1) {
@@ -947,12 +955,20 @@ function cmpP(a, b) {
return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
}
//forces a WebSockets reconnect if timeout (error toast), or successful HTTP response to JSON request
function reconnectWS() {
if (ws) ws.close();
ws = null;
if (lastinfo && lastinfo.ws > -1) setTimeout(makeWS,500);
}
function makeWS() {
if (ws) return;
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
ws.binaryType = "arraybuffer";
ws.onmessage = function(event) {
if (event.data instanceof ArrayBuffer) return; //liveview packet
var json = JSON.parse(event.data);
if (json.leds) return; //liveview packet
clearTimeout(jsonTimeout);
jsonTimeout = null;
clearErrorToast();
@@ -967,9 +983,16 @@ function makeWS() {
displayRover(info, s);
readState(json.state);
};
ws.onclose = function(event) {
d.getElementById('connind').style.backgroundColor = "#831";
}
ws.onclose = (e)=>{
//if there is already a new web socket open, do not null ws
if (ws && ws.readyState === WebSocket.OPEN) return;
d.getElementById('connind').style.backgroundColor = "#831";
ws = null;
}
ws.onopen = (e)=>{
reqsLegal = true;
}
}
function readState(s,command=false) {
@@ -1121,6 +1144,7 @@ function requestJson(command, rinfo = true) {
return;
}
var s = json;
if (reqsLegal && !ws) reconnectWS();
if (!command || rinfo) { //we have info object
if (!rinfo) { //entire JSON (on load)
@@ -1136,7 +1160,7 @@ function requestJson(command, rinfo = true) {
});
},25);
reqsLegal = true;
reqsLegal = true;
}
var info = json.info;
@@ -1169,7 +1193,7 @@ function requestJson(command, rinfo = true) {
displayRover(info, s);
}
readState(s,command);
readState(s,command);
})
.catch(function (error) {
showToast(error, true);
@@ -1254,8 +1278,8 @@ function makeSeg() {
<td class="segtd">${cfg.comp.seglen?"Length":"Stop LED"}</td>
</tr>
<tr>
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lowestUnused})"></td>
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?ns:0)}" value="${ledCount-(cfg.comp.seglen?ns:0)}" oninput="updateLen(${lowestUnused})"></td>
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lowestUnused})" onkeydown="segEnter(${lowestUnused})"></td>
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?ns:0)}" value="${ledCount-(cfg.comp.seglen?ns:0)}" oninput="updateLen(${lowestUnused})" onkeydown="segEnter(${lowestUnused})"></td>
</tr>
</table>
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LED${ledCount - ns >1 ? "s":""}</div>
@@ -1445,8 +1469,8 @@ function makePlEntry(p,i) {
}
function makePlUtil() {
if (pNum < 2) {
showToast("You need at least 2 presets to make a playlist!"); return;
if (pNum < 1) {
showToast("Please make a preset first!"); return;
}
if (plJson[0].transition[0] < 0) plJson[0].transition[0] = tr;
d.getElementById('putil').innerHTML = `<div class="seg pres">
@@ -1491,11 +1515,34 @@ function selSeg(s){
requestJson(obj, false);
}
function rptSeg(s)
{
var name = d.getElementById(`seg${s}t`).value;
var start = parseInt(d.getElementById(`seg${s}s`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value);
if (stop == 0) {return;}
var rev = d.getElementById(`seg${s}rev`).checked;
var mi = d.getElementById(`seg${s}mi`).checked;
var sel = d.getElementById(`seg${s}sel`).checked;
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": powered[s], "bri": parseInt(d.getElementById(`seg${s}bri`).value), "sel": sel}};
if (d.getElementById(`seg${s}grp`)) {
var grp = parseInt(d.getElementById(`seg${s}grp`).value);
var spc = parseInt(d.getElementById(`seg${s}spc`).value);
var ofs = parseInt(d.getElementById(`seg${s}of` ).value);
obj.seg.grp = grp;
obj.seg.spc = spc;
obj.seg.of = ofs;
}
obj.seg.rpt = true;
expand(s);
requestJson(obj);
}
function setSeg(s){
var name = d.getElementById(`seg${s}t`).value;
var start = parseInt(d.getElementById(`seg${s}s`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value);
if (stop <= start) {delSeg(s); return;}
var stop = parseInt(d.getElementById(`seg${s}e`).value);
if ((cfg.comp.seglen && stop == 0) || (!cfg.comp.seglen && stop <= start)) {delSeg(s); return;}
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}};
if (d.getElementById(`seg${s}grp`))
{
@@ -1542,6 +1589,13 @@ function setSegBri(s){
requestJson(obj);
}
function tglFreeze(s=null)
{
var obj = {"seg": {"frz": "t"}}; // toggle
if (s!==null) obj.id = s;
requestJson(obj);
}
function setX(ind = null) {
if (ind === null) {
ind = parseInt(d.querySelector('#fxlist input[name="fx"]:checked').value);
@@ -1764,6 +1818,10 @@ function hexEnter() {
if(event.keyCode == 13) fromHex();
}
function segEnter(s) {
if(event.keyCode == 13) setSeg(s);
}
function fromHex()
{
var str = d.getElementById('hexc').value;

View File

@@ -24,40 +24,40 @@
function updatePreview(leds) {
var str = "linear-gradient(90deg,";
var len = leds.length;
for (i = 0; i < len; i++) {
var leddata = leds[i];
if (leddata.length > 6) leddata = leddata.substring(2);
str += "#" + leddata;
if (i < len -1) str += ","
for (i = 2; i < len; i+=3) {
str += `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
if (i < len -3) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
}
function getLiveJson(event) {
function getLiveJson(e) {
try {
var json = JSON.parse(event.data);
if (json && json.leds) {
requestAnimationFrame(function () {updatePreview(json.leds);});
}
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
updatePreview(leds);
}
}
catch (err) {
console.error("Live-Preview ws error:",err);
console.error("Peek WS error:",err);
}
}
var ws = top.window.ws;
if (ws && ws.readyState === WebSocket.OPEN) {
console.info("Use top WS for peek");
console.info("Peek uses top WS");
ws.send("{'lv':true}");
} else {
console.info("Peek ws opening");
console.info("Peek WS opening");
ws = new WebSocket("ws://"+document.location.host+"/ws");
ws.onopen = function () {
console.info("Peek WS opened");
console.info("Peek WS open");
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message',getLiveJson);
</script>
</body>

View File

@@ -6,7 +6,7 @@
<title>LED Settings</title>
<script>
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=[];
var customStarts=false,startsDirty=[],maxCOOverrides=5;
function H()
{
window.open("https://kno.wled.ge/features/settings/#led-settings");
@@ -347,10 +347,57 @@ ${i+1}:
if (!init) UI();
}
function addCOM(start=0,len=1,co=0) {
var i = d.getElementsByClassName("com_entry").length;
if (i >= 10) return;
var b = `<div class="com_entry">
<hr style="width:260px">
${i+1}: Start: <input type="number" name="XS${i}" id="xs${i}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">&nbsp;
Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65535" value="${len}" required="" oninput="UI()">
<div style="display:inline">Color Order:
<select id="xo${i}" name="XO${i}">
<option value="0">GRB</option>
<option value="1">RGB</option>
<option value="2">BRG</option>
<option value="3">RBG</option>
<option value="4">BGR</option>
<option value="5">GBR</option>
</select>
</div><br></div>`;
gId("com_entries").insertAdjacentHTML("beforeend", b);
gId("xo"+i).value = co;
btnCOM(i+1);
}
function remCOM() {
var entries = d.getElementsByClassName("com_entry");
var i = entries.length;
if (i === 0) return;
entries[i-1].remove();
btnCOM(i-1);
}
function resetCOM(_newMaxCOOverrides=undefined) {
if (_newMaxCOOverrides) {
maxCOOverrides = _newMaxCOOverrides;
}
for (let e of d.getElementsByClassName("com_entry")) {
e.remove();
}
btnCOM(0);
}
function btnCOM(i) {
gId("com_add").style.display = (i<maxCOOverrides) ? "inline":"none";
gId("com_rem").style.display = (i>0) ? "inline":"none";
}
function addBtn(i,p,t) {
var c = gId("btns").innerHTML;
var bt = "BT" + i;
var be = "BE" + i;
var bt = "BT" + String.fromCharCode((i<10?48:55)+i);;
var be = "BE" + String.fromCharCode((i<10?48:55)+i);;
c += `Button ${i} GPIO: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
c += `&nbsp;<select name="${be}">`
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
@@ -431,6 +478,12 @@ ${i+1}:
d.getElementsByName("CV"+i)[0].checked = v.rev;
});
}
if(c.hw.com) {
resetCOM();
c.hw.com.forEach(e => {
addCOM(e.start, e.len, e.order);
});
}
if (c.hw.btn) {
var b = c.hw.btn;
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
@@ -510,6 +563,14 @@ ${i+1}:
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="color_order_mapping">
Color Order Override:
<div id="com_entries"></div>
<hr style="width:260px">
<button type="button" id="com_add" onclick="addCOM();UI()" style="display:none;border-radius:20px;height:36px;">+</button>
<button type="button" id="com_rem" onclick="remCOM();UI()" style="display:none;border-radius:20px;width:36px;height:36px;">-</button><br>
</div>
<hr style="width:260px">
<div id="btns"></div>
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()">
@@ -571,6 +632,7 @@ ${i+1}:
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
</select><br>
Target refresh rate: <input type="number" class="s" min="1" max="120" name="FR" required> FPS
<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>

View File

@@ -85,7 +85,7 @@
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2021 Christian Schwinne <br>
(c) 2016-2022 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>

View File

@@ -5,7 +5,8 @@ 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 H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
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 FC()
@@ -81,7 +82,8 @@ UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required
<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>
Receive: <input type="checkbox" name="RB"> Brightness, <input type="checkbox" name="RC"> Color, and <input type="checkbox" name="RX"> Effects<br>
<input type="checkbox" name="SO"> Segment options, <input type="checkbox" name="SG"> bounds<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>
Send Alexa notifications: <input type="checkbox" name="SA"><br>
@@ -117,48 +119,62 @@ DMX mode:
<option value=5>Dimmer + Multi RGB</option>
<option value=6>Multi RGBW</option>
</select><br>
<a href="https://github.com/Aircoookie/WLED/wiki/E1.31-DMX" target="_blank">E1.31 info</a><br>
<a href="https://kno.wled.ge/interfaces/e1.31-dmx/" target="_blank">E1.31 info</a><br>
Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
Force max brightness: <input type="checkbox" name="FB"><br>
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required>
<h3>Alexa Voice Assistant</h3>
Emulate Alexa device: <input type="checkbox" name="AL"><br>
Alexa invocation name: <input name="AI" maxlength="32">
Alexa invocation name: <input type="text" name="AI" maxlength="32">
<h3>Blynk</h3>
<b>Blynk, MQTT and Hue sync all connect to external hosts!<br>
This may impact the responsiveness of the ESP8266.</b><br>
For best results, only use one of these services at a time.<br>
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>
Host: <input name="BH" maxlength="32">
Host: <input type="text" name="BH" maxlength="32">
Port: <input name="BP" type="number" min="1" max="65535" value="80" class="d5"><br>
Device Auth token: <input name="BK" maxlength="33"><br>
<i>Clear the token field to disable. </i><a href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info</a>
<i>Clear the token field to disable. </i><a href="https://kno.wled.ge/interfaces/blynk/" target="_blank">Setup info</a>
<h3>MQTT</h3>
Enable MQTT: <input type="checkbox" name="MQ"><br>
Broker: <input name="MS" maxlength="32">
Broker: <input type="text" name="MS" maxlength="32">
Port: <input name="MQPORT" type="number" min="1" max="65535" class="d5"><br>
<b>The MQTT credentials are sent over an unsecured connection.<br>
Never use the MQTT password for another service!</b><br>
Username: <input name="MQUSER" maxlength="40"><br>
Username: <input type="text" name="MQUSER" maxlength="40"><br>
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>
Client ID: <input type="text" name="MQCID" maxlength="40"><br>
Device Topic: <input type="text" name="MD" maxlength="32"><br>
Group Topic: <input type="text" 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>
<i>Reboot required to apply changes. </i><a href="https://kno.wled.ge/interfaces/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>
Poll Hue light <input name="HL" type="number" min="1" max="99" > every <input name="HI" type="number" min="100" max="65000"> ms: <input type="checkbox" name="HP"><br>
Then, receive <input type="checkbox" name="HO"> On/Off, <input type="checkbox" name="HB"> Brightness, and <input type="checkbox" name="HC"> Color<br>
Hue Bridge IP:<br>
<input name="H0" type="number" min="0" max="255" > .
<input name="H1" type="number" min="0" max="255" > .
<input name="H2" type="number" min="0" max="255" > .
<input name="H3" type="number" min="0" max="255" ><br>
<input name="H0" type="number" class="s" min="0" max="255" > .
<input name="H1" type="number" class="s" min="0" max="255" > .
<input name="H2" type="number" class="s" min="0" max="255" > .
<input name="H3" type="number" class="s" min="0" max="255" ><br>
<b>Press the pushlink button on the bridge, after that save this page!</b><br>
(when first connecting)<br>
Hue status: <span class="sip"> Disabled in this build </span><hr>
Hue status: <span class="sip"> Disabled in this build </span>
<h3>Serial</h3>
Baud rate:
<select name=BD>
<option value=1152>115200</option>
<option value=2304>230400</option>
<option value=4608>460800</option>
<option value=5000>500000</option>
<option value=5760>576000</option>
<option value=9216>921600</option>
<option value=10000>1000000</option>
<option value=15000>1500000</option>
</select><br>
<i>Keep at 115200 to use Improv. Some boards may not support high rates.</i>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>

View File

@@ -6,9 +6,11 @@
<title>Time Settings</title>
<script>
var d=document;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var cals = 'style="font-size:27px;margin-top:-6px;cursor:pointer"'; //hack as to not repeat CSS on all pages
function H()
{
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");
window.open("https://kno.wled.ge/features/settings/#time-settings");
}
function B()
{
@@ -16,12 +18,21 @@
}
function S()
{
BTa();GetV();Cs();FC();
BTa();GetV();updLoc();Cs();FC();
}
function gId(s)
{
return d.getElementById(s);
}
function gN(s) {
return d.getElementsByName(s)[0];
}
function expand(o,i)
{
var t = gId("WD"+i);
t.style.display = t.style.display!=="none" ? "none" : "";
o.innerHTML = t.style.display==="none" ? "&#128467;" : "&#x2715;";
}
function Cs()
{
gId("cac").style.display="none";
@@ -41,54 +52,87 @@
gId("coc").style.display="none";
}
}
function BTa()
{
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
for (i=0;i<8;i++)
{
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
}
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>";
ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>";
gId("TMT").innerHTML=ih;
}
function FC()
{
for(j=0;j<8;j++)
{
for(i=0;i<10;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1;
}
}
function Wd()
{
a=[0,0,0,0,0,0,0,0,0,0];
for(i=0;i<10;i++)
{
m=1;
for(j=0;j<8;j++)
{
a[i]+=gId("W"+i+j).checked*m;m*=2;
}
gId("W"+i).value=a[i];
}
}
function BTa()
{
var ih="<tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr>";
for (i=0;i<8;i++) {
ih+=`<tr><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td>
<td><input name="H${i}" class="xs" type="number" min="0" max="24"></td>
<td><input name="N${i}" class="xs" type="number" min="0" max="59"></td>
<td><input name="T${i}" class="s" type="number" min="0" max="250"></td>
<td><div id="CB${i}" onclick="expand(this,${i})" ${cals}>&#128467;</div></td></tr>`;
ih+=`<tr><td colspan=5><div id="WD${i}" style="display:none;">Run on weekdays`;
ih+=`<table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`
for (j=1;j<8;j++) ih+=`<td><input id="W${i}${j}" type="checkbox"></td>`;
ih+=`</tr></table>from
<select name="M${i}">`;
for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
ih+=`</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to
<select name="P${i}">`;
for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
ih+=`</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input>
<hr></div></td></tr>`;
}
ih+=`<tr><td><input name="W8" id="W8" type="hidden"><input id="W80" type="checkbox"></td>
<td>Sunrise<input name="H8" value="255" type="hidden"></td>
<td><input name="N8" class="xs" type="number" min="-59" max="59"></td>
<td><input name="T8" class="s" type="number" min="0" max="250"></td>
<td><div id="CB8" onclick="expand(this,8)" ${cals}>&#128467;</div></td></tr><tr><td colspan=5>`;
ih+=`<div id="WD8"style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
for (j=1;j<8;j++) ih+=`<td><input id="W8${j}" type="checkbox"></td>`;
ih+="</tr></table><hr></div></td></tr>";
ih+=`<tr><td><input name="W9" id="W9" type="hidden"><input id="W90" type="checkbox"></td>
<td>Sunset<input name="H9" value="255" type="hidden"></td>
<td><input name="N9" class="xs" type="number" min="-59" max="59"></td>
<td><input name="T9" class="s" type="number" min="0" max="250"></td>
<td><div id="CB9" onclick="expand(this,9)" ${cals}>&#128467;</div></td></tr><tr><td colspan=5>`;
ih+=`<div id="WD9" style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
for (j=1;j<8;j++) ih+=`<td><input id="W9${j}" type="checkbox"></td>`;
ih+="</tr></table><hr></div></td></tr>";
gId("TMT").innerHTML=ih;
}
function FC()
{
for(i=0;i<10;i++)
{
let wd = gId("W"+i).value;
for(j=0;j<8;j++) {
gId("W"+i+j).checked=wd>>j&1;
}
if ((wd&127) != 127 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
}
}
}
function Wd()
{
a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++) {
m=1;
for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;}
gId("W"+i).value=a[i];
}
if (d.Sf.LTR.value==="S") { d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); }
if (d.Sf.LNR.value==="W") { d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); }
}
function addRow(i,p,l,d) {
var t = gId("macros"); // table
var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); // table row.
var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); // table row.
var b = String.fromCharCode((i<10?48:55)+i);
var td = document.createElement('td'); // TABLE DEFINITION.
td = tr.insertCell(0);
td.innerHTML = `Button ${i}:`;
td = tr.insertCell(1);
td.innerHTML = `<input name="MP${i}" type="number" min="0" max="250" value="${p}" required>`;
td.innerHTML = `<input name="MP${b}" type="number" class="s" min="0" max="250" value="${p}" required>`;
td = tr.insertCell(2);
td.innerHTML = `<input name="ML${i}" type="number" min="0" max="250" value="${l}" required>`;
td.innerHTML = `<input name="ML${b}" type="number" class="s" min="0" max="250" value="${l}" required>`;
td = tr.insertCell(3);
td.innerHTML = `<input name="MD${i}" type="number" min="0" max="250" value="${d}" required>`;
td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
}
function updLoc(i) {
if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N";
if (parseFloat(d.Sf.LN.value)<0) { d.Sf.LNR.value = "W"; d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } else d.Sf.LNR.value = "E";
}
function GetV()
{
@@ -105,7 +149,7 @@
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
<h2>Time setup</h2>
Get time from NTP server: <input type="checkbox" name="NT"><br>
<input name="NS" maxlength="32"><br>
<input type="text" name="NS" maxlength="32"><br>
Use 24h format: <input type="checkbox" name="CF"><br>
Time zone:
<select name="TZ">
@@ -134,8 +178,8 @@
</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>
Latitude (N): <input name="LT" type="number" min="-66.6" max="66.6" step="0.01">
Longitude (E): <input name="LN" type="number" min="-180" max="180" step="0.01">
Latitude: <select name="LTR"><option value="N">N</option><option value="S">S</option></select><input name="LT" type="number" class="xl" min="0" max="66.6" step="0.01"><br>
Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01">
<div id="sun" class="times"></div>
<h3>Clock</h3>
Clock Overlay:
@@ -158,16 +202,16 @@
</div>
Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br>
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br>
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br>
Date:&nbsp;<nowrap>20<input name="CY" class="xs" type="number" min="0" max="99" required>-<input name="CI" class="xs" type="number" min="1" max="12" required>-<input name="CD" class="xs" type="number" min="1" max="31" required></nowrap><br>
Time: <nowrap><input name="CH" class="xs" type="number" min="0" max="23" required>:<input name="CM" class="xs" type="number" min="0" max="59" required>:<input name="CS" class="xs" type="number" min="0" max="59" required></nowrap><br>
<h3>Macro presets</h3>
<b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset id below!</i>
Just enter the preset ID below!</i>
<i>Use 0 for the default action instead of a preset</i><br>
Alexa On/Off Preset: <input name="A0" type="number" min="0" max="250" required> <input name="A1" type="number" min="0" max="250" required><br>
Countdown-Over Preset: <input name="MC" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" required><br>
Alexa On/Off Preset: <input name="A0" class="m" type="number" min="0" max="250" required> <input name="A1" class="m" type="number" min="0" max="250" required><br>
Countdown-Over Preset: <input name="MC" class="m" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" class="m" type="number" min="0" max="250" required><br>
<h3>Button actions</h3>
<table style="margin: 0 auto;" id="macros">
<thead>
@@ -181,11 +225,12 @@
<tbody>
</tbody>
</table>
<a href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button" target="_blank">Analog Button setup</a>
<a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
<h3>Time-controlled presets</h3>
<div style="display: inline-block">
<table id="TMT">
</table></div><hr>
<div style="display: inline-block">
<table id="TMT" style="min-width:330px;"></table>
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>

View File

@@ -66,7 +66,6 @@ input[type="number"].xs {
}
input[type="checkbox"] {
transform: scale(1.5);
margin-right: 10px;
}
select {
background: #333;
@@ -80,7 +79,6 @@ td {
.d5 {
width: 4.5em !important;
}
#toast {
opacity: 0;
background-color: #444;

View File

@@ -15,12 +15,16 @@ void handleAlexa();
void onAlexaChange(EspalexaDevice* dev);
//blynk.cpp
#ifndef WLED_DISABLE_BLYNK
void initBlynk(const char* auth, const char* host, uint16_t port);
void handleBlynk();
void updateBlynk();
#endif
//button.cpp
void shortPressAction(uint8_t b=0);
void longPressAction(uint8_t b=0);
void doublePressAction(uint8_t b=0);
bool isButtonPressed(uint8_t b=0);
void handleButton();
void handleIO();
@@ -56,7 +60,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
void colorFromUint32(uint32_t in, bool secondary = false);
void colorFromUint24(uint32_t in, bool secondary = false);
uint32_t colorFromRgbw(byte* rgbw);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb);
@@ -71,6 +75,8 @@ bool colorFromHexString(byte* rgb, const char* in);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
//dmx.cpp
void initDMX();
void handleDMX();
@@ -133,16 +139,19 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root);
void serveJson(AsyncWebServerRequest* request);
#ifdef WLED_ENABLE_JSONLIVE
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
#endif
//led.cpp
void setValuesFromMainSeg();
void setValuesFromFirstSelectedSeg();
void resetTimebase();
void toggleOnOff();
void setAllLeds();
void setLedsStandard();
bool colorChanged();
void colorUpdated(int callMode);
void applyBri();
void applyFinalBri();
void applyValuesToSelectedSegs();
void colorUpdated(byte callMode);
void stateUpdated(byte callMode);
void updateInterfaces(uint8_t callMode);
void handleTransitions();
void handleNightlight();
@@ -190,11 +199,12 @@ void handlePlaylist();
//presets.cpp
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());
inline void saveTemporaryPreset() {savePreset(255, false);};
void deletePreset(byte index);
//set.cpp
void _setRandomColor(bool _sec,bool fromButton=false);
bool isAsterisksOnly(const char* str, byte maxLen);
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
@@ -217,10 +227,10 @@ void sendSysInfoUDP();
//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();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
//um_manager.cpp
class Usermod {
@@ -279,6 +289,7 @@ void clearEEPROM();
//wled_serial.cpp
void handleSerial();
void updateBaudRate(uint32_t rate);
//wled_server.cpp
bool isIp(String str);

View File

@@ -5,7 +5,7 @@
*/
#ifdef ARDUINO_ARCH_ESP32 //FS info bare IDF function until FS wrapper is available for ESP32
#if WLED_FS != LITTLEFS
#if WLED_FS != LITTLEFS && ESP_IDF_VERSION_MAJOR < 4
#include "esp_spiffs.h"
#endif
#endif
@@ -359,9 +359,9 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
void updateFSInfo() {
#ifdef ARDUINO_ARCH_ESP32
#if WLED_FS == LITTLEFS
fsBytesTotal = LITTLEFS.totalBytes();
fsBytesUsed = LITTLEFS.usedBytes();
#if WLED_FS == LITTLEFS || ESP_IDF_VERSION_MAJOR >= 4
fsBytesTotal = WLED_FS.totalBytes();
fsBytesUsed = WLED_FS.usedBytes();
#else
esp_spiffs_info(nullptr, &fsBytesTotal, &fsBytesUsed);
#endif

View File

@@ -42,7 +42,7 @@ function B(){window.history.back()}function U(){document.getElementById("uf").st
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}#msg{display:none}
</style></head><body><h2>WLED Software Update</h2><form method="POST"
action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()">
Installed version: 0.13.0-b6<br>Download the latest binary: <a
Installed version: 0.13.0-b7<br>Download the latest binary: <a
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
</a><br><input type="file" class="bt" name="update" required><br><input
@@ -85,7 +85,7 @@ charset="utf-8"><meta name="theme-color" content="#222222"><title>
WLED Live Preview</title><style>
body{margin:0}#canv{background:#000;filter:brightness(175%);width:100%;height:100%;position:absolute}
</style></head><body><div id="canv"><script>
function updatePreview(e){var n="linear-gradient(90deg,",o=e.length;for(i=0;i<o;i++){var t=e[i];t.length>6&&(t=t.substring(2)),n+="#"+t,i<o-1&&(n+=",")}n+=")",document.getElementById("canv").style.background=n}function getLiveJson(e){try{var n=JSON.parse(e.data);n&&n.leds&&requestAnimationFrame((function(){updatePreview(n.leds)}))}catch(e){console.error("Live-Preview ws error:",e)}}var ws=top.window.ws;ws&&ws.readyState===WebSocket.OPEN?(console.info("Use top WS for peek"),ws.send("{'lv':true}")):(console.info("Peek ws opening"),(ws=new WebSocket("ws://"+document.location.host+"/ws")).onopen=function(){console.info("Peek WS opened"),ws.send("{'lv':true}")}),ws.addEventListener("message",getLiveJson)
function updatePreview(e){var n="linear-gradient(90deg,",t=e.length;for(i=2;i<t;i+=3)n+=`rgb(${e[i]},${e[i+1]},${e[i+2]})`,i<t-3&&(n+=",");n+=")",document.getElementById("canv").style.background=n}function getLiveJson(e){try{if("[object ArrayBuffer]"===toString.call(e.data)){let e=new Uint8Array(event.data);if(76!=e[0])return;updatePreview(e)}}catch(e){console.error("Peek WS error:",e)}}var ws=top.window.ws;ws&&ws.readyState===WebSocket.OPEN?(console.info("Peek uses top WS"),ws.send("{'lv':true}")):(console.info("Peek WS opening"),(ws=new WebSocket("ws://"+document.location.host+"/ws")).onopen=function(){console.info("Peek WS open"),ws.send("{'lv':true}")}),ws.binaryType="arraybuffer",ws.addEventListener("message",getLiveJson)
</script></body></html>)=====";

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -189,7 +189,7 @@ void sendImprovInfoResponse() {
out[11] = 4; //Firmware len ("WLED")
out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D';
uint8_t lengthSum = 17;
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b6/%i"),VERSION);
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b7/%i"),VERSION);
out[16] = vlen; lengthSum += vlen;
uint8_t hlen = 7;
#ifdef ESP8266

View File

@@ -72,7 +72,7 @@ void decBrightness()
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
{
byte prevError = errorFlag;
if (!applyPreset(presetID, CALL_MODE_BUTTON)) {
if (!applyPreset(presetID, CALL_MODE_BUTTON_PRESET)) {
effectCurrent = effectID;
effectPalette = paletteID;
errorFlag = prevError; //clear error 12 from non-existent preset
@@ -87,7 +87,7 @@ bool decodeIRCustom(uint32_t code)
{
//just examples, feel free to modify or remove
case IRCUSTOM_ONOFF : toggleOnOff(); break;
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON); break;
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON_PRESET); break;
default: return false;
}
@@ -162,6 +162,7 @@ void decodeIR(uint32_t code)
return;
}
lastValidCode = 0; irTimesRepeated = 0;
lastRepeatableAction = ACTION_NONE;
if (decodeIRCustom(code)) return;
if (irEnabled == 8) { // any remote configurable with ir.json file
decodeIRJson(code);
@@ -193,53 +194,31 @@ void decodeIR(uint32_t code)
colorUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
}
void applyRepeatActions(){
if (lastRepeatableAction == ACTION_BRIGHT_UP)
{
incBrightness(); colorUpdated(CALL_MODE_BUTTON);
}
else if (lastRepeatableAction == ACTION_BRIGHT_DOWN )
{
decBrightness(); colorUpdated(CALL_MODE_BUTTON);
}
if (lastRepeatableAction == ACTION_SPEED_UP)
{
changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
}
else if (lastRepeatableAction == ACTION_SPEED_DOWN )
{
changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
}
if (lastRepeatableAction == ACTION_INTENSITY_UP)
{
changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
}
else if (lastRepeatableAction == ACTION_INTENSITY_DOWN )
{
changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
}
if (lastValidCode == IR40_WPLUS)
{
relativeChangeWhite(10); colorUpdated(CALL_MODE_BUTTON);
}
else if (lastValidCode == IR40_WMINUS)
{
relativeChangeWhite(-10, 5); colorUpdated(CALL_MODE_BUTTON);
}
else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 )
{
nightlightActive = true;
nightlightStartTime = millis();
colorUpdated(CALL_MODE_BUTTON);
}
else if (irEnabled == 8)
{
decodeIRJson(lastValidCode);
}
void applyRepeatActions()
{
if (irEnabled == 8) {
decodeIRJson(lastValidCode);
return;
} else switch (lastRepeatableAction) {
case ACTION_BRIGHT_UP : incBrightness(); colorUpdated(CALL_MODE_BUTTON); return;
case ACTION_BRIGHT_DOWN : decBrightness(); colorUpdated(CALL_MODE_BUTTON); return;
case ACTION_SPEED_UP : changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
case ACTION_SPEED_DOWN : changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
case ACTION_INTENSITY_UP : changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
case ACTION_INTENSITY_DOWN : changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
default: break;
}
if (lastValidCode == IR40_WPLUS) {
relativeChangeWhite(10);
colorUpdated(CALL_MODE_BUTTON);
} else if (lastValidCode == IR40_WMINUS) {
relativeChangeWhite(-10, 5);
colorUpdated(CALL_MODE_BUTTON);
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
nightlightActive = true;
nightlightStartTime = millis();
colorUpdated(CALL_MODE_BUTTON);
}
}
void decodeIR24(uint32_t code)
@@ -363,20 +342,20 @@ void decodeIR40(uint32_t code)
case IR40_MAGENTA : colorFromUint24(COLOR_MAGENTA); break;
case IR40_PINK : colorFromUint24(COLOR_PINK); break;
case IR40_WARMWHITE2 : {
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE2); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE2); } break;
case IR40_WARMWHITE : {
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE); } break;
case IR40_WHITE : {
if (strip.isRgbw) { colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_NEUTRALWHITE); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_NEUTRALWHITE); } break;
case IR40_COLDWHITE : {
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE); } break;
case IR40_COLDWHITE2 : {
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE2); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE2); } break;
case IR40_WPLUS : relativeChangeWhite(10); break;
case IR40_WMINUS : relativeChangeWhite(-10, 5); break;
case IR40_WOFF : whiteLast = col[3]; col[3] = 0; break;
@@ -420,22 +399,22 @@ void decodeIR44(uint32_t code)
case IR44_MAGENTA : colorFromUint24(COLOR_MAGENTA); break;
case IR44_PINK : colorFromUint24(COLOR_PINK); break;
case IR44_WHITE : {
if (strip.isRgbw) {
if (strip.hasWhiteChannel()) {
if (col[3] > 0) col[3] = 0;
else { colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
} else colorFromUint24(COLOR_NEUTRALWHITE); } break;
case IR44_WARMWHITE2 : {
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE2); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE2); } break;
case IR44_WARMWHITE : {
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_WARMWHITE); } break;
case IR44_COLDWHITE : {
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE); } break;
case IR44_COLDWHITE2 : {
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE2); } break;
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
else colorFromUint24(COLOR_COLDWHITE2); } break;
case IR44_REDPLUS : relativeChange(&effectCurrent, 1, 0, MODE_COUNT); break;
case IR44_REDMINUS : relativeChange(&effectCurrent, -1, 0); break;
case IR44_GREENPLUS : relativeChange(&effectPalette, 1, 0, strip.getPaletteCount() -1); break;
@@ -596,7 +575,7 @@ void decodeIRJson(uint32_t code)
cmdStr = fdo["cmd"].as<String>();
jsonCmdObj = fdo["cmd"]; //object
if (!cmdStr.isEmpty())
if (jsonCmdObj.isNull())
{
if (cmdStr.startsWith("!")) {
// call limited set of C functions
@@ -626,12 +605,19 @@ void decodeIRJson(uint32_t code)
if (!cmdStr.startsWith("win&")) {
cmdStr = "win&" + cmdStr;
}
handleSet(nullptr, cmdStr, false);
fdo.clear(); //clear JSON buffer (it is no longer needed)
handleSet(nullptr, cmdStr, false); // no colorUpdated() call here
}
colorUpdated(CALL_MODE_BUTTON);
} else if (!jsonCmdObj.isNull()) {
} else {
// command is JSON object
deserializeState(jsonCmdObj, CALL_MODE_BUTTON);
if (jsonCmdObj[F("psave")].isNull()) deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
else {
uint8_t psave = jsonCmdObj[F("psave")].as<int>();
char pname[33];
sprintf_P(pname, PSTR("IR Preset %d"), psave);
fdo.clear();
if (psave > 0 && psave < 251) savePreset(psave, true, pname, fdo);
}
}
releaseJSONBufferLock();
}
@@ -661,7 +647,7 @@ void handleIR()
{
if (results.value != 0) // only print results if anything is received ( != 0 )
{
if (!pinManager.isPinAllocated(1)) //GPIO 1 - Serial TX pin
if (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut) //GPIO 1 - Serial TX pin
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
}
decodeIR(results.value);

View File

@@ -8,7 +8,7 @@
bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255) {
if (elem.is<int>()) {
if (elem < 0) return false; //ignore e.g. {"ps":-1}
if (elem < 0) return false; //ignore e.g. {"ps":-1}
*val = elem;
return true;
} else if (elem.is<const char*>()) {
@@ -27,16 +27,33 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (id >= strip.getMaxSegments()) return;
WS2812FX::Segment& seg = strip.getSegment(id);
//WS2812FX::Segment prev;
//prev = seg; //make a backup so we can tell if something changed
WS2812FX::Segment prev = seg; //make a backup so we can tell if something changed
uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1;
if (stop < 0) {
uint16_t len = elem[F("len")];
uint16_t len = elem["len"];
stop = (len > 0) ? start + len : seg.stop;
}
//repeat, multiplies segment until all LEDs are used, or max segments reached
bool repeat = elem["rpt"] | false;
if (repeat && stop>0) {
elem.remove("id"); // remove for recursive call
elem.remove("rpt"); // remove for recursive call
elem.remove("n"); // remove for recursive call
uint16_t len = stop - start;
for (byte i=id+1; i<strip.getMaxSegments(); i++) {
start = start + len;
if (start >= strip.getLengthTotal()) break;
elem["start"] = start;
elem["stop"] = start + len;
elem["rev"] = !elem["rev"]; // alternate reverse on even/odd segments
deserializeSegment(elem, i, presetId); // recursive call with new id
}
return;
}
if (elem["n"]) {
// name field exists
if (seg.name) { //clear old name
@@ -64,7 +81,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing;
uint16_t of = seg.offset;
uint16_t of = seg.offset;
uint16_t len = 1;
if (stop > start) len = stop - start;
@@ -76,7 +93,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs;
}
if (stop > start && of > len -1) of = len -1;
strip.setSegment(id, start, stop, grp, spc, of);
strip.setSegment(id, start, stop, grp, spc, of);
byte segbri = 0;
if (getVal(elem["bri"], &segbri)) {
@@ -87,10 +104,11 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id);
uint8_t cctPrev = seg.cct;
bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE);
seg.setOption(SEG_OPTION_FREEZE, frz, id);
seg.setCCT(elem["cct"] | seg.cct, id);
if (seg.cct != cctPrev && id == strip.getMainSegmentId()) effectChanged = true; //send UDP
JsonArray colarr = elem["col"];
if (!colarr.isNull())
@@ -117,21 +135,14 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array
byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0)
seg.setColor(i, 0, id);
copyArray(colX, rgbw, 4);
colValid = true;
}
if (!colValid) continue;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
} else { //normal case, apply directly to segment
seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id);
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]), id);
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
}
@@ -147,35 +158,24 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
}
#endif
//if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal);
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
//temporary, strip object gets updated via colorUpdated()
if (id == strip.getMainSegmentId()) {
byte effectPrev = effectCurrent;
if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
effectSpeed = elem[F("sx")] | effectSpeed;
effectIntensity = elem[F("ix")] | effectIntensity;
getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount());
} else { //permanent
byte fx = seg.mode;
byte fxPrev = fx;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
strip.setMode(id, fx);
if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity;
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
byte fx = seg.mode;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
if (!presetId && currentPlaylist>=0) unloadPlaylist();
strip.setMode(id, fx);
}
//getVal also supports inc/decrementing and random
getVal(elem[F("sx")], &seg.speed, 0, 255);
getVal(elem[F("ix")], &seg.intensity, 0, 255);
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) {
strip.setPixelSegment(id);
uint8_t oldSegId = strip.setPixelSegment(id);
//freeze and init to black
if (!seg.getOption(SEG_OPTION_FREEZE)) {
@@ -221,17 +221,21 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
set = 0;
}
}
strip.setPixelSegment(255);
strip.setPixelSegment(oldSegId);
strip.trigger();
} else { //return to regular effect
} else if (!elem["frz"] && iarr.isNull()) { //return to regular effect
seg.setOption(SEG_OPTION_FREEZE, false);
}
return; // seg.differs(prev);
// send UDP if not in preset and something changed that is not just selection
//if (!presetId && (seg.differs(prev) & 0x7F)) stateChanged = true;
// send UDP if something changed that is not just selection
if (seg.differs(prev) & 0x7F) stateChanged = true;
return;
}
// deserializes WLED state (fileDoc points to doc object if called from web server)
bool deserializeState(JsonObject root, byte callMode, byte presetId)
{
strip.applyToAllSelected = false;
bool stateResponse = root[F("v")] | false;
getVal(root["bri"], &bri);
@@ -264,16 +268,16 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
tr = root[F("tb")] | -1;
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
JsonObject nl = root["nl"];
JsonObject nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl[F("dur")] | nightlightDelayMins;
nightlightMode = nl[F("mode")] | nightlightMode;
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
nightlightMode = nl["mode"] | nightlightMode;
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
JsonObject udpn = root["udpn"];
JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect;
receiveNotifications = udpn["recv"] | receiveNotifications;
bool noNotification = udpn[F("nn")]; //send no notification just for this request
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
if (timein != UINT32_MAX) {
@@ -292,34 +296,30 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
else realtimeTimeout = 0; //cancel realtime mode immediately
}
byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root[F("mainseg")] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
strip.setMainSegmentId(root[F("mainseg")] | strip.getMainSegmentId());
int it = 0;
JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>())
{
int id = segVar["id"] | -1;
if (id < 0) { //set all selected segments
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) {
//apply all selected segments
bool didSet = false;
byte lowestActive = 99;
for (byte s = 0; s < strip.getMaxSegments(); s++)
{
WS2812FX::Segment sg = strip.getSegment(s);
if (sg.isActive())
{
if (lowestActive == 99) lowestActive = s;
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) {
if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
didSet = true;
}
}
}
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
} else { //set only the segment with the specified ID
deserializeSegment(segVar, it, presetId);
//if none selected, apply to the main segment
if (!didSet) deserializeSegment(segVar, strip.getMainSegmentId(), presetId);
} else {
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
}
} else {
JsonArray segs = segVar.as<JsonArray>();
@@ -369,63 +369,60 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
JsonObject playlist = root[F("playlist")];
if (!playlist.isNull() && loadPlaylist(playlist, presetId)) {
//do not notify here, because the first playlist entry will do
noNotification = true;
if (root["on"].isNull()) callMode = CALL_MODE_NO_NOTIFY;
else callMode = CALL_MODE_DIRECT_CHANGE; // possible bugfix for playlist only containing HTTP API preset FX=~
} else {
interfaceUpdateCallMode = CALL_MODE_WS_SEND;
}
colorUpdated(noNotification ? CALL_MODE_NO_NOTIFY : callMode);
stateUpdated(callMode);
return stateResponse;
}
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
{
root["id"] = id;
root["id"] = id;
if (segmentBounds) {
root["start"] = seg.start;
root["stop"] = seg.stop;
}
if (!forPreset) root[F("len")] = seg.stop - seg.start;
if (!forPreset) root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset;
root["on"] = seg.getOption(SEG_OPTION_ON);
root["frz"] = seg.getOption(SEG_OPTION_FREEZE);
byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255;
root["cct"] = seg.cct;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
for (uint8_t i = 0; i < 3; i++)
{
// to conserve RAM we will serialize the col array manually
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
const char *format = strip.hasWhiteChannel() ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]");
for (uint8_t i = 0; i < 3; i++)
{
byte segcol[4]; byte* c = segcol;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
c = (i == 0)? col:colSec;
} else {
segcol[0] = (byte)(seg.colors[i] >> 16); segcol[1] = (byte)(seg.colors[i] >> 8);
segcol[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24);
}
segcol[0] = R(seg.colors[i]);
segcol[1] = G(seg.colors[i]);
segcol[2] = B(seg.colors[i]);
segcol[3] = W(seg.colors[i]);
char tmpcol[22];
if (strip.isRgbw) sprintf_P(tmpcol, PSTR("[%u,%u,%u,%u]"), c[0], c[1], c[2], c[3]);
else sprintf_P(tmpcol, PSTR("[%u,%u,%u]"), c[0], c[1], c[2]);
strcat(colstr, i<2 ? strcat(tmpcol,",") : tmpcol);
}
strcat(colstr,"]");
sprintf_P(tmpcol, format, (unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3]);
strcat(colstr, i<2 ? strcat(tmpcol, ",") : tmpcol);
}
strcat(colstr, "]");
root["col"] = serialized(colstr);
root["fx"] = seg.mode;
root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity;
root["pal"] = seg.palette;
root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root["fx"] = seg.mode;
root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity;
root["pal"] = seg.palette;
root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
}
@@ -438,7 +435,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
}
if (!forPreset) {
if (errorFlag) root[F("error")] = errorFlag;
if (errorFlag) {root[F("error")] = errorFlag; errorFlag = ERR_NONE;} //prevent error message to persist on screen
root["ps"] = (currentPreset > 0) ? currentPreset : -1;
root[F("pl")] = currentPlaylist;
@@ -447,8 +444,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
JsonObject nl = root.createNestedObject("nl");
nl["on"] = nightlightActive;
nl[F("dur")] = nightlightDelayMins;
nl[F("mode")] = nightlightMode;
nl["dur"] = nightlightDelayMins;
nl["mode"] = nightlightMode;
nl[F("tbri")] = nightlightTargetBri;
if (nightlightActive) {
nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
@@ -466,11 +463,9 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root[F("mainseg")] = strip.getMainSegmentId();
JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++)
{
WS2812FX::Segment sg = strip.getSegment(s);
if (sg.isActive())
{
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
} else if (forPreset && segmentBounds) { //disable segments not part of preset
@@ -508,21 +503,32 @@ void serializeInfo(JsonObject root)
JsonObject leds = root.createNestedObject("leds");
leds[F("count")] = strip.getLengthTotal();
leds[F("rgbw")] = strip.isRgbw;
leds[F("wv")] = false;
leds["cct"] = correctWB || strip.hasCCTBus();
switch (Bus::getAutoWhiteMode()) {
case RGBW_MODE_MANUAL_ONLY:
case RGBW_MODE_DUAL:
if (strip.isRgbw) leds[F("wv")] = true;
break;
}
leds[F("rgbw")] = strip.hasRGBWBus(); //deprecated, use info.leds.lc
leds[F("wv")] = false; //deprecated, use info.leds.lc
leds["cct"] = correctWB || strip.hasCCTBus(); //deprecated, use info.leds.lc
switch (Bus::getAutoWhiteMode()) {
case RGBW_MODE_MANUAL_ONLY:
case RGBW_MODE_DUAL:
if (strip.hasWhiteChannel()) leds[F("wv")] = true;
break;
}
leds[F("pwr")] = strip.currentMilliamps;
leds[F("fps")] = strip.getFps();
leds["fps"] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds[F("maxseg")] = strip.getMaxSegments();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
uint8_t totalLC = 0;
JsonArray lcarr = leds.createNestedArray(F("seglc"));
uint8_t nSegs = strip.getLastActiveSegmentId();
for (byte s = 0; s <= nSegs; s++) {
uint8_t lc = strip.getSegment(s).getLightCapabilities();
totalLC |= lc;
lcarr.add(lc);
}
leds["lc"] = totalLC;
root[F("str")] = syncToggleReceive;
@@ -596,9 +602,11 @@ void serializeInfo(JsonObject root)
#endif
root[F("freeheap")] = ESP.getFreeHeap();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) root[F("psram")] = ESP.getFreePsram();
#endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
usermods.addToJsonInfo(root);
byte os = 0;
@@ -645,7 +653,7 @@ void setPaletteColors(JsonArray json, CRGBPalette16 palette)
for (int i = 0; i < 16; i++) {
JsonArray colors = json.createNestedArray();
CRGB color = palette[i];
colors.add((((float)i / (float)16) * 255));
colors.add(i<<4);
colors.add(color.red);
colors.add(color.green);
colors.add(color.blue);
@@ -707,9 +715,6 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
for (int i = start; i < end; i++) {
JsonArray curPalette = palettes.createNestedArray(String(i));
CRGB prim;
CRGB sec;
CRGB ter;
switch (i) {
case 0: //default palette
setPaletteColors(curPalette, PartyColors_p);
@@ -814,10 +819,12 @@ void serveJson(AsyncWebServerRequest* request)
else if (url.indexOf("si") > 0) subJson = 3;
else if (url.indexOf("nodes") > 0) subJson = 4;
else if (url.indexOf("palx") > 0) subJson = 5;
#ifdef WLED_ENABLE_JSONLIVE
else if (url.indexOf("live") > 0) {
serveLiveLeds(request);
return;
}
#endif
else if (url.indexOf(F("eff")) > 0) {
request->send_P(200, "application/json", JSON_mode_names);
return;
@@ -873,17 +880,18 @@ void serveJson(AsyncWebServerRequest* request)
releaseJSONBufferLock();
}
#ifdef WLED_ENABLE_JSONLIVE
#define MAX_LIVE_LEDS 180
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
{
#ifdef WLED_ENABLE_WEBSOCKETS
AsyncWebSocketClient * wsc = nullptr;
if (!request) { //not HTTP, use Websockets
#ifdef WLED_ENABLE_WEBSOCKETS
wsc = ws.client(wsClient);
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
#endif
}
#endif
uint16_t used = strip.getLengthTotal();
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
@@ -894,7 +902,11 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
for (uint16_t i= 0; i < used; i += n)
{
olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i) & 0xFFFFFF);
uint32_t c = strip.getPixelColor(i);
uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map
uint8_t g = qadd8(W(c), G(c));
uint8_t b = qadd8(W(c), B(c));
olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0));
}
olen -= 1;
oappend((const char*)F("],\"n\":"));
@@ -910,3 +922,4 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
#endif
return true;
}
#endif

View File

@@ -3,9 +3,9 @@
/*
* LED methods
*/
void setValuesFromMainSeg()
void setValuesFromFirstSelectedSeg()
{
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
WS2812FX::Segment& seg = strip.getFirstSelectedSeg();
colorFromUint32(seg.colors[0]);
colorFromUint32(seg.colors[1], true);
effectCurrent = seg.mode;
@@ -15,6 +15,35 @@ void setValuesFromMainSeg()
}
// applies global legacy values (col, colSec, effectCurrent...)
// problem: if the first selected segment already has the value to be set, other selected segments are not updated
void applyValuesToSelectedSegs()
{
// copy of first selected segment to tell if value was updated
uint8_t firstSel = strip.getFirstSelectedSegId();
WS2812FX::Segment selsegPrev = strip.getSegment(firstSel);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue;
if (effectSpeed != selsegPrev.speed) {
seg.speed = effectSpeed; stateChanged = true;}
if (effectIntensity != selsegPrev.intensity) {
seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {
seg.palette = effectPalette; stateChanged = true;}
if (effectCurrent != selsegPrev.mode) {
strip.setMode(i, effectCurrent); stateChanged = true;}
uint32_t col0 = RGBW32(col[0],col[1],col[2],col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {
seg.setColor(0, col0, i); stateChanged = true;}
if (col1 != selsegPrev.colors[1]) {
seg.setColor(1, col1, i); stateChanged = true;}
}
}
void resetTimebase()
{
strip.timebase = 0 - millis();
@@ -43,9 +72,8 @@ byte scaledBri(byte in)
}
void setAllLeds() {
strip.setColor(0, col[0], col[1], col[2], col[3]);
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]);
//applies global brightness
void applyBri() {
if (!realtimeMode || !arlsForceMaxBri)
{
strip.setBrightness(scaledBri(briT));
@@ -53,113 +81,72 @@ void setAllLeds() {
}
void setLedsStandard()
{
//applies global brightness and sets it as the "current" brightness (no transition)
void applyFinalBri() {
briOld = bri;
briT = bri;
setAllLeds();
applyBri();
}
bool colorChanged()
{
for (byte i=0; i<4; i++)
{
if (col[i] != colIT[i]) return true;
if (colSec[i] != colSecIT[i]) return true;
}
if (bri != briIT) return true;
return false;
}
void colorUpdated(int callMode)
{
//called after every state changes, schedules interface updates, handles brightness transition and nightlight activation
//unlike colorUpdated(), does NOT apply any colors or FX to segments
void stateUpdated(byte callMode) {
//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
if (callMode != CALL_MODE_INIT &&
callMode != CALL_MODE_DIRECT_CHANGE &&
callMode != CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset
setValuesFromFirstSelectedSeg();
bool someSel = false;
if (callMode == CALL_MODE_NOTIFICATION) {
someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
}
//Notifier: apply received FX to selected segments only if actually receiving FX
if (someSel) strip.applyToAllSelected = receiveNotificationEffects;
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette) || effectChanged;
bool colChanged = colorChanged();
//Notifier: apply received color to selected segments only if actually receiving color
if (someSel) strip.applyToAllSelected = receiveNotificationColor;
if (fxChanged || colChanged)
{
effectChanged = false;
if (bri != briOld || stateChanged) {
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
currentPreset = 0; //something changed, so we are no longer in the preset
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
notify(callMode);
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
//set flag to update blynk, ws and mqtt
interfaceUpdateCallMode = callMode;
stateChanged = false;
} else {
if (nightlightActive && !nightlightActiveOld &&
callMode != CALL_MODE_NOTIFICATION &&
callMode != CALL_MODE_NO_NOTIFY)
{
if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
notify(CALL_MODE_NIGHTLIGHT);
interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT;
}
}
if (!colChanged) return; //following code is for e.g. initiating transitions
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE))
{
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) {
briNlT = bri;
nightlightDelayMs -= (millis() - nightlightStartTime);
nightlightStartTime = millis();
}
for (byte i=0; i<4; i++)
{
colIT[i] = col[i];
colSecIT[i] = colSec[i];
}
if (briT == 0)
{
if (briT == 0) {
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
}
briIT = bri;
if (bri > 0) briLast = bri;
//deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false;
if (fadeTransition)
{
if (fadeTransition) {
//set correct delay if not using notification delay
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp);
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;}
if (transitionActive)
{
if (transitionDelayTemp == 0) {
applyFinalBri();
strip.trigger();
return;
}
if (transitionActive) {
briOld = briT;
tperLast = 0;
}
strip.setTransitionMode(true);
transitionActive = true;
transitionStartTime = millis();
} else
{
} else {
strip.setTransition(0);
setLedsStandard();
applyFinalBri();
strip.trigger();
}
}
@@ -168,10 +155,8 @@ void colorUpdated(int callMode)
void updateInterfaces(uint8_t callMode)
{
sendDataWs();
if (callMode == CALL_MODE_WS_SEND) {
lastInterfaceUpdate = millis();
return;
}
lastInterfaceUpdate = millis();
if (callMode == CALL_MODE_WS_SEND) return;
#ifndef WLED_DISABLE_ALEXA
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
@@ -184,14 +169,13 @@ void updateInterfaces(uint8_t callMode)
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
#endif
doPublishMqtt = true;
lastInterfaceUpdate = millis();
}
void handleTransitions()
{
//handle still pending interface update
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > 2000)
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN)
{
updateInterfaces(interfaceUpdateCallMode);
interfaceUpdateCallMode = 0; //disable
@@ -206,20 +190,34 @@ void handleTransitions()
strip.setTransitionMode(false);
transitionActive = false;
tperLast = 0;
setLedsStandard();
applyFinalBri();
return;
}
if (tper - tperLast < 0.004) return;
tperLast = tper;
briT = briOld +((bri - briOld )*tper);
setAllLeds();
applyBri();
}
}
//legacy method, applies values from col, effectCurrent, ... to selected segments
void colorUpdated(byte callMode){
applyValuesToSelectedSegs();
stateUpdated(callMode);
}
void handleNightlight()
{
/*
static unsigned long lastNlUpdate;
unsigned long now = millis();
if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; //take care of millis() rollover
if (now - lastNlUpdate < 100) return; //allow only 10 NL updates per second
lastNlUpdate = now;
*/
if (nightlightActive)
{
if (!nightlightActiveOld) //init
@@ -236,7 +234,7 @@ void handleNightlight()
colNlT[1] = effectSpeed;
colNlT[2] = effectPalette;
strip.setMode(strip.getMainSegmentId(), FX_MODE_STATIC); //make sure seg runtime is reset if left in sunrise mode
strip.setMode(strip.getFirstSelectedSegId(), FX_MODE_STATIC); // make sure seg runtime is reset if it was in sunrise mode
effectCurrent = FX_MODE_SUNRISE;
effectSpeed = nightlightDelayMins;
effectPalette = 0;
@@ -273,7 +271,7 @@ void handleNightlight()
effectSpeed = colNlT[1];
effectPalette = colNlT[2];
toggleOnOff();
setLedsStandard();
applyFinalBri();
}
}
#ifndef WLED_DISABLE_BLYNK

View File

@@ -9,13 +9,13 @@
void parseMQTTBriPayload(char* payload)
{
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; colorUpdated(1);}
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); colorUpdated(1);}
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(1);}
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(1);}
else {
uint8_t in = strtoul(payload, NULL, 10);
if (in == 0 && bri > 0) briLast = bri;
bri = in;
colorUpdated(CALL_MODE_DIRECT_CHANGE);
stateUpdated(CALL_MODE_DIRECT_CHANGE);
}
}

View File

@@ -13,8 +13,8 @@
* DO NOT make changes to the "my_config_sample.h" file directly! Your changes will not be applied.
*/
// force the compiler to show a warning to confirm that this file is included
#warning **** my_config.h: Settings from this file are honored ****
// uncomment to force the compiler to show a warning to confirm that this file is included
//#warning **** my_config.h: Settings from this file are honored ****
/* Uncomment to use your WIFI settings as defaults
//WARNING: this will hardcode these as the default even after a factory reset

View File

@@ -318,6 +318,32 @@ byte weekdayMondayFirst()
return wd;
}
bool isTodayInDateRange(byte monthStart, byte dayStart, byte monthEnd, byte dayEnd)
{
if (monthStart == 0 || dayStart == 0) return true;
if (monthEnd == 0) monthEnd = monthStart;
if (dayEnd == 0) dayEnd = 31;
byte d = day(localTime);
byte m = month(localTime);
if (monthStart < monthEnd) {
if (m > monthStart && m < monthEnd) return true;
if (m == monthStart) return (d >= dayStart);
if (m == monthEnd) return (d <= dayEnd);
return false;
}
if (monthEnd < monthStart) { //range spans change of year
if (m > monthStart || m < monthEnd) return true;
if (m == monthStart) return (d >= dayStart);
if (m == monthEnd) return (d <= dayEnd);
return false;
}
//start month and end month are the same
if (dayEnd < dayStart) return (m != monthStart || (d <= dayEnd || d >= dayStart)); //all year, except the designated days in this month
return (m == monthStart && d >= dayStart && d <= dayEnd); //just the designated days this month
}
void checkTimers()
{
if (lastTimerMinute != minute(localTime)) //only check once a new minute begins
@@ -331,11 +357,14 @@ void checkTimers()
for (uint8_t i = 0; i < 8; i++)
{
if (timerMacro[i] != 0
&& (timerWeekday[i] & 0x01) //timer is enabled
&& (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour
&& timerMinutes[i] == minute(localTime)
&& (timerWeekday[i] & 0x01) //timer is enabled
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01) //timer should activate at current day of week
&& isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])
)
{
unloadPlaylist();
applyPreset(timerMacro[i]);
}
}
@@ -349,6 +378,7 @@ void checkTimers()
&& (timerWeekday[8] & 0x01) //timer is enabled
&& ((timerWeekday[8] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
{
unloadPlaylist();
applyPreset(timerMacro[8]);
DEBUG_PRINTF("Sunrise macro %d triggered.",timerMacro[8]);
}
@@ -363,6 +393,7 @@ void checkTimers()
&& (timerWeekday[9] & 0x01) //timer is enabled
&& ((timerWeekday[9] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
{
unloadPlaylist();
applyPreset(timerMacro[9]);
DEBUG_PRINTF("Sunset macro %d triggered.",timerMacro[9]);
}

View File

@@ -1,6 +1,7 @@
#include "pin_manager.h"
#include "wled.h"
#ifdef WLED_DEBUG
static void DebugPrintOwnerTag(PinOwner tag)
{
uint32_t q = static_cast<uint8_t>(tag);
@@ -10,6 +11,7 @@ static void DebugPrintOwnerTag(PinOwner tag)
DEBUG_PRINT(F("(no owner)"));
}
}
#endif
/// Actual allocation/deallocation routines
bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
@@ -19,12 +21,14 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
// if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided
if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN DEALLOC: IO "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINT(F(", but attempted de-allocation by "));
DebugPrintOwnerTag(tag);
#endif
return false;
}
@@ -34,6 +38,49 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
ownerTag[gpio] = PinOwner::None;
return true;
}
// support function for deallocating multiple pins
bool PinManagerClass::deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag)
{
bool shouldFail = false;
DEBUG_PRINTLN(F("MULTIPIN DEALLOC"));
// first verify the pins are OK and allocated by selected owner
for (int i = 0; i < arrayElementCount; i++) {
byte gpio = pinArray[i];
if (gpio == 0xFF) {
// explicit support for io -1 as a no-op (no allocation of pin),
// as this can greatly simplify configuration arrays
continue;
}
if (isPinAllocated(gpio, tag)) {
// if the current pin is allocated by selected owner it is possible to release it
continue;
}
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN DEALLOC: IO "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINT(F(", but attempted de-allocation by "));
DebugPrintOwnerTag(tag);
#endif
shouldFail = true;
}
if (shouldFail) {
return false; // no pins deallocated
}
if (tag==PinOwner::HW_I2C) {
if (i2cAllocCount && --i2cAllocCount>0) {
// no deallocation done until last owner releases pins
return true;
}
}
for (int i = 0; i < arrayElementCount; i++) {
deallocatePin(pinArray[i], tag);
}
return true;
}
bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{
bool shouldFail = false;
@@ -46,17 +93,24 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
continue;
}
if (!isPinOk(gpio, mptArray[i].isOutput)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: "));
DEBUG_PRINT(gpio);
DEBUG_PRINTLN(F(""));
#endif
shouldFail = true;
}
if (isPinAllocated(gpio)) {
if (tag==PinOwner::HW_I2C && isPinAllocated(gpio, tag)) {
// allow multiple "allocations" of HW I2C bus pins
continue;
} else if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: FAIL: IO "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F(""));
#endif
shouldFail = true;
}
}
@@ -64,6 +118,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
return false;
}
if (tag==PinOwner::HW_I2C) i2cAllocCount++;
// all pins are available .. track each one
for (int i = 0; i < arrayElementCount; i++) {
byte gpio = mptArray[i].pin;
@@ -76,18 +132,29 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, true);
ownerTag[gpio] = tag;
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(tag);
DEBUG_PRINTLN(F(""));
#endif
}
return true;
}
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
{
if (!isPinOk(gpio, output)) return false;
// HW I2C pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C) return false;
if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F(""));
#endif
return false;
}
@@ -95,7 +162,14 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, true);
ownerTag[gpio] = tag;
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(tag);
DEBUG_PRINTLN(F(""));
#endif
return true;
}
@@ -106,7 +180,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
if (!isPinOk(gpio, false)) return true;
if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false;
byte by = gpio >> 3;
byte bi = gpio - 8*by;
byte bi = gpio - (by<<3);
return bitRead(pinAlloc[by], bi);
}

View File

@@ -7,8 +7,8 @@
#include "const.h" // for USERMOD_* values
typedef struct PinManagerPinType {
int8_t pin;
uint8_t isOutput;
int8_t pin;
bool isOutput;
} managed_pin_type;
/*
@@ -21,9 +21,8 @@ typedef struct PinManagerPinType {
* 40 bytes on ESP32
*/
enum struct PinOwner : uint8_t {
None = 0, // default == legacy == unspecified owner
None = 0, // default == legacy == unspecified owner
// High bit is set for all built-in pin owners
// StatusLED -- THIS SHOULD NEVER BE ALLOCATED -- see handleStatusLED()
Ethernet = 0x81,
BusDigital = 0x82,
BusDigital2 = 0x83,
@@ -34,26 +33,27 @@ enum struct PinOwner : uint8_t {
SPI_RAM = 0x88, // 'SpiR' == SPI RAM
DebugOut = 0x89, // 'Dbg' == debug output always IO1
DMX = 0x8A, // 'DMX' == hard-coded to IO2
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
// Use UserMod IDs from const.h here
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.h"
// #define USERMOD_ID_FIXNETSERVICES // 0x04 // Usermod "usermod_Fix_unreachable_netservices.h" -- Does not allocate pins
UM_PIR = USERMOD_ID_PIRSWITCH, // 0x05 // Usermod "usermod_PIR_sensor_switch.h"
// #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h
// #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" HW_I2C pins
UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h -- May use "standard" HW_I2C pins
UM_RotaryEncoderUI = USERMOD_ID_ROTARY_ENC_UI, // 0x08 // Usermod "usermod_v2_rotary_encoder_ui.h"
// #define USERMOD_ID_AUTO_SAVE // 0x09 // Usermod "usermod_v2_auto_save.h" -- Does not allocate pins
// #define USERMOD_ID_DHT // 0x0A // Usermod "usermod_dht.h" -- Statically allocates pins, not compatible with pinManager?
// #define USERMOD_ID_MODE_SORT // 0x0B // Usermod "usermod_v2_mode_sort.h" -- Does not allocate pins
// #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
// #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" HW_I2C pins
UM_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h"
UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h"
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h"
};
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
@@ -67,10 +67,13 @@ class PinManagerClass {
uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels
PinOwner ownerTag[40] = { PinOwner::None };
#endif
uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations
public:
// De-allocates a single pin
bool deallocatePin(byte gpio, PinOwner tag);
// De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified)
bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag);
// Allocates a single pin, with an owner tag.
// De-allocation requires the same owner tag (or override)
bool allocatePin(byte gpio, bool output, PinOwner tag);
@@ -85,11 +88,13 @@ class PinManagerClass {
#endif
inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
#if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute
[[deprecated("Replaced by three-parameter deallocatePin(gpio, output, ownerTag), for improved debugging")]]
[[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]]
#endif
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
// will return true for reserved pins
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
// will return false for reserved pins
bool isPinOk(byte gpio, bool output = true);
PinOwner getPinOwner(byte gpio);

View File

@@ -107,7 +107,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
playlistRepeat = rep;
if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start
playlistEndPreset = playlistObj[F("end")] | 0;
playlistEndPreset = playlistObj["end"] | 0;
shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;
@@ -119,7 +119,8 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
void handlePlaylist() {
static unsigned long presetCycledTime = 0;
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
// if fileDoc is not null JSON buffer is in use so just quit
if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return;
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
presetCycledTime = millis();

View File

@@ -10,7 +10,15 @@ bool applyPreset(byte index, byte callMode)
const char *filename = index < 255 ? "/presets.json" : "/tmp.json";
if (fileDoc) {
uint8_t core = 1;
//crude way to determine if this was called by a network request
#ifdef ARDUINO_ARCH_ESP32
core = xPortGetCoreID();
#endif
//only allow use of fileDoc from the core responsible for network requests
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
if (fileDoc && core) {
errorFlag = readObjectFromFileUsingId(filename, index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fileDoc->as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash
@@ -45,6 +53,7 @@ bool applyPreset(byte index, byte callMode)
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
{
if (index == 0 || (index > 250 && persist) || (index<255 && !persist)) return;
char tmp[12];
JsonObject sObj = saveobj;
const char *filename = persist ? "/presets.json" : "/tmp.json";
@@ -57,7 +66,11 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
if (!requestJSONBufferLock(10)) return;
#endif
sObj = doc.to<JsonObject>();
if (pname) sObj["n"] = pname;
if (sObj["n"].isNull() && pname == nullptr) {
sprintf_P(tmp, PSTR("Preset %d"), index);
sObj["n"] = tmp;
} else if (pname) sObj["n"] = pname;
DEBUGFS_PRINTLN(F("Save current state"));
serializeState(sObj, true);

View File

@@ -3,19 +3,6 @@
/*
* Receives client input
*/
void _setRandomColor(bool _sec,bool fromButton)
{
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex);
if (_sec){
colorHStoRGB(lastRandomIndex*256,255,colSec);
} else {
colorHStoRGB(lastRandomIndex*256,255,col);
}
if (fromButton) colorUpdated(2);
}
bool isAsterisksOnly(const char* str, byte maxLen)
{
for (byte i = 0; i < maxLen; i++) {
@@ -100,6 +87,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strip.cctBlending = request->arg(F("CB")).toInt();
Bus::setCCTBlend(strip.cctBlending);
Bus::setAutoWhiteMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt());
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
@@ -135,6 +123,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
doInitBusses = true;
}
ColorOrderMap com = {};
for (uint8_t s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
char xs[4] = "XS"; xs[2] = 48+s; xs[3] = 0; //start LED
char xc[4] = "XC"; xc[2] = 48+s; xc[3] = 0; //strip length
char xo[4] = "XO"; xo[2] = 48+s; xo[3] = 0; //color order
if (request->hasArg(xs)) {
start = request->arg(xs).toInt();
length = request->arg(xc).toInt();
colorOrder = request->arg(xo).toInt();
com.add(start, length, colorOrder);
}
}
busses.updateColorOrderMap(com);
// upate other pins
int hw_ir_pin = request->arg(F("IR")).toInt();
if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) {
@@ -153,8 +155,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
rlyMde = (bool)request->hasArg(F("RM"));
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
char bt[4] = "BT"; bt[2] = 48+i; bt[3] = 0; // button pin
char be[4] = "BE"; be[2] = 48+i; be[3] = 0; // button type
char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
int hw_btn_pin = request->arg(bt).toInt();
if (pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) {
btnPin[i] = hw_btn_pin;
@@ -217,7 +219,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
receiveNotificationBrightness = request->hasArg(F("RB"));
receiveNotificationColor = request->hasArg(F("RC"));
receiveNotificationEffects = request->hasArg(F("RX"));
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
receiveSegmentOptions = request->hasArg(F("SO"));
receiveSegmentBounds = request->hasArg(F("SG"));
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
notifyDirectDefault = request->hasArg(F("SD"));
notifyDirect = notifyDirectDefault;
notifyButton = request->hasArg(F("SB"));
@@ -293,6 +297,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
hueStoreAllowed = true;
reconnectHue();
#endif
t = request->arg(F("BD")).toInt();
if (t >= 96 && t <= 15000) serialBaud = t;
updateBaudRate(serialBaud *100);
}
//TIME
@@ -306,6 +314,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//start ntp if not already connected
if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort);
ntpLastSyncTime = 0; // force new NTP query
longitude = request->arg(F("LN")).toFloat();
latitude = request->arg(F("LT")).toFloat();
@@ -341,9 +350,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
macroCountdown = request->arg(F("MC")).toInt();
macroNl = request->arg(F("MN")).toInt();
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
char mp[4] = "MP"; mp[2] = 48+i; mp[3] = 0; // short
char ml[4] = "ML"; ml[2] = 48+i; ml[3] = 0; // long
char md[4] = "MD"; md[2] = 48+i; md[3] = 0; // double
char mp[4] = "MP"; mp[2] = (i<10?48:55)+i; mp[3] = 0; // short
char ml[4] = "ML"; ml[2] = (i<10?48:55)+i; ml[3] = 0; // long
char md[4] = "MD"; md[2] = (i<10?48:55)+i; md[3] = 0; // double
//if (!request->hasArg(mp)) break;
macroButton[i] = request->arg(mp).toInt(); // these will default to 0 if not present
macroLongPress[i] = request->arg(ml).toInt();
@@ -351,21 +360,27 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
char k[3]; k[2] = 0;
for (int i = 0; i<10; i++)
{
k[1] = i+48;//ascii 0,1,2,3
for (int i = 0; i<10; i++) {
k[1] = i+48;//ascii 0,1,2,3,...
k[0] = 'H'; //timer hours
timerHours[i] = request->arg(k).toInt();
k[0] = 'N'; //minutes
timerMinutes[i] = request->arg(k).toInt();
k[0] = 'T'; //macros
timerMacro[i] = request->arg(k).toInt();
k[0] = 'W'; //weekdays
timerWeekday[i] = request->arg(k).toInt();
if (i<8) {
k[0] = 'M'; //start month
timerMonth[i] = request->arg(k).toInt() & 0x0F;
timerMonth[i] <<= 4;
k[0] = 'P'; //end month
timerMonth[i] += (request->arg(k).toInt() & 0x0F);
k[0] = 'D'; //start day
timerDay[i] = request->arg(k).toInt();
k[0] = 'E'; //end day
timerDayEnd[i] = request->arg(k).toInt();
}
}
}
@@ -442,7 +457,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
JsonObject um = doc.createNestedObject("um");
size_t args = request->args();
uint j=0;
uint16_t j=0;
for (size_t i=0; i<args; i++) {
String name = request->argName(i);
String value = request->arg(i);
@@ -476,12 +491,15 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
// check if parameters represent array
if (name.endsWith("[]")) {
name.replace("[]","");
value.replace(",","."); // just in case conversion
if (!subObj[name].is<JsonArray>()) {
JsonArray ar = subObj.createNestedArray(name);
ar.add(value.toInt());
if (value.indexOf(".") >= 0) ar.add(value.toFloat()); // we do have a float
else ar.add(value.toInt()); // we may have an int
j=0;
} else {
subObj[name].add(value.toInt());
if (value.indexOf(".") >= 0) subObj[name].add(value.toFloat()); // we do have a float
else subObj[name].add(value.toInt()); // we may have an int
j++;
}
DEBUG_PRINT("[");
@@ -511,9 +529,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
}
usermods.readFromConfig(um); // force change of usermod parameters
}
releaseJSONBufferLock();
releaseJSONBufferLock();
}
if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
if (subPage == 4) alexaInit();
@@ -585,36 +603,43 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
DEBUG_PRINT(F("API req: "));
DEBUG_PRINTLN(req);
strip.applyToAllSelected = false;
//segment select (sets main segment)
byte prevMain = strip.getMainSegmentId();
pos = req.indexOf(F("SM="));
if (pos > 0) {
strip.mainSegment = getNumVal(&req, pos);
strip.setMainSegmentId(getNumVal(&req, pos));
}
byte selectedSeg = strip.getMainSegmentId();
if (selectedSeg != prevMain) setValuesFromMainSeg();
byte selectedSeg = strip.getFirstSelectedSegId();
bool singleSegment = false;
pos = req.indexOf(F("SS="));
if (pos > 0) {
byte t = getNumVal(&req, pos);
if (t < strip.getMaxSegments()) selectedSeg = t;
if (t < strip.getMaxSegments()) {
selectedSeg = t;
singleSegment = true;
}
}
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) {
byte t = getNumVal(&req, pos);
if (t == 2) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0);
}
}
if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
selseg.setOption(SEG_OPTION_SELECTED, t);
}
// temporary values, write directly to segments, globals are updated by setValuesFromFirstSelectedSeg()
uint32_t col0 = selseg.colors[0];
uint32_t col1 = selseg.colors[1];
byte colIn[4] = {R(col0), G(col0), B(col0), W(col0)};
byte colInSec[4] = {R(col1), G(col1), B(col1), W(col1)};
byte effectIn = selseg.mode;
byte speedIn = selseg.speed;
byte intensityIn = selseg.intensity;
byte paletteIn = selseg.palette;
uint16_t startI = selseg.start;
uint16_t stopI = selseg.stop;
uint8_t grpI = selseg.grouping;
@@ -677,43 +702,39 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
applyPreset(presetCycCurr);
}
//snapshot to check if request changed values later, temporary.
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
byte prevEffect = effectCurrent;
byte prevSpeed = effectSpeed;
byte prevIntensity = effectIntensity;
byte prevPalette = effectPalette;
//set brightness
updateVal(&req, "&A=", &bri);
bool col0Changed = false, col1Changed = false;
//set colors
updateVal(&req, "&R=", &col[0]);
updateVal(&req, "&G=", &col[1]);
updateVal(&req, "&B=", &col[2]);
updateVal(&req, "&W=", &col[3]);
updateVal(&req, "R2=", &colSec[0]);
updateVal(&req, "G2=", &colSec[1]);
updateVal(&req, "B2=", &colSec[2]);
updateVal(&req, "W2=", &colSec[3]);
col0Changed |= updateVal(&req, "&R=", &colIn[0]);
col0Changed |= updateVal(&req, "&G=", &colIn[1]);
col0Changed |= updateVal(&req, "&B=", &colIn[2]);
col0Changed |= updateVal(&req, "&W=", &colIn[3]);
col1Changed |= updateVal(&req, "R2=", &colInSec[0]);
col1Changed |= updateVal(&req, "G2=", &colInSec[1]);
col1Changed |= updateVal(&req, "B2=", &colInSec[2]);
col1Changed |= updateVal(&req, "W2=", &colInSec[3]);
#ifdef WLED_ENABLE_LOXONE
//lox parser
pos = req.indexOf(F("LX=")); // Lox primary color
if (pos > 0) {
int lxValue = getNumVal(&req, pos);
if (parseLx(lxValue, col)) {
if (parseLx(lxValue, colIn)) {
bri = 255;
nightlightActive = false; //always disable nightlight when toggling
col0Changed = true;
}
}
pos = req.indexOf(F("LY=")); // Lox secondary color
if (pos > 0) {
int lxValue = getNumVal(&req, pos);
if(parseLx(lxValue, colSec)) {
if(parseLx(lxValue, colInSec)) {
bri = 255;
nightlightActive = false; //always disable nightlight when toggling
col1Changed = true;
}
}
#endif
@@ -727,59 +748,95 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
tempsat = getNumVal(&req, pos);
}
colorHStoRGB(temphue,tempsat,(req.indexOf(F("H2"))>0)? colSec:col);
byte sec = req.indexOf(F("H2"));
colorHStoRGB(temphue, tempsat, (sec>0) ? colInSec : colIn);
col0Changed |= (!sec); col1Changed |= sec;
}
//set white spectrum (kelvin)
pos = req.indexOf(F("&K="));
if (pos > 0) {
colorKtoRGB(getNumVal(&req, pos),(req.indexOf(F("K2"))>0)? colSec:col);
byte sec = req.indexOf(F("K2"));
colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colInSec : colIn);
col0Changed |= (!sec); col1Changed |= sec;
}
//set color from HEX or 32bit DEC
byte tmpCol[4];
pos = req.indexOf(F("CL="));
if (pos > 0) {
colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str());
colorFromDecOrHexString(colIn, (char*)req.substring(pos + 3).c_str());
col0Changed = true;
}
pos = req.indexOf(F("C2="));
if (pos > 0) {
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str());
colorFromDecOrHexString(colInSec, (char*)req.substring(pos + 3).c_str());
col1Changed = true;
}
pos = req.indexOf(F("C3="));
if (pos > 0) {
byte t[4];
colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str());
if (selectedSeg != strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(2, t[0], t[1], t[2], t[3]);
} else {
selseg.setColor(2,((t[0] << 16) + (t[1] << 8) + t[2] + (t[3] << 24)), selectedSeg); // defined above (SS=)
}
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
selseg.setColor(2, col2, selectedSeg); // defined above (SS= or main)
stateChanged = true;
if (!singleSegment) strip.setColor(2, col2);
}
//set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf(F("SR"));
if (pos > 0) {
_setRandomColor(getNumVal(&req, pos));
byte sec = getNumVal(&req, pos);
setRandomColor(sec? colInSec : colIn);
col0Changed |= (!sec); col1Changed |= sec;
}
//swap 2nd & 1st
pos = req.indexOf(F("SC"));
if (pos > 0) {
byte temp;
for (uint8_t i=0; i<4; i++)
{
temp = col[i];
col[i] = colSec[i];
colSec[i] = temp;
for (uint8_t i=0; i<4; i++) {
temp = colIn[i];
colIn[i] = colInSec[i];
colInSec[i] = temp;
}
col0Changed = col1Changed = true;
}
//set effect parameters
if (updateVal(&req, "FX=", &effectCurrent, 0, strip.getModeCount()-1) && request != nullptr) unloadPlaylist(); //unload playlist if changing FX using web request
updateVal(&req, "SX=", &effectSpeed);
updateVal(&req, "IX=", &effectIntensity);
updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1);
// apply colors to selected segment, and all selected segments if applicable
if (col0Changed) {
stateChanged = true;
uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]);
selseg.setColor(0, colIn0, selectedSeg);
if (!singleSegment) strip.setColor(0, colIn0);
}
if (col1Changed) {
stateChanged = true;
uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]);
selseg.setColor(1, colIn1, selectedSeg);
if (!singleSegment) strip.setColor(1, colIn1);
}
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
// set effect parameters
if (updateVal(&req, "FX=", &effectIn, 0, strip.getModeCount()-1)) {
if (request != nullptr) unloadPlaylist(); // unload playlist if changing FX using web request
fxModeChanged = true;
}
speedChanged = updateVal(&req, "SX=", &speedIn);
intensityChanged = updateVal(&req, "IX=", &intensityIn);
paletteChanged = updateVal(&req, "FP=", &paletteIn, 0, strip.getPaletteCount()-1);
stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue;
if (fxModeChanged) strip.setMode(i, effectIn);
if (speedChanged) seg.speed = speedIn;
if (intensityChanged) seg.intensity = intensityIn;
if (paletteChanged) seg.palette = paletteIn;
}
//set advanced overlay
pos = req.indexOf(F("OL="));
@@ -907,60 +964,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
userVar1 = getNumVal(&req, pos);
}
//you can add more if you need
// you can add more if you need
//apply to all selected manually to prevent #1618. Temporary
bool col0Changed = false, col1Changed = false;
for (uint8_t i = 0; i < 4; i++) {
if (col[i] != prevCol[i]) col0Changed = true;
if (colSec[i] != prevColSec[i]) col1Changed = true;
}
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue;
if (effectCurrent != prevEffect) {
strip.setMode(i, effectCurrent);
effectChanged = true;
}
if (effectSpeed != prevSpeed) {
seg.speed = effectSpeed;
effectChanged = true;
}
if (effectIntensity != prevIntensity) {
seg.intensity = effectIntensity;
effectChanged = true;
}
if (effectPalette != prevPalette) {
seg.palette = effectPalette;
effectChanged = true;
}
}
// global col[], effectCurrent, ... are updated in stateChanged()
if (!apply) return true; // when called by JSON API, do not call colorUpdated() here
if (col0Changed) {
if (selectedSeg == strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(0, colorFromRgbw(col));
}
}
if (col1Changed) {
if (selectedSeg == strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(1, colorFromRgbw(colSec));
}
}
//end of temporary fix code
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
stateUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
//internal call, does not send XML response
// internal call, does not send XML response
pos = req.indexOf(F("IN"));
if (pos < 1) XML_response(request);
strip.applyToAllSelected = false;
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);
return true;
}

View File

@@ -4,7 +4,9 @@
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
*/
#define WLEDPACKETSIZE 39
#define UDP_SEG_SIZE 28
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
#define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
@@ -17,6 +19,7 @@ void notify(byte callMode, bool followUp)
case CALL_MODE_INIT: return;
case CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break;
case CALL_MODE_BUTTON: if (!notifyButton) return; break;
case CALL_MODE_BUTTON_PRESET: if (!notifyButton) return; break;
case CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break;
case CALL_MODE_HUE: if (!notifyHue) return; break;
case CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break;
@@ -25,37 +28,39 @@ void notify(byte callMode, bool followUp)
default: return;
}
byte udpOut[WLEDPACKETSIZE];
WS2812FX::Segment& mainseg = strip.getSegment(strip.getMainSegmentId());
WS2812FX::Segment& mainseg = strip.getMainSegment();
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
udpOut[1] = callMode;
udpOut[2] = bri;
udpOut[3] = col[0];
udpOut[4] = col[1];
udpOut[5] = col[2];
uint32_t col = mainseg.colors[0];
udpOut[3] = R(col);
udpOut[4] = G(col);
udpOut[5] = B(col);
udpOut[6] = nightlightActive;
udpOut[7] = nightlightDelayMins;
udpOut[8] = effectCurrent;
udpOut[9] = effectSpeed;
udpOut[10] = col[3];
udpOut[8] = mainseg.mode;
udpOut[9] = mainseg.speed;
udpOut[10] = W(col);
//compatibilityVersionByte:
//0: old 1: supports white 2: supports secondary color
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet
udpOut[11] = 10;
udpOut[12] = colSec[0];
udpOut[13] = colSec[1];
udpOut[14] = colSec[2];
udpOut[15] = colSec[3];
udpOut[16] = effectIntensity;
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet 11: per segment options, variable packet length (40+MAX_NUM_SEGMENTS*3)
udpOut[11] = 11;
col = mainseg.colors[1];
udpOut[12] = R(col);
udpOut[13] = G(col);
udpOut[14] = B(col);
udpOut[15] = W(col);
udpOut[16] = mainseg.intensity;
udpOut[17] = (transitionDelay >> 0) & 0xFF;
udpOut[18] = (transitionDelay >> 8) & 0xFF;
udpOut[19] = effectPalette;
uint32_t colTer = mainseg.colors[2];
udpOut[20] = (colTer >> 16) & 0xFF;
udpOut[21] = (colTer >> 8) & 0xFF;
udpOut[22] = (colTer >> 0) & 0xFF;
udpOut[23] = (colTer >> 24) & 0xFF;
udpOut[19] = mainseg.palette;
col = mainseg.colors[2];
udpOut[20] = R(col);
udpOut[21] = G(col);
udpOut[22] = B(col);
udpOut[23] = W(col);
udpOut[24] = followUp;
uint32_t t = millis() + strip.timebase;
@@ -78,12 +83,50 @@ void notify(byte callMode, bool followUp)
//sync groups
udpOut[36] = syncGroups;
//Might be changed to Kelvin in the future, receiver code should handle that case
//0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
udpOut[38] = mainseg.cct;
//Might be changed to Kelvin in the future, receiver code should handle that case
//0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
udpOut[38] = mainseg.cct;
udpOut[39] = strip.getMaxSegments();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i);
uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[0 +ofs] = i;
udpOut[1 +ofs] = selseg.start >> 8;
udpOut[2 +ofs] = selseg.start & 0xFF;
udpOut[3 +ofs] = selseg.stop >> 8;
udpOut[4 +ofs] = selseg.stop & 0xFF;
udpOut[5 +ofs] = selseg.grouping;
udpOut[6 +ofs] = selseg.spacing;
udpOut[7 +ofs] = selseg.offset >> 8;
udpOut[8 +ofs] = selseg.offset & 0xFF;
udpOut[9 +ofs] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed
udpOut[10+ofs] = selseg.opacity;
udpOut[11+ofs] = selseg.mode;
udpOut[12+ofs] = selseg.speed;
udpOut[13+ofs] = selseg.intensity;
udpOut[14+ofs] = selseg.palette;
udpOut[15+ofs] = R(selseg.colors[0]);
udpOut[16+ofs] = G(selseg.colors[0]);
udpOut[17+ofs] = B(selseg.colors[0]);
udpOut[18+ofs] = W(selseg.colors[0]);
udpOut[19+ofs] = R(selseg.colors[1]);
udpOut[20+ofs] = G(selseg.colors[1]);
udpOut[21+ofs] = B(selseg.colors[1]);
udpOut[22+ofs] = W(selseg.colors[1]);
udpOut[23+ofs] = R(selseg.colors[2]);
udpOut[24+ofs] = G(selseg.colors[2]);
udpOut[25+ofs] = B(selseg.colors[2]);
udpOut[26+ofs] = W(selseg.colors[2]);
udpOut[27+ofs] = selseg.cct;
}
//uint16_t offs = SEG_OFFSET;
//next value to be added has index: udpOut[offs + 0]
IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@@ -246,47 +289,84 @@ void handleNotifications()
} else if (!(receiveGroups & udpIn[36])) return;
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification
if (receiveNotificationColor || !someSel)
{
col[0] = udpIn[3];
col[1] = udpIn[4];
col[2] = udpIn[5];
if (version > 0) //sending module's white val is intended
{
col[3] = udpIn[10];
if (version > 1)
{
colSec[0] = udpIn[12];
colSec[1] = udpIn[13];
colSec[2] = udpIn[14];
colSec[3] = udpIn[15];
//apply colors from notification to main segment, only if not syncing full segments
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
// primary color, only apply white if intented (version > 0)
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
if (version > 1) {
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
}
if (version > 6) {
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
if (version > 9 && version < 200 && udpIn[37] < 255) { // valid CCT/Kelvin value
uint8_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
}
strip.setCCT(cct);
}
if (version > 6)
{
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
}
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
uint8_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
}
uint8_t segid = strip.getMainSegmentId();
strip.getSegment(segid).setCCT(cct, segid);
}
}
}
bool timebaseUpdated = false;
//apply effects from notification
if (version < 200 && (receiveNotificationEffects || !someSel))
bool applyEffects = (receiveNotificationEffects || !someSel);
if (version < 200)
{
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
effectSpeed = udpIn[9];
if (version > 2) effectIntensity = udpIn[16];
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
if (version > 5)
{
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
uint8_t numSrcSegs = udpIn[39];
for (uint8_t i = 0; i < numSrcSegs; i++) {
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
uint8_t id = udpIn[0 +ofs];
if (id > strip.getMaxSegments()) continue;
WS2812FX::Segment& selseg = strip.getSegment(id);
uint16_t start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
uint16_t stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
if (!receiveSegmentOptions) {
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset);
continue;
}
for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed
selseg.setOpacity(udpIn[10+ofs], id);
if (applyEffects) {
strip.setMode(id, udpIn[11+ofs]);
selseg.speed = udpIn[12+ofs];
selseg.intensity = udpIn[13+ofs];
selseg.palette = udpIn[14+ofs];
}
if (receiveNotificationColor || !someSel) {
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]), id);
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]), id);
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]), id);
selseg.setCCT(udpIn[27+ofs], id);
}
//setSegment() also properly resets segments
if (receiveSegmentBounds) {
strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset);
} else {
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset);
}
}
stateChanged = true;
}
// simple effect sync, applies to all selected segments
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
if (udpIn[8] < strip.getModeCount()) strip.setMode(i, udpIn[8]);
seg.speed = udpIn[9];
if (version > 2) seg.intensity = udpIn[16];
if (version > 4 && udpIn[19] < strip.getPaletteCount()) seg.palette = udpIn[19];
}
stateChanged = true;
}
if (applyEffects && version > 5) {
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis();
@@ -328,7 +408,7 @@ void handleNotifications()
if (nightlightActive) nightlightDelayMins = udpIn[7];
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
colorUpdated(CALL_MODE_NOTIFICATION);
stateUpdated(CALL_MODE_NOTIFICATION);
return;
}

View File

@@ -100,6 +100,10 @@
#include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h"
#endif
#ifdef USERMOD_SSDR
#include "../usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h"
#endif
#ifdef QUINLED_AN_PENTA
#include "../usermods/quinled-an-penta/quinled-an-penta.h"
#endif
@@ -192,6 +196,10 @@ void registerUsermods()
usermods.add(new SevenSegmentDisplay());
#endif
#ifdef USERMOD_SSDR
usermods.add(new UsermodSSDR());
#endif
#ifdef QUINLED_AN_PENTA
usermods.add(new QuinLEDAnPentaUsermod());
#endif

View File

@@ -31,3 +31,38 @@ void releaseJSONBufferLock()
fileDoc = nullptr;
jsonBufferLock = 0;
}
// extracts effect mode (or palette) name from names serialized string
// caller must provide large enough buffer for name (incluing SR extensions)!
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen)
{
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
size_t len = strlen_P(src);
// Find the mode name in JSON
for (size_t i = 0; i < len; i++) {
singleJsonSymbol = pgm_read_byte_near(src + i);
if (singleJsonSymbol == '\0') break;
if (singleJsonSymbol == '@' && insideQuotes && qComma == mode) break; //stop when SR extension encountered
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
if (!insideQuotes) qComma++;
default:
if (!insideQuotes || (qComma != mode)) break;
dest[printedChars++] = singleJsonSymbol;
}
if ((qComma > mode) || (printedChars >= maxLen)) break;
}
dest[printedChars] = '\0';
return strlen(dest);
}

View File

@@ -27,7 +27,7 @@ void WLED::reset()
while (millis() - dly < 450) {
yield(); // enough time to send response to client
}
setAllLeds();
applyBri();
DEBUG_PRINTLN(F("MODULE RESET"));
ESP.restart();
}
@@ -177,7 +177,7 @@ void WLED::loop()
yield();
if (!offMode || strip.isOffRefreshRequred)
if (!offMode || strip.isOffRefreshRequired())
strip.service();
#ifdef ESP8266
else if (!noWifiSleep)
@@ -193,6 +193,8 @@ void WLED::loop()
if (lastMqttReconnectAttempt > millis()) {
rolloverMillis++;
lastMqttReconnectAttempt = 0;
ntpLastSyncTime = 0;
strip.restartRuntime();
}
if (millis() - lastMqttReconnectAttempt > 30000) {
lastMqttReconnectAttempt = millis();
@@ -571,7 +573,7 @@ void WLED::initConnection()
if (staticIP[0] != 0 && staticGateway[0] != 0) {
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(1, 1, 1, 1));
} else {
WiFi.config(0U, 0U, 0U);
WiFi.config(IPAddress((uint32_t)0), IPAddress((uint32_t)0), IPAddress((uint32_t)0));
}
lastReconnectAttempt = millis();
@@ -682,13 +684,15 @@ void WLED::handleConnection()
if (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS))
return;
if (lastReconnectAttempt == 0)
if (lastReconnectAttempt == 0) {
initConnection();
return;
}
// reconnect WiFi to clear stale allocations if heap gets too low
if (now - heapTime > 5000) {
uint32_t heap = ESP.getFreeHeap();
if (heap < JSON_BUFFER_SIZE+512 && lastHeap < JSON_BUFFER_SIZE+512) {
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
DEBUG_PRINT(F("Heap too low! "));
DEBUG_PRINTLN(heap);
forceReconnect = true;
@@ -800,4 +804,4 @@ void WLED::handleStatusLED()
}
#endif
}
}

View File

@@ -3,12 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.13.0-b6
@version 0.13.0-b7
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2112080
#define VERSION 2202222
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -16,6 +16,7 @@
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
// 2-step OTA may still be possible: https://github.com/Aircoookie/WLED/issues/2040#issuecomment-981111096
// Uncomment some of the following lines to disable features:
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini
@@ -33,6 +34,7 @@
#endif
#define WLED_ENABLE_ADALIGHT // saves 500b only (uses GPIO3 (RX) for serial)
//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2)
//#define WLED_ENABLE_JSONLIVE // peek LED output via /json/live (WS binary peek is always enabled)
#ifndef WLED_DISABLE_LOXONE
#define WLED_ENABLE_LOXONE // uses 1.2kb
#endif
@@ -69,11 +71,14 @@
#include "esp_wifi.h"
#include <ESPmDNS.h>
#include <AsyncTCP.h>
//#include "SPIFFS.h"
#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2
#define CONFIG_LITTLEFS_FOR_IDF_3_2
#if LOROL_LITTLEFS
#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2
#define CONFIG_LITTLEFS_FOR_IDF_3_2
#endif
#include <LITTLEFS.h>
#else
#include <LittleFS.h>
#endif
#include <LITTLEFS.h>
#endif
#include "src/dependencies/network/Network.h"
@@ -172,7 +177,11 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#ifdef ESP8266
#define WLED_FS LittleFS
#else
#define WLED_FS LITTLEFS
#if LOROL_LITTLEFS
#define WLED_FS LITTLEFS
#else
#define WLED_FS LittleFS
#endif
#endif
// GLOBAL VARIABLES
@@ -216,7 +225,7 @@ WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({0});
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({BTNPIN});
#endif
#ifndef RLYPIN
WLED_GLOBAL int8_t rlyPin _INIT(12);
WLED_GLOBAL int8_t rlyPin _INIT(-1);
#else
WLED_GLOBAL int8_t rlyPin _INIT(RLYPIN);
#endif
@@ -305,6 +314,8 @@ WLED_GLOBAL uint8_t receiveGroups _INIT(0x01); // sync receiv
WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications
WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color
WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup
WLED_GLOBAL bool receiveSegmentOptions _INIT(false); // apply segment options
WLED_GLOBAL bool receiveSegmentBounds _INIT(false); // apply segment bounds (start, stop, offset)
WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API
WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote
WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa
@@ -360,11 +371,13 @@ WLED_GLOBAL bool hueApplyBri _INIT(true);
WLED_GLOBAL bool hueApplyColor _INIT(true);
#endif
WLED_GLOBAL uint16_t serialBaud _INIT(1152); // serial baud rate, multiply by 100
// Time CONFIG
WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros
WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format
WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino
WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation
WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros
WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format
WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino
WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation
WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clock 3: cronixie
WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(DEFAULT_LED_COUNT - 1); // boundaries of overlay mode
@@ -416,9 +429,6 @@ WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false);
// color
WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs
WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 }));
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
// transitions
@@ -468,7 +478,7 @@ WLED_GLOBAL byte effectCurrent _INIT(0);
WLED_GLOBAL byte effectSpeed _INIT(128);
WLED_GLOBAL byte effectIntensity _INIT(128);
WLED_GLOBAL byte effectPalette _INIT(0);
WLED_GLOBAL bool effectChanged _INIT(false);
WLED_GLOBAL bool stateChanged _INIT(false);
// network
WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);
@@ -497,13 +507,17 @@ WLED_GLOBAL byte dP[] _INIT_N(({ 255, 255, 255, 255, 255, 255 }));
WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L);
WLED_GLOBAL bool countdownOverTriggered _INIT(true);
// timer
WLED_GLOBAL byte lastTimerMinute _INIT(0);
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
//timer
WLED_GLOBAL byte lastTimerMinute _INIT(0);
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on
// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
//weekdays to activate on, bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }));
//upper 4 bits start, lower 4 bits end month (default 28: start month 1 and end month 12)
WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28}));
WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1}));
WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31}));
// blynk
WLED_GLOBAL bool blynkEnabled _INIT(false);

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