Merge branch 'Aircoookie:main' into pr_fxsegs

This commit is contained in:
mx 2023-01-16 17:28:44 +01:00 committed by GitHub
commit d892c7290c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 5348 additions and 4557 deletions

View File

@ -1,5 +1,12 @@
## WLED changelog ## WLED changelog
### WLED release 0.14.0-b1
#### Build 2212222
- Version bump to v0.14.0-b1 "Hoshi"
- Full changelog TBD
### WLED release 0.13.3 ### WLED release 0.13.3
- Version bump to v0.13.3 "Toki" - Version bump to v0.13.3 "Toki"

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.0-b0", "version": "0.14.0-b1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

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

View File

@ -214,6 +214,7 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_USE_WDT=0 -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 #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS -D LOROL_LITTLEFS
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when builing with arduino-esp32 >=2.0.3
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
@ -230,6 +231,9 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32S2 -DCONFIG_IDF_TARGET_ESP32S2
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO -DCO
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT, ARDUINO_USB_MSC_ON_BOOT, ARDUINO_USB_DFU_ON_BOOT
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
@ -243,6 +247,9 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32C3 -DCONFIG_IDF_TARGET_ESP32C3
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO -DCO
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
@ -258,6 +265,8 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32S3 -DCONFIG_IDF_TARGET_ESP32S3
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO -DCO
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT, ARDUINO_USB_MSC_ON_BOOT, ARDUINO_USB_DFU_ON_BOOT
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
@ -276,7 +285,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D WLED_DISABLE_BLYNK #-DWLED_DISABLE_2D
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder monitor_filters = esp8266_exception_decoder
@ -286,7 +295,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 -D WLED_DISABLE_BLYNK
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full] [env:esp01_1m_full]
@ -295,7 +304,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D WLED_DISABLE_BLYNK
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp07] [env:esp07]
@ -341,7 +350,7 @@ board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
@ -351,7 +360,7 @@ board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
@ -381,12 +390,14 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME
lib_deps = ${esp32s2.lib_deps} lib_deps = ${esp32s2.lib_deps}
[env:esp32c3] [env:esp32c3]
platform = espressif32@5.1.1 platform = espressif32@5.1.1 ;; well-tested on -C3, good compatibility with WLED
; platform = espressif32@~5.2.0 ;; might help in case you experience bootloops due to corrupted flash filesystem
framework = arduino framework = arduino
board = esp32-c3-devkitm-1 board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual USB
upload_speed = 460800 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps} lib_deps = ${esp32c3.lib_deps}

View File

@ -56,7 +56,7 @@ private:
// runtime // runtime
bool initDone = false; bool initDone = false;
uint32_t lastOverlayDraw = 0; uint32_t lastOverlayDraw = 0;
void validateAndUpdate() { void validateAndUpdate() {
mainSegment.validateAndUpdate(); mainSegment.validateAndUpdate();
@ -110,9 +110,9 @@ private:
static inline uint32_t qadd32(uint32_t c1, uint32_t c2) { static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
return RGBW32( return RGBW32(
qadd8(R(c1), R(c2)), qadd8(R(c1), R(c2)),
qadd8(G(c1), G(c2)), qadd8(G(c1), G(c2)),
qadd8(B(c1), B(c2)), qadd8(B(c1), B(c2)),
qadd8(W(c1), W(c2)) qadd8(W(c1), W(c2))
); );
} }
@ -166,7 +166,7 @@ public:
double secondP = second(localTime) / 60.0; double secondP = second(localTime) / 60.0;
double minuteP = minute(localTime) / 60.0; double minuteP = minute(localTime) / 60.0;
double hourP = (hour(localTime) % 12) / 12.0 + minuteP / 12.0; double hourP = (hour(localTime) % 12) / 12.0 + minuteP / 12.0;
if (hourMarksEnabled) { if (hourMarksEnabled) {
for (int Led = 0; Led <= 55; Led = Led + 5) for (int Led = 0; Led <= 55; Led = Led + 5)
{ {
@ -174,7 +174,7 @@ public:
setPixelColor(hourmarkled, hourMarkColor); setPixelColor(hourmarkled, hourMarkColor);
} }
} }
if (secondsEnabled) { if (secondsEnabled) {
int16_t secondLed = adjustToSegment(secondP, secondsSegment); int16_t secondLed = adjustToSegment(secondP, secondsSegment);
@ -203,45 +203,45 @@ public:
void addToConfig(JsonObject& root) override { void addToConfig(JsonObject& root) override {
validateAndUpdate(); validateAndUpdate();
JsonObject top = root.createNestedObject("Analog Clock"); JsonObject top = root.createNestedObject(F("Analog Clock"));
top["Overlay Enabled"] = enabled; top[F("Overlay Enabled")] = enabled;
top["First LED (Main Ring)"] = mainSegment.firstLed; top[F("First LED (Main Ring)")] = mainSegment.firstLed;
top["Last LED (Main Ring)"] = mainSegment.lastLed; top[F("Last LED (Main Ring)")] = mainSegment.lastLed;
top["Center/12h LED (Main Ring)"] = mainSegment.centerLed; top[F("Center/12h LED (Main Ring)")] = mainSegment.centerLed;
top["Hour Marks Enabled"] = hourMarksEnabled; top[F("Hour Marks Enabled")] = hourMarksEnabled;
top["Hour Mark Color (RRGGBB)"] = colorToHexString(hourMarkColor); top[F("Hour Mark Color (RRGGBB)")] = colorToHexString(hourMarkColor);
top["Hour Color (RRGGBB)"] = colorToHexString(hourColor); top[F("Hour Color (RRGGBB)")] = colorToHexString(hourColor);
top["Minute Color (RRGGBB)"] = colorToHexString(minuteColor); top[F("Minute Color (RRGGBB)")] = colorToHexString(minuteColor);
top["Show Seconds"] = secondsEnabled; top[F("Show Seconds")] = secondsEnabled;
top["First LED (Seconds Ring)"] = secondsSegment.firstLed; top[F("First LED (Seconds Ring)")] = secondsSegment.firstLed;
top["Last LED (Seconds Ring)"] = secondsSegment.lastLed; top[F("Last LED (Seconds Ring)")] = secondsSegment.lastLed;
top["Center/12h LED (Seconds Ring)"] = secondsSegment.centerLed; top[F("Center/12h LED (Seconds Ring)")] = secondsSegment.centerLed;
top["Second Color (RRGGBB)"] = colorToHexString(secondColor); top[F("Second Color (RRGGBB)")] = colorToHexString(secondColor);
top["Seconds Effect (0-1)"] = secondsEffect; top[F("Seconds Effect (0-1)")] = secondsEffect;
top["Blend Colors"] = blendColors; top[F("Blend Colors")] = blendColors;
} }
bool readFromConfig(JsonObject& root) override { bool readFromConfig(JsonObject& root) override {
JsonObject top = root["Analog Clock"]; JsonObject top = root[F("Analog Clock")];
bool configComplete = !top.isNull(); bool configComplete = !top.isNull();
String color; String color;
configComplete &= getJsonValue(top["Overlay Enabled"], enabled, false); configComplete &= getJsonValue(top[F("Overlay Enabled")], enabled, false);
configComplete &= getJsonValue(top["First LED (Main Ring)"], mainSegment.firstLed, 0); configComplete &= getJsonValue(top[F("First LED (Main Ring)")], mainSegment.firstLed, 0);
configComplete &= getJsonValue(top["Last LED (Main Ring)"], mainSegment.lastLed, 59); configComplete &= getJsonValue(top[F("Last LED (Main Ring)")], mainSegment.lastLed, 59);
configComplete &= getJsonValue(top["Center/12h LED (Main Ring)"], mainSegment.centerLed, 0); configComplete &= getJsonValue(top[F("Center/12h LED (Main Ring)")], mainSegment.centerLed, 0);
configComplete &= getJsonValue(top["Hour marks Enabled"], hourMarksEnabled, false); configComplete &= getJsonValue(top[F("Hour Marks Enabled")], hourMarksEnabled, false);
configComplete &= getJsonValue(top["Hour mark Color (RRGGBB)"], color, "FF0000") && hexStringToColor(color, hourMarkColor, 0x0000FF); configComplete &= getJsonValue(top[F("Hour Mark Color (RRGGBB)")], color, F("161616")) && hexStringToColor(color, hourMarkColor, 0x161616);
configComplete &= getJsonValue(top["Hour Color (RRGGBB)"], color, "0000FF") && hexStringToColor(color, hourColor, 0x0000FF); configComplete &= getJsonValue(top[F("Hour Color (RRGGBB)")], color, F("0000FF")) && hexStringToColor(color, hourColor, 0x0000FF);
configComplete &= getJsonValue(top["Minute Color (RRGGBB)"], color, "00FF00") && hexStringToColor(color, minuteColor, 0x00FF00); configComplete &= getJsonValue(top[F("Minute Color (RRGGBB)")], color, F("00FF00")) && hexStringToColor(color, minuteColor, 0x00FF00);
configComplete &= getJsonValue(top["Show Seconds"], secondsEnabled, true); configComplete &= getJsonValue(top[F("Show Seconds")], secondsEnabled, true);
configComplete &= getJsonValue(top["First LED (Seconds Ring)"], secondsSegment.firstLed, 0); configComplete &= getJsonValue(top[F("First LED (Seconds Ring)")], secondsSegment.firstLed, 0);
configComplete &= getJsonValue(top["Last LED (Seconds Ring)"], secondsSegment.lastLed, 59); configComplete &= getJsonValue(top[F("Last LED (Seconds Ring)")], secondsSegment.lastLed, 59);
configComplete &= getJsonValue(top["Center/12h LED (Seconds Ring)"], secondsSegment.centerLed, 0); configComplete &= getJsonValue(top[F("Center/12h LED (Seconds Ring)")], secondsSegment.centerLed, 0);
configComplete &= getJsonValue(top["Second Color (RRGGBB)"], color, "FF0000") && hexStringToColor(color, secondColor, 0xFF0000); configComplete &= getJsonValue(top[F("Second Color (RRGGBB)")], color, F("FF0000")) && hexStringToColor(color, secondColor, 0xFF0000);
configComplete &= getJsonValue(top["Seconds Effect (0-1)"], secondsEffect, 0); configComplete &= getJsonValue(top[F("Seconds Effect (0-1)")], secondsEffect, 0);
configComplete &= getJsonValue(top["Blend Colors"], blendColors, true); configComplete &= getJsonValue(top[F("Blend Colors")], blendColors, true);
if (initDone) { if (initDone) {
validateAndUpdate(); validateAndUpdate();
@ -253,4 +253,4 @@ public:
uint16_t getId() override { uint16_t getId() override {
return USERMOD_ID_ANALOG_CLOCK; return USERMOD_ID_ANALOG_CLOCK;
} }
}; };

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,72 @@
// pin defaults
// for the esp32 it is best to use the ADC1: GPIO32 - GPIO39
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
#ifndef USERMOD_BATTERY_MEASUREMENT_PIN
#ifdef ARDUINO_ARCH_ESP32
#define USERMOD_BATTERY_MEASUREMENT_PIN 35
#else //ESP8266 boards
#define USERMOD_BATTERY_MEASUREMENT_PIN A0
#endif
#endif
// the frequency to check the battery, 30 sec
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
#endif
// default for 18650 battery
// https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop
// Discharge voltage: 2.5 volt + .1 for personal safety
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
#ifdef USERMOD_BATTERY_USE_LIPO
// LiPo "1S" Batteries should not be dischared below 3V !!
#define USERMOD_BATTERY_MIN_VOLTAGE 3.2f
#else
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
#endif
#endif
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
#endif
// a common capacity for single 18650 battery cells is between 2500 and 3600 mAh
#ifndef USERMOD_BATTERY_TOTAL_CAPACITY
#define USERMOD_BATTERY_TOTAL_CAPACITY 3100
#endif
// offset or calibration value to fine tune the calculated voltage
#ifndef USERMOD_BATTERY_CALIBRATION
#define USERMOD_BATTERY_CALIBRATION 0
#endif
// calculate remaining time / the time that is left before the battery runs out of power
// #ifndef USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED
// #define USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED false
// #endif
// auto-off feature
#ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED
#define USERMOD_BATTERY_AUTO_OFF_ENABLED true
#endif
#ifndef USERMOD_BATTERY_AUTO_OFF_THRESHOLD
#define USERMOD_BATTERY_AUTO_OFF_THRESHOLD 10
#endif
// low power indication feature
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED true
#endif
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET 0
#endif
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD 20
#endif
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION 5
#endif

112
usermods/Battery/readme.md Normal file
View File

@ -0,0 +1,112 @@
<p align="center">
<img width="700" src="assets/battery_usermod_logo.png">
</p>
# Welcome to the battery usermod! 🔋
Enables battery level monitoring of your project.
For this to work, the positive side of the (18650) battery must be connected to pin `A0` of the d1 mini/esp8266 with a 100k Ohm resistor (see [Useful Links](#useful-links)).
If you have an ESP32 board, connect the positive side of the battery to ADC1 (GPIO32 - GPIO39)
<p align="center">
<img width="500" src="assets/battery_info_screen.png">
</p>
## ⚙️ Features
- 💯 Displays current battery voltage
- 🚥 Displays battery level
- 🚫 Auto-off with configurable Threshold
- 🚨 Low power indicator with many configuration posibilities
## 🎈 Installation
define `USERMOD_BATTERY` in `wled00/my_config.h`
### Example wiring
<p align="center">
<img width="300" src="assets/battery_connection_schematic_01.png">
</p>
### Define Your Options
| Name | Unit | Description |
| ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- |
| `USERMOD_BATTERY` | | define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp |
| `USERMOD_BATTERY_USE_LIPO` | | define this (in `my_config.h`) if you use LiPo rechargeables (1S) |
| `USERMOD_BATTERY_MEASUREMENT_PIN` | | defaults to A0 on ESP8266 and GPIO35 on ESP32 |
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds |
| `USERMOD_BATTERY_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
| `USERMOD_BATTERY_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
| `USERMOD_BATTERY_TOTAL_CAPACITY` | mAh | the capacity of all cells in parralel sumed up |
| `USERMOD_BATTERY_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
| Auto-Off | --- | --- |
| `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | enables auto-off |
| `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | when this threshold is reached master power turns off |
| Low-Power-Indicator | --- | --- |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED` | true/false | enables low power indication |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET` | preset id | when low power is detected then use this preset to indicate low power |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD` | % (0-100) | when this threshold is reached low power gets indicated |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION` | seconds | for this long the configured preset is played |
All parameters can be configured at runtime via the Usermods settings page.
## ⚠️ Important
- Make sure you know your battery specifications! All batteries are **NOT** the same!
- Example:
| Your battery specification table | | Options you can define |
| :-------------------------------- |:--------------- | :---------------------------- |
| Capacity | 3500mAh 12,5 Wh | |
| Minimum capacity | 3350mAh 11,9 Wh | |
| Rated voltage | 3.6V - 3.7V | |
| **Charging end voltage** | **4,2V ± 0,05** | `USERMOD_BATTERY_MAX_VOLTAGE` |
| **Discharge voltage** | **2,5V** | `USERMOD_BATTERY_MIN_VOLTAGE` |
| Max. discharge current (constant) | 10A (10000mA) | |
| max. charging current | 1.7A (1700mA) | |
| ... | ... | ... |
| .. | .. | .. |
Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833)
## 🌐 Useful Links
- https://lazyzero.de/elektronik/esp8266/wemos_d1_mini_a0/start
- https://arduinodiy.wordpress.com/2016/12/25/monitoring-lipo-battery-voltage-with-wemos-d1-minibattery-shield-and-thingspeak/
## 📝 Change Log
2023-01-04
- basic support for LiPo rechargeable batteries ( `-D USERMOD_BATTERY_USE_LIPO`)
- improved support for esp32 (read calibrated voltage)
- corrected config saving (measurement pin, and battery min/max were lost)
- various bugfixes
2022-12-25
- added "auto-off" feature
- added "low-power-indication" feature
- added "calibration/offset" field to configuration page
- added getter and setter, so that user usermods could interact with this one
- update readme (added new options, made it markdownlint compliant)
2021-09-02
- added "Battery voltage" to info
- added circuit diagram to readme
- added MQTT support, sending battery voltage
- minor fixes
2021-08-15
- changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries
- Updated readme, added specification table
2021-08-10
- Created

View File

@ -0,0 +1,771 @@
#pragma once
#include "wled.h"
#include "battery_defaults.h"
/*
* Usermod by Maximilian Mewes
* Mail: mewes.maximilian@gmx.de
* GitHub: itCarl
* Date: 25.12.2022
* If you have any questions, please feel free to contact me.
*/
class UsermodBattery : public Usermod
{
private:
// battery pin can be defined in my_config.h
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN;
// how often to read the battery voltage
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0;
// battery min. voltage
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
// battery max. voltage
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
// all battery cells summed up
unsigned int totalBatteryCapacity = USERMOD_BATTERY_TOTAL_CAPACITY;
// raw analog reading
float rawValue = 0.0f;
// calculated voltage
float voltage = maxBatteryVoltage;
// mapped battery level based on voltage
int8_t batteryLevel = 100;
// offset or calibration value to fine tune the calculated voltage
float calibration = USERMOD_BATTERY_CALIBRATION;
// time left estimation feature
// bool calculateTimeLeftEnabled = USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED;
// float estimatedTimeLeft = 0.0;
// auto shutdown/shutoff/master off feature
bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED;
int8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD;
// low power indicator feature
bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED;
int8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
int8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
int8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
int8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
bool lowPowerIndicationDone = false;
unsigned long lowPowerActivationTime = 0; // used temporary during active time
int8_t lastPreset = 0;
bool initDone = false;
bool initializing = true;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _readInterval[];
static const char _enabled[];
static const char _threshold[];
static const char _preset[];
static const char _duration[];
static const char _init[];
// custom map function
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float dot2round(float x)
{
float nx = (int)(x * 100 + .5);
return (float)(nx / 100);
}
/*
* Turn off all leds
*/
void turnOff()
{
bri = 0;
stateUpdated(CALL_MODE_DIRECT_CHANGE);
}
/*
* Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously
*/
void lowPowerIndicator()
{
if (!lowPowerIndicatorEnabled) return;
if (batteryPin < 0) return; // no measurement
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= batteryLevel) lowPowerIndicationDone = false;
if (lowPowerIndicatorThreshold <= batteryLevel) return;
if (lowPowerIndicationDone) return;
if (lowPowerActivationTime <= 1) {
lowPowerActivationTime = millis();
lastPreset = currentPreset;
applyPreset(lowPowerIndicatorPreset);
}
if (lowPowerActivationTime+(lowPowerIndicatorDuration*1000) <= millis()) {
lowPowerIndicationDone = true;
lowPowerActivationTime = 0;
applyPreset(lastPreset);
}
}
public:
//Functions called by WLED
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup()
{
#ifdef ARDUINO_ARCH_ESP32
bool success = false;
DEBUG_PRINTLN(F("Allocating battery pin..."));
if (batteryPin >= 0 && digitalPinToAnalogChannel(batteryPin) >= 0)
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
success = true;
}
if (!success) {
DEBUG_PRINTLN(F("Battery pin allocation failed."));
batteryPin = -1; // allocation failed
} else {
pinMode(batteryPin, INPUT);
}
#else //ESP8266 boards have only one analog input pin A0
pinMode(batteryPin, INPUT);
#endif
nextReadTime = millis() + readingInterval;
lastReadTime = millis();
initDone = true;
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
{
//Serial.println("Connected to WiFi!");
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*
*/
void loop()
{
if(strip.isUpdating()) return;
lowPowerIndicator();
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
if (millis() < nextReadTime) return;
nextReadTime = millis() + readingInterval;
lastReadTime = millis();
if (batteryPin < 0) return; // nothing to read
initializing = false;
#ifdef ARDUINO_ARCH_ESP32
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV)
rawValue = analogReadMilliVolts(batteryPin);
// calculate the voltage
voltage = (rawValue / 1000.0f) + calibration;
// usually a voltage divider (50%) is used on ESP32, so we need to multiply by 2
voltage *= 2.0f;
#else
// read battery raw input
rawValue = analogRead(batteryPin);
// calculate the voltage
voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration;
#endif
// check if voltage is within specified voltage range, allow 10% over/under voltage
voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
// translate battery voltage into percentage
/*
the standard "map" function doesn't work
https://www.arduino.cc/reference/en/language/functions/math/map/ notes and warnings at the bottom
*/
#ifdef USERMOD_BATTERY_USE_LIPO
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100); // basic mapping
// LiPo batteries have a differnt dischargin curve, see
// https://blog.ampow.com/lipo-voltage-chart/
if (batteryLevel < 40.0f)
batteryLevel = mapf(batteryLevel, 0, 40, 0, 12); // last 45% -> drops very quickly
else {
if (batteryLevel < 90.0f)
batteryLevel = mapf(batteryLevel, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
else // level > 90%
batteryLevel = mapf(batteryLevel, 90, 105, 95, 100); // highest 15% -> drop slowly
}
#else
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100);
#endif
if (voltage > -1.0f) batteryLevel = constrain(batteryLevel, 0.0f, 110.0f);
// if (calculateTimeLeftEnabled) {
// float currentBatteryCapacity = totalBatteryCapacity;
// estimatedTimeLeft = (currentBatteryCapacity/strip.currentMilliamps)*60;
// }
// Auto off -- Master power off
if (autoOffEnabled && (autoOffThreshold >= batteryLevel))
turnOff();
// SmartHome stuff
// still don't know much about MQTT and/or HA
if (WLED_MQTT_CONNECTED) {
char buf[64]; // buffer for snprintf()
snprintf_P(buf, 63, PSTR("%s/voltage"), mqttDeviceTopic);
mqtt->publish(buf, 0, false, String(voltage).c_str());
}
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root)
{
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
if (batteryPin < 0) {
JsonArray infoVoltage = user.createNestedArray(F("Battery voltage"));
infoVoltage.add(F("n/a"));
infoVoltage.add(F(" invalid GPIO"));
return; // no GPIO - nothing to report
}
// info modal display names
JsonArray infoPercentage = user.createNestedArray(F("Battery level"));
JsonArray infoVoltage = user.createNestedArray(F("Battery voltage"));
// if (calculateTimeLeftEnabled)
// {
// JsonArray infoEstimatedTimeLeft = user.createNestedArray(F("Estimated time left"));
// if (initializing) {
// infoEstimatedTimeLeft.add(FPSTR(_init));
// } else {
// infoEstimatedTimeLeft.add(estimatedTimeLeft);
// infoEstimatedTimeLeft.add(F(" min"));
// }
// }
JsonArray infoNextUpdate = user.createNestedArray(F("Next update"));
infoNextUpdate.add((nextReadTime - millis()) / 1000);
infoNextUpdate.add(F(" sec"));
if (initializing) {
infoPercentage.add(FPSTR(_init));
infoVoltage.add(FPSTR(_init));
return;
}
if (batteryLevel < 0) {
infoPercentage.add(F("invalid"));
} else {
infoPercentage.add(batteryLevel);
}
infoPercentage.add(F(" %"));
if (voltage < 0) {
infoVoltage.add(F("invalid"));
} else {
infoVoltage.add(dot2round(voltage));
}
infoVoltage.add(F(" V"));
}
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void addToJsonState(JsonObject& root)
{
}
*/
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void readFromJsonState(JsonObject& root)
{
}
*/
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
*
* Usermod Settings Overview:
* - Numeric values are treated as floats in the browser.
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
* used in the Usermod when reading the value from ArduinoJson.
* - Pin values can be treated differently from an integer value by using the key name "pin"
* - "pin" can contain a single or array of integer values
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
*
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
*
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname
#ifdef ARDUINO_ARCH_ESP32
battery[F("pin")] = batteryPin;
#endif
// battery[F("time-left")] = calculateTimeLeftEnabled;
battery[F("min-voltage")] = minBatteryVoltage;
battery[F("max-voltage")] = maxBatteryVoltage;
battery[F("capacity")] = totalBatteryCapacity;
battery[F("calibration")] = calibration;
battery[FPSTR(_readInterval)] = readingInterval;
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
ao[FPSTR(_enabled)] = autoOffEnabled;
ao[FPSTR(_threshold)] = autoOffThreshold;
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
DEBUG_PRINTLN(F("Battery config saved."));
}
void appendConfigData()
{
oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');"));
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');"));
oappend(SET_F("addInfo('Battery:capacity', 1, 'mAh');"));
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');"));
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');"));
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');"));
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');"));
// cannot quite get this mf to work. its exeeding some buffer limit i think
// what i wanted is a list of all presets to select one from
// oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
// the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
// for(int8_t i=1; i < 42; i++) {
// oappend(SET_F("addOption(bd, 'Preset#"));
// oappendi(i);
// oappend(SET_F("',"));
// oappendi(i);
// oappend(SET_F(");"));
// }
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
*
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
bool readFromConfig(JsonObject& root)
{
#ifdef ARDUINO_ARCH_ESP32
int8_t newBatteryPin = batteryPin;
#endif
JsonObject battery = root[FPSTR(_name)];
if (battery.isNull())
{
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
#ifdef ARDUINO_ARCH_ESP32
newBatteryPin = battery[F("pin")] | newBatteryPin;
#endif
// calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
setMinBatteryVoltage(battery[F("min-voltage")] | minBatteryVoltage);
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
setCalibration(battery[F("calibration")] | calibration);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
JsonObject ao = battery[F("auto-off")];
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
JsonObject lp = battery[F("indicator")];
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset); // dropdown trickery (int)lp["preset"]
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
DEBUG_PRINT(FPSTR(_name));
#ifdef ARDUINO_ARCH_ESP32
if (!initDone)
{
// first run: reading from cfg.json
batteryPin = newBatteryPin;
DEBUG_PRINTLN(F(" config loaded."));
}
else
{
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
if (newBatteryPin != batteryPin)
{
// deallocate pin
pinManager.deallocatePin(batteryPin, PinOwner::UM_Battery);
batteryPin = newBatteryPin;
// initialise
setup();
}
}
#endif
return !battery[FPSTR(_readInterval)].isNull();
}
/*
* Generate a preset sample for low power indication
*/
void generateExamplePreset()
{
// StaticJsonDocument<300> j;
// JsonObject preset = j.createNestedObject();
// preset["mainseg"] = 0;
// JsonArray seg = preset.createNestedArray("seg");
// JsonObject seg0 = seg.createNestedObject();
// seg0["id"] = 0;
// seg0["start"] = 0;
// seg0["stop"] = 60;
// seg0["grp"] = 0;
// seg0["spc"] = 0;
// seg0["on"] = true;
// seg0["bri"] = 255;
// JsonArray col0 = seg0.createNestedArray("col");
// JsonArray col00 = col0.createNestedArray();
// col00.add(255);
// col00.add(0);
// col00.add(0);
// seg0["fx"] = 1;
// seg0["sx"] = 128;
// seg0["ix"] = 128;
// savePreset(199, "Low power Indicator", preset);
}
/*
*
* Getter and Setter. Just in case some other usermod wants to interact with this in the future
*
*/
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_BATTERY;
}
unsigned long getReadingInterval()
{
return readingInterval;
}
/*
* minimum repetition is 3000ms (3s)
*/
void setReadingInterval(unsigned long newReadingInterval)
{
readingInterval = max((unsigned long)3000, newReadingInterval);
}
/*
* Get lowest configured battery voltage
*/
float getMinBatteryVoltage()
{
return minBatteryVoltage;
}
/*
* Set lowest battery voltage
* can't be below 0 volt
*/
void setMinBatteryVoltage(float voltage)
{
minBatteryVoltage = max(0.0f, voltage);
}
/*
* Get highest configured battery voltage
*/
float getMaxBatteryVoltage()
{
return maxBatteryVoltage;
}
/*
* Set highest battery voltage
* can't be below minBatteryVoltage
*/
void setMaxBatteryVoltage(float voltage)
{
#ifdef USERMOD_BATTERY_USE_LIPO
maxBatteryVoltage = max(getMinBatteryVoltage()+0.7f, voltage);
#else
maxBatteryVoltage = max(getMinBatteryVoltage()+1.0f, voltage);
#endif
}
/*
* Get the capacity of all cells in parralel sumed up
* unit: mAh
*/
unsigned int getTotalBatteryCapacity()
{
return totalBatteryCapacity;
}
void setTotalBatteryCapacity(unsigned int capacity)
{
totalBatteryCapacity = capacity;
}
/*
* Get the choosen adc precision
* esp8266 = 10bit resolution = 1024.0f
* esp32 = 12bit resolution = 4095.0f
*/
float getAdcPrecision()
{
#ifdef ARDUINO_ARCH_ESP32
// esp32
return 4096.0f;
#else
// esp8266
return 1024.0f;
#endif
}
/*
* Get the calculated voltage
* formula: (adc pin value / adc precision * max voltage) + calibration
*/
float getVoltage()
{
return voltage;
}
/*
* Get the mapped battery level (0 - 100) based on voltage
* important: voltage can drop when a load is applied, so its only an estimate
*/
int8_t getBatteryLevel()
{
return batteryLevel;
}
/*
* Get the configured calibration value
* a offset value to fine-tune the calculated voltage.
*/
float getCalibration()
{
return calibration;
}
/*
* Set the voltage calibration offset value
* a offset value to fine-tune the calculated voltage.
*/
void setCalibration(float offset)
{
calibration = offset;
}
/*
* Get auto-off feature enabled status
* is auto-off enabled, true/false
*/
bool getAutoOffEnabled()
{
return autoOffEnabled;
}
/*
* Set auto-off feature status
*/
void setAutoOffEnabled(bool enabled)
{
autoOffEnabled = enabled;
}
/*
* Get auto-off threshold in percent (0-100)
*/
int8_t getAutoOffThreshold()
{
return autoOffThreshold;
}
/*
* Set auto-off threshold in percent (0-100)
*/
void setAutoOffThreshold(int8_t threshold)
{
autoOffThreshold = min((int8_t)100, max((int8_t)0, threshold));
// when low power indicator is enabled the auto-off threshold cannot be above indicator threshold
autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold;
}
/*
* Get low-power-indicator feature enabled status
* is the low-power-indicator enabled, true/false
*/
bool getLowPowerIndicatorEnabled()
{
return lowPowerIndicatorEnabled;
}
/*
* Set low-power-indicator feature status
*/
void setLowPowerIndicatorEnabled(bool enabled)
{
lowPowerIndicatorEnabled = enabled;
}
/*
* Get low-power-indicator preset to activate when low power is detected
*/
int8_t getLowPowerIndicatorPreset()
{
return lowPowerIndicatorPreset;
}
/*
* Set low-power-indicator preset to activate when low power is detected
*/
void setLowPowerIndicatorPreset(int8_t presetId)
{
// String tmp = ""; For what ever reason this doesn't work :(
// lowPowerIndicatorPreset = getPresetName(presetId, tmp) ? presetId : lowPowerIndicatorPreset;
lowPowerIndicatorPreset = presetId;
}
/*
* Get low-power-indicator threshold in percent (0-100)
*/
int8_t getLowPowerIndicatorThreshold()
{
return lowPowerIndicatorThreshold;
}
/*
* Set low-power-indicator threshold in percent (0-100)
*/
void setLowPowerIndicatorThreshold(int8_t threshold)
{
lowPowerIndicatorThreshold = threshold;
// when auto-off is enabled the indicator threshold cannot be below auto-off threshold
lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold+1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold);
}
/*
* Get low-power-indicator duration in seconds
*/
int8_t getLowPowerIndicatorDuration()
{
return lowPowerIndicatorDuration;
}
/*
* Set low-power-indicator duration in seconds
*/
void setLowPowerIndicatorDuration(int8_t duration)
{
lowPowerIndicatorDuration = duration;
}
/*
* Get low-power-indicator status when the indication is done thsi returns true
*/
bool getLowPowerIndicatorDone()
{
return lowPowerIndicationDone;
}
};
// strings to reduce flash memory usage (used more than twice)
const char UsermodBattery::_name[] PROGMEM = "Battery";
const char UsermodBattery::_readInterval[] PROGMEM = "interval";
const char UsermodBattery::_enabled[] PROGMEM = "enabled";
const char UsermodBattery::_threshold[] PROGMEM = "threshold";
const char UsermodBattery::_preset[] PROGMEM = "preset";
const char UsermodBattery::_duration[] PROGMEM = "duration";
const char UsermodBattery::_init[] PROGMEM = "init";

View File

@ -271,6 +271,7 @@ class UsermodCronixie : public Usermod {
{ {
if (root["nx"].is<const char*>()) { if (root["nx"].is<const char*>()) {
strncpy(cronixieDisplay, root["nx"], 6); strncpy(cronixieDisplay, root["nx"], 6);
setCronixie();
} }
} }

View File

@ -136,7 +136,7 @@ private:
} }
} else { } else {
if (m_offPreset) { if (m_offPreset) {
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(m_offPreset, NotifyUpdateMode); applyPreset(m_offPreset, NotifyUpdateMode);
return; return;
} else if (prevPlaylist) { } else if (prevPlaylist) {
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode); if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode);

View File

@ -14,7 +14,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
### Define Your Options ### Define Your Options
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp * `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - umber of milliseconds after boot to take first measurement, defaults to 20000 ms * `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - number of milliseconds after boot to take first measurement, defaults to 20000 ms
All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval. All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@ -1,68 +0,0 @@
# :battery: Battery status/level Usermod :battery:
Enables battery level monitoring of your project.
You can see the battery level and voltage in the `info modal`.
For this to work, the positive side of the (18650) battery must be connected to pin `A0` of the d1 mini/esp8266 with a 100k Ohm resistor (see [Useful Links](#useful-links)).
If you have an ESP32 board, connect the positive side of the battery to ADC1 (GPIO32 - GPIO39)
<p align="center">
<img width="300" src="assets/battery_info_screen.png">
</p>
## Installation
define `USERMOD_BATTERY_STATUS_BASIC` in `my_config.h`
### Basic wiring diagram
<p align="center">
<img width="300" src="assets/battery_connection_schematic_01.png">
</p>
### Define Your Options
* `USERMOD_BATTERY_STATUS_BASIC` - define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp
* `USERMOD_BATTERY_MEASUREMENT_PIN` - defaults to A0 on ESP8266 and GPIO32 on ESP32
* `USERMOD_BATTERY_MEASUREMENT_INTERVAL` - battery check interval. defaults to 30 seconds
* `USERMOD_BATTERY_MIN_VOLTAGE` - minimum battery voltage. default is 2.6 (18650 battery standard)
* `USERMOD_BATTERY_MAX_VOLTAGE` - maximum battery voltage. default is 4.2 (18650 battery standard)
All parameters can be configured at runtime via the Usermods settings page.
## Important :warning:
* Make sure you know your battery specifications! All batteries are **NOT** the same!
* Example:
| Your battery specification table | | Options you can define |
| :-------------------------------- |:--------------- | :---------------------------- |
| Capacity | 3500mAh 12,5 Wh | |
| Minimum capacity | 3350mAh 11,9 Wh | |
| Rated voltage | 3.6V - 3.7V | |
| **Charging end voltage** | **4,2V ± 0,05** | `USERMOD_BATTERY_MAX_VOLTAGE` |
| **Discharge voltage** | **2,5V** | `USERMOD_BATTERY_MIN_VOLTAGE` |
| Max. discharge current (constant) | 10A (10000mA) | |
| max. charging current | 1.7A (1700mA) | |
| ... | ... | ... |
| .. | .. | .. |
Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833)
## Useful Links
* https://lazyzero.de/elektronik/esp8266/wemos_d1_mini_a0/start
* https://arduinodiy.wordpress.com/2016/12/25/monitoring-lipo-battery-voltage-with-wemos-d1-minibattery-shield-and-thingspeak/
## Change Log
2021-09-02
* added "Battery voltage" to info
* added circuit diagram to readme
* added MQTT support, sending battery voltage
* minor fixes
2021-08-15
* changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries
* Updated readme, added specification table
2021-08-10
* Created

View File

@ -1,398 +0,0 @@
#pragma once
#include "wled.h"
// pin defaults
// for the esp32 it is best to use the ADC1: GPIO32 - GPIO39
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
#ifndef USERMOD_BATTERY_MEASUREMENT_PIN
#ifdef ARDUINO_ARCH_ESP32
#define USERMOD_BATTERY_MEASUREMENT_PIN 32
#else //ESP8266 boards
#define USERMOD_BATTERY_MEASUREMENT_PIN A0
#endif
#endif
// esp32 has a 12bit adc resolution
// esp8266 only 10bit
#ifndef USERMOD_BATTERY_ADC_PRECISION
#ifdef ARDUINO_ARCH_ESP32
// 12 bits
#define USERMOD_BATTERY_ADC_PRECISION 4095.0f
#else
// 10 bits
#define USERMOD_BATTERY_ADC_PRECISION 1024.0f
#endif
#endif
// the frequency to check the battery, 30 sec
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
#endif
// default for 18650 battery
// https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop
// Discharge voltage: 2.5 volt + .1 for personal safety
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
#endif
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
#endif
class UsermodBatteryBasic : public Usermod
{
private:
// battery pin can be defined in my_config.h
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN;
// how often to read the battery voltage
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0;
// battery min. voltage
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
// battery max. voltage
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
// 0 - 1024 for esp8266 (10-bit resolution)
// 0 - 4095 for esp32 (Default is 12-bit resolution)
float adcPrecision = USERMOD_BATTERY_ADC_PRECISION;
// raw analog reading
float rawValue = 0.0;
// calculated voltage
float voltage = 0.0;
// mapped battery level based on voltage
long batteryLevel = 0;
bool initDone = false;
bool initializing = true;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _readInterval[];
// custom map function
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float truncate(float val, byte dec)
{
float x = val * pow(10, dec);
float y = round(x);
float z = x - y;
if ((int)z == 5)
{
y++;
}
x = y / pow(10, dec);
return x;
}
public:
//Functions called by WLED
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup()
{
#ifdef ARDUINO_ARCH_ESP32
DEBUG_PRINTLN(F("Allocating battery pin..."));
if (batteryPin >= 0 && pinManager.allocatePin(batteryPin, false))
{
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
} else {
if (batteryPin >= 0) DEBUG_PRINTLN(F("Battery pin allocation failed."));
batteryPin = -1; // allocation failed
}
#else //ESP8266 boards have only one analog input pin A0
pinMode(batteryPin, INPUT);
#endif
nextReadTime = millis() + readingInterval;
lastReadTime = millis();
initDone = true;
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
{
//Serial.println("Connected to WiFi!");
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*
*/
void loop()
{
if(strip.isUpdating()) return;
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
if (millis() < nextReadTime) return;
nextReadTime = millis() + readingInterval;
lastReadTime = millis();
initializing = false;
// read battery raw input
rawValue = analogRead(batteryPin);
// calculate the voltage
voltage = (rawValue / adcPrecision) * maxBatteryVoltage ;
// check if voltage is within specified voltage range
voltage = voltage<minBatteryVoltage||voltage>maxBatteryVoltage?-1.0f:voltage;
// translate battery voltage into percentage
/*
the standard "map" function doesn't work
https://www.arduino.cc/reference/en/language/functions/math/map/ notes and warnings at the bottom
*/
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100);
// SmartHome stuff
if (WLED_MQTT_CONNECTED) {
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/voltage"));
mqtt->publish(subuf, 0, false, String(voltage).c_str());
}
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root)
{
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
// info modal display names
JsonArray batteryPercentage = user.createNestedArray("Battery level");
JsonArray batteryVoltage = user.createNestedArray("Battery voltage");
if (initializing) {
batteryPercentage.add((nextReadTime - millis()) / 1000);
batteryPercentage.add(" sec");
batteryVoltage.add((nextReadTime - millis()) / 1000);
batteryVoltage.add(" sec");
return;
}
if(batteryLevel < 0) {
batteryPercentage.add(F("invalid"));
} else {
batteryPercentage.add(batteryLevel);
}
batteryPercentage.add(F(" %"));
if(voltage < 0) {
batteryVoltage.add(F("invalid"));
} else {
batteryVoltage.add(truncate(voltage, 2));
}
batteryVoltage.add(F(" V"));
}
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void addToJsonState(JsonObject& root)
{
}
*/
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void readFromJsonState(JsonObject& root)
{
}
*/
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
*
* Usermod Settings Overview:
* - Numeric values are treated as floats in the browser.
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
* used in the Usermod when reading the value from ArduinoJson.
* - Pin values can be treated differently from an integer value by using the key name "pin"
* - "pin" can contain a single or array of integer values
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
*
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
*
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
// created JSON object:
/*
{
"Battery-Level": {
"pin": "A0", <--- only when using esp32 boards
"minBatteryVoltage": 2.6,
"maxBatteryVoltage": 4.2,
"read-interval-ms": 30000
}
}
*/
JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname
#ifdef ARDUINO_ARCH_ESP32
battery["pin"] = batteryPin; // usermodparam
#endif
battery["minBatteryVoltage"] = minBatteryVoltage; // usermodparam
battery["maxBatteryVoltage"] = maxBatteryVoltage; // usermodparam
battery[FPSTR(_readInterval)] = readingInterval;
DEBUG_PRINTLN(F("Battery config saved."));
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
*
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
bool readFromConfig(JsonObject& root)
{
// looking for JSON object:
/*
{
"BatteryLevel": {
"pin": "A0", <--- only when using esp32 boards
"minBatteryVoltage": 2.6,
"maxBatteryVoltage": 4.2,
"read-interval-ms": 30000
}
}
*/
#ifdef ARDUINO_ARCH_ESP32
int8_t newBatteryPin = batteryPin;
#endif
JsonObject battery = root[FPSTR(_name)];
if (battery.isNull())
{
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
#ifdef ARDUINO_ARCH_ESP32
newBatteryPin = battery["pin"] | newBatteryPin;
#endif
minBatteryVoltage = battery["minBatteryVoltage"] | minBatteryVoltage;
//minBatteryVoltage = min(12.0f, (int)readingInterval);
maxBatteryVoltage = battery["maxBatteryVoltage"] | maxBatteryVoltage;
//maxBatteryVoltage = min(14.4f, max(3.3f,(int)readingInterval));
readingInterval = battery["read-interval-ms"] | readingInterval;
readingInterval = max(3000, (int)readingInterval); // minimum repetition is >5000ms (5s)
DEBUG_PRINT(FPSTR(_name));
#ifdef ARDUINO_ARCH_ESP32
if (!initDone)
{
// first run: reading from cfg.json
newBatteryPin = batteryPin;
DEBUG_PRINTLN(F(" config loaded."));
}
else
{
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing paramters from settings page
if (newBatteryPin != batteryPin)
{
// deallocate pin
pinManager.deallocatePin(batteryPin);
batteryPin = newBatteryPin;
// initialise
setup();
}
}
#endif
return !battery[FPSTR(_readInterval)].isNull();
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_BATTERY_STATUS_BASIC;
}
};
// strings to reduce flash memory usage (used more than twice)
const char UsermodBatteryBasic::_name[] PROGMEM = "Battery-level";
const char UsermodBatteryBasic::_readInterval[] PROGMEM = "read-interval-ms";

View File

@ -138,7 +138,7 @@ class MPU6050Driver : public Usermod {
// (if it's going to break, usually the code will be 1) // (if it's going to break, usually the code will be 1)
DEBUG_PRINT(F("DMP Initialization failed (code ")); DEBUG_PRINT(F("DMP Initialization failed (code "));
DEBUG_PRINT(devStatus); DEBUG_PRINT(devStatus);
DEBUG_PRINTLN(F(")")); DEBUG_PRINTLN(")");
} }
} }

View File

@ -26,9 +26,8 @@ class ShtUsermod : public Usermod
const byte shtI2cAddress = 0x44; // i2c address of the sensor. 0x44 is the default for all SHT sensors. Change this, if needed const byte shtI2cAddress = 0x44; // i2c address of the sensor. 0x44 is the default for all SHT sensors. Change this, if needed
unsigned long shtLastTimeUpdated = 0; // Remembers when we read data the last time unsigned long shtLastTimeUpdated = 0; // Remembers when we read data the last time
bool shtDataRequested = false; // Reading data is done async. This remembers if we asked the sensor to read data bool shtDataRequested = false; // Reading data is done async. This remembers if we asked the sensor to read data
float shtCurrentTempC = 0; // Last read temperature in Celsius float shtCurrentTempC = 0.0f; // Last read temperature in Celsius
float shtCurrentTempF = 0; // Last read temperature in Fahrenheit float shtCurrentHumidity = 0.0f; // Last read humidity in RH%
float shtCurrentHumidity = 0; // Last read humidity in RH%
void initShtTempHumiditySensor(); void initShtTempHumiditySensor();
@ -56,18 +55,22 @@ class ShtUsermod : public Usermod
bool readFromConfig(JsonObject &root); bool readFromConfig(JsonObject &root);
void addToJsonInfo(JsonObject& root); void addToJsonInfo(JsonObject& root);
float getTemperatureC(); bool isEnabled() { return enabled; }
float getTemperatureF();
float getHumidity(); float getTemperature();
float getTemperatureC() { return shtCurrentTempC; }
float getTemperatureF() { return (shtCurrentTempC * 1.8f) + 32.0f; }
float getHumidity() { return shtCurrentHumidity; }
const char* getUnitString();
uint16_t getId() { return USERMOD_ID_SHT; } uint16_t getId() { return USERMOD_ID_SHT; }
}; };
// Strings to reduce flash memory usage (used more than twice) // Strings to reduce flash memory usage (used more than twice)
const char ShtUsermod::_name[] PROGMEM = "SHT-Sensor"; const char ShtUsermod::_name[] PROGMEM = "SHT-Sensor";
const char ShtUsermod::_enabled[] PROGMEM = "Enabled"; const char ShtUsermod::_enabled[] PROGMEM = "Enabled";
const char ShtUsermod::_shtType[] PROGMEM = "SHT-Type"; const char ShtUsermod::_shtType[] PROGMEM = "SHT-Type";
const char ShtUsermod::_unitOfTemp[] PROGMEM = "Unit"; const char ShtUsermod::_unitOfTemp[] PROGMEM = "Unit";
const char ShtUsermod::_haMqttDiscovery[] PROGMEM = "Add-To-HA-MQTT-Discovery"; const char ShtUsermod::_haMqttDiscovery[] PROGMEM = "Add-To-HA-MQTT-Discovery";
/** /**
@ -90,7 +93,6 @@ void ShtUsermod::initShtTempHumiditySensor()
shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl); shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl);
if (shtTempHumidSensor->readStatus() == 0xFFFF) { if (shtTempHumidSensor->readStatus() == 0xFFFF) {
DEBUG_PRINTF("[%s] SHT init failed!\n", _name); DEBUG_PRINTF("[%s] SHT init failed!\n", _name);
cleanupShtTempHumiditySensor();
cleanup(); cleanup();
return; return;
} }
@ -107,12 +109,8 @@ void ShtUsermod::initShtTempHumiditySensor()
*/ */
void ShtUsermod::cleanupShtTempHumiditySensor() void ShtUsermod::cleanupShtTempHumiditySensor()
{ {
if (isShtReady()) { if (isShtReady()) shtTempHumidSensor->reset();
shtTempHumidSensor->reset();
}
delete shtTempHumidSensor; delete shtTempHumidSensor;
shtInitDone = false; shtInitDone = false;
} }
@ -126,9 +124,7 @@ void ShtUsermod::cleanupShtTempHumiditySensor()
*/ */
void ShtUsermod::cleanup() void ShtUsermod::cleanup()
{ {
if (isShtReady()) { cleanupShtTempHumiditySensor();
cleanupShtTempHumiditySensor();
}
if (pinAllocDone) { if (pinAllocDone) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
@ -162,9 +158,9 @@ void ShtUsermod::publishTemperatureAndHumidityViaMqtt() {
char buf[128]; char buf[128];
snprintf_P(buf, 127, PSTR("%s/temperature"), mqttDeviceTopic); snprintf_P(buf, 127, PSTR("%s/temperature"), mqttDeviceTopic);
mqtt->publish(buf, 0, false, String((unitOfTemp ? getTemperatureF() : getTemperatureC())).c_str()); mqtt->publish(buf, 0, false, String(getTemperature()).c_str());
snprintf_P(buf, 127, PSTR("%s/humidity"), mqttDeviceTopic); snprintf_P(buf, 127, PSTR("%s/humidity"), mqttDeviceTopic);
mqtt->publish(buf, 0, false, String(shtCurrentHumidity).c_str()); mqtt->publish(buf, 0, false, String(getHumidity()).c_str());
} }
/** /**
@ -191,7 +187,7 @@ void ShtUsermod::publishHomeAssistantAutodiscovery() {
json[F("stat_cla")] = F("measurement"); json[F("stat_cla")] = F("measurement");
snprintf_P(buf, 127, PSTR("%s-temperature"), escapedMac.c_str()); snprintf_P(buf, 127, PSTR("%s-temperature"), escapedMac.c_str());
json[F("uniq_id")] = buf; json[F("uniq_id")] = buf;
json[F("unit_of_meas")] = F("°C"); json[F("unit_of_meas")] = unitOfTemp ? F("°F") : F("°C");
appendDeviceToMqttDiscoveryMessage(json); appendDeviceToMqttDiscoveryMessage(json);
payload_size = serializeJson(json, json_str); payload_size = serializeJson(json, json_str);
snprintf_P(buf, 127, PSTR("homeassistant/sensor/%s/%s-temperature/config"), escapedMac.c_str(), escapedMac.c_str()); snprintf_P(buf, 127, PSTR("homeassistant/sensor/%s/%s-temperature/config"), escapedMac.c_str(), escapedMac.c_str());
@ -222,7 +218,7 @@ void ShtUsermod::publishHomeAssistantAutodiscovery() {
* @return void * @return void
*/ */
void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) { void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
JsonObject device = root.createNestedObject("dev"); JsonObject device = root.createNestedObject(F("dev"));
device[F("ids")] = escapedMac.c_str(); device[F("ids")] = escapedMac.c_str();
device[F("name")] = serverDescription; device[F("name")] = serverDescription;
device[F("sw")] = versionString; device[F("sw")] = versionString;
@ -290,13 +286,11 @@ void ShtUsermod::loop()
if (shtTempHumidSensor->dataReady()) { if (shtTempHumidSensor->dataReady()) {
if (shtTempHumidSensor->readData(false)) { if (shtTempHumidSensor->readData(false)) {
shtCurrentTempC = shtTempHumidSensor->getTemperature(); shtCurrentTempC = shtTempHumidSensor->getTemperature();
shtCurrentTempF = shtTempHumidSensor->getFahrenheit();
shtCurrentHumidity = shtTempHumidSensor->getHumidity(); shtCurrentHumidity = shtTempHumidSensor->getHumidity();
publishTemperatureAndHumidityViaMqtt(); publishTemperatureAndHumidityViaMqtt();
shtReadDataSuccess = true; shtReadDataSuccess = true;
} } else {
else {
shtReadDataSuccess = false; shtReadDataSuccess = false;
} }
@ -387,6 +381,7 @@ bool ShtUsermod::readFromConfig(JsonObject &root)
bool oldEnabled = enabled; bool oldEnabled = enabled;
byte oldShtType = shtType; byte oldShtType = shtType;
byte oldUnitOfTemp = unitOfTemp;
bool oldHaMqttDiscovery = haMqttDiscovery; bool oldHaMqttDiscovery = haMqttDiscovery;
getJsonValue(top[FPSTR(_enabled)], enabled); getJsonValue(top[FPSTR(_enabled)], enabled);
@ -410,6 +405,11 @@ bool ShtUsermod::readFromConfig(JsonObject &root)
initShtTempHumiditySensor(); initShtTempHumiditySensor();
} }
if (oldUnitOfTemp != unitOfTemp) {
publishTemperatureAndHumidityViaMqtt();
publishHomeAssistantAutodiscovery();
}
if (oldHaMqttDiscovery != haMqttDiscovery && haMqttDiscovery) { if (oldHaMqttDiscovery != haMqttDiscovery && haMqttDiscovery) {
publishHomeAssistantAutodiscovery(); publishHomeAssistantAutodiscovery();
} }
@ -448,45 +448,34 @@ void ShtUsermod::addToJsonInfo(JsonObject& root)
if (shtLastTimeUpdated == 0) { if (shtLastTimeUpdated == 0) {
jsonTemp.add(F(" Not read yet")); jsonTemp.add(F(" Not read yet"));
jsonHumidity.add(F(" Not read yet")); jsonHumidity.add(F(" Not read yet"));
} } else {
else {
jsonTemp.add(F(" Error")); jsonTemp.add(F(" Error"));
jsonHumidity.add(F(" Error")); jsonHumidity.add(F(" Error"));
} }
return; return;
} }
jsonHumidity.add(shtCurrentHumidity); jsonHumidity.add(getHumidity());
jsonHumidity.add(F(" RH")); jsonHumidity.add(F(" RH"));
unitOfTemp ? jsonTemp.add(getTemperatureF()) : jsonTemp.add(getTemperatureC()); jsonTemp.add(getTemperature());
unitOfTemp ? jsonTemp.add(F(" °F")) : jsonTemp.add(F(" °C")); jsonTemp.add(unitOfTemp ? "°F" : "°C");
} }
/** /**
* Getter for last read temperature in Celsius. * Getter for last read temperature for configured unit.
* *
* @return float * @return float
*/ */
float ShtUsermod::getTemperatureC() { float ShtUsermod::getTemperature() {
return shtCurrentTempC; return unitOfTemp ? getTemperatureF() : getTemperatureC();
} }
/** /**
* Getter for last read temperature in Fahrenheit. * Returns the current configured unit as human readable string.
* *
* @return float * @return const char*
*/ */
float ShtUsermod::getTemperatureF() { const char* ShtUsermod::getUnitString() {
return shtCurrentTempF; return unitOfTemp ? "°F" : "°C";
}
/**
* Getter for last read humidity in RH%.
*
* @return float
*/
float ShtUsermod::getHumidity() {
return shtCurrentHumidity;
} }

File diff suppressed because it is too large Load Diff

View File

@ -250,6 +250,7 @@
#define FX_MODE_2DBLOBS 121 //gap fill #define FX_MODE_2DBLOBS 121 //gap fill
#define FX_MODE_2DSCROLLTEXT 122 //gap fill #define FX_MODE_2DSCROLLTEXT 122 //gap fill
#define FX_MODE_2DDRIFTROSE 123 //gap fill #define FX_MODE_2DDRIFTROSE 123 //gap fill
#define FX_MODE_2DDISTORTIONWAVES 124
// WLED-SR effects (SR compatible IDs !!!) // WLED-SR effects (SR compatible IDs !!!)
#define FX_MODE_PIXELS 128 #define FX_MODE_PIXELS 128
@ -504,6 +505,7 @@ typedef struct Segment {
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; } static void addUsedSegmentData(int len) { _usedSegmentData += len; }
void set(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed bool setColor(uint8_t slot, uint32_t c); //returns true if changed
void setCCT(uint16_t k); void setCCT(uint16_t k);
void setOpacity(uint8_t o); void setOpacity(uint8_t o);
@ -518,9 +520,9 @@ typedef struct Segment {
bool allocateData(size_t len); bool allocateData(size_t len);
void deallocateData(void); void deallocateData(void);
void resetIfRequired(void); void resetIfRequired(void);
/** /**
* Flags that before the next effect is calculated, * Flags that before the next effect is calculated,
* the internal segment state should be reset. * the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function. * Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests. * Safe to call from interrupts and network requests.
*/ */
@ -587,6 +589,7 @@ typedef struct Segment {
void moveX(int8_t delta); void moveX(int8_t delta);
void moveY(int8_t delta); void moveY(int8_t delta);
void move(uint8_t dir, uint8_t delta); void move(uint8_t dir, uint8_t delta);
void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
@ -640,7 +643,7 @@ class WS2812FX { // 96 bytes
} mode_data_t; } mode_data_t;
static WS2812FX* instance; static WS2812FX* instance;
public: public:
WS2812FX() : WS2812FX() :
@ -654,12 +657,8 @@ class WS2812FX { // 96 bytes
timebase(0), timebase(0),
isMatrix(false), isMatrix(false),
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
hPanels(1), panels(1),
vPanels(1),
panelH(8),
panelW(8),
matrix{0,0,0,0}, matrix{0,0,0,0},
panel{{0,0,0,0}},
#endif #endif
// semi-private (just obscured) used in effect functions through macros // semi-private (just obscured) used in effect functions through macros
_currentPalette(CRGBPalette16(CRGB::Black)), _currentPalette(CRGBPalette16(CRGB::Black)),
@ -696,6 +695,9 @@ class WS2812FX { // 96 bytes
_mode.clear(); _mode.clear();
_modeData.clear(); _modeData.clear();
_segments.clear(); _segments.clear();
#ifndef WLED_DISABLE_2D
panel.clear();
#endif
customPalettes.clear(); customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds); if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds);
} }
@ -808,22 +810,31 @@ class WS2812FX { // 96 bytes
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
#define WLED_MAX_PANELS 64 #define WLED_MAX_PANELS 64
uint8_t uint8_t
hPanels, panels;
vPanels;
uint16_t struct {
panelH, bool bottomStart : 1;
panelW; bool rightStart : 1;
bool vertical : 1;
bool serpentine : 1;
} matrix;
typedef struct panel_bitfield_t { typedef struct panel_t {
bool bottomStart : 1; // starts at bottom? uint16_t xOffset; // x offset relative to the top left of matrix in LEDs
bool rightStart : 1; // starts on right? uint16_t yOffset; // y offset relative to the top left of matrix in LEDs
bool vertical : 1; // is vertical? uint8_t width; // width of the panel
bool serpentine : 1; // is serpentine? uint8_t height; // height of the panel
union {
uint8_t options;
struct {
bool bottomStart : 1; // starts at bottom?
bool rightStart : 1; // starts on right?
bool vertical : 1; // is vertical?
bool serpentine : 1; // is serpentine?
};
};
} Panel; } Panel;
Panel std::vector<Panel> panel;
matrix,
panel[WLED_MAX_PANELS];
#endif #endif
void void
@ -876,9 +887,9 @@ class WS2812FX { // 96 bytes
uint16_t* customMappingTable; uint16_t* customMappingTable;
uint16_t customMappingSize; uint16_t customMappingSize;
uint32_t _lastShow; uint32_t _lastShow;
uint8_t _segment_index; uint8_t _segment_index;
uint8_t _mainSegment; uint8_t _mainSegment;

View File

@ -1,6 +1,6 @@
/* /*
FX_2Dfcn.cpp contains all 2D utility functions FX_2Dfcn.cpp contains all 2D utility functions
LICENSE LICENSE
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2022 Blaz Kristan (https://blaz.at/home) Copyright (c) 2022 Blaz Kristan (https://blaz.at/home)
@ -43,49 +43,55 @@ void WS2812FX::setUpMatrix() {
// isMatrix is set in cfg.cpp or set.cpp // isMatrix is set in cfg.cpp or set.cpp
if (isMatrix) { if (isMatrix) {
Segment::maxWidth = hPanels * panelW; // calculate width dynamically because it will have gaps
Segment::maxHeight = vPanels * panelH; Segment::maxWidth = 1;
Segment::maxHeight = 1;
for (size_t i = 0; i < panel.size(); i++) {
Panel &p = panel[i];
if (p.xOffset + p.width > Segment::maxWidth) {
Segment::maxWidth = p.xOffset + p.width;
}
if (p.yOffset + p.height > Segment::maxHeight) {
Segment::maxHeight = p.yOffset + p.height;
}
}
// safety check // safety check
if (Segment::maxWidth * Segment::maxHeight > MAX_LEDS || Segment::maxWidth == 1 || Segment::maxHeight == 1) { if (Segment::maxWidth * Segment::maxHeight > MAX_LEDS || Segment::maxWidth <= 1 || Segment::maxHeight <= 1) {
DEBUG_PRINTLN(F("2D Bounds error."));
isMatrix = false;
Segment::maxWidth = _length; Segment::maxWidth = _length;
Segment::maxHeight = 1; Segment::maxHeight = 1;
isMatrix = false; panels = 0;
panel.clear(); // release memory allocated by panels
return; return;
} }
customMappingSize = Segment::maxWidth * Segment::maxHeight; customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight];
customMappingTable = new uint16_t[customMappingSize];
if (customMappingTable != nullptr) { if (customMappingTable != nullptr) {
uint16_t startL; // index in custom mapping array (logical strip) customMappingSize = Segment::maxWidth * Segment::maxHeight;
uint16_t startP; // position of 1st pixel of panel on (virtual) strip
uint16_t x, y, offset;
uint8_t h = matrix.vertical ? vPanels : hPanels;
uint8_t v = matrix.vertical ? hPanels : vPanels;
for (uint8_t j=0, p=0; j<v; j++) { // fill with empty in case we don't fill the entire matrix
for (uint8_t i=0; i<h; i++, p++) { for (size_t i = 0; i< customMappingSize; i++) {
y = (matrix.vertical ? matrix.rightStart : matrix.bottomStart) ? v - j - 1 : j; customMappingTable[i] = (uint16_t)-1;
x = (matrix.vertical ? matrix.bottomStart : matrix.rightStart) ? h - i - 1 : i; }
x = matrix.serpentine && j%2 ? h - x - 1 : x;
startL = (matrix.vertical ? y : x) * panelW + (matrix.vertical ? x : y) * Segment::maxWidth * panelH; // logical index (top-left corner) uint16_t x, y, pix=0; //pixel
startP = p * panelW * panelH; // physical index (top-left corner) for (size_t pan = 0; pan < panel.size(); pan++) {
Panel &p = panel[pan];
uint8_t H = panel[h*j + i].vertical ? panelW : panelH; uint16_t h = p.vertical ? p.height : p.width;
uint8_t W = panel[h*j + i].vertical ? panelH : panelW; uint16_t v = p.vertical ? p.width : p.height;
for (uint16_t l=0, q=0; l<H; l++) { for (size_t j = 0; j < v; j++){
for (uint16_t k=0; k<W; k++, q++) { for(size_t i = 0; i < h; i++, pix++) {
y = (panel[h*j + i].vertical ? panel[h*j + i].rightStart : panel[h*j + i].bottomStart) ? H - l - 1 : l; y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j;
x = (panel[h*j + i].vertical ? panel[h*j + i].bottomStart : panel[h*j + i].rightStart) ? W - k - 1 : k; x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i;
x = (panel[h*j + i].serpentine && l%2) ? (W - x - 1) : x; x = p.serpentine && j%2 ? h-x-1 : x;
offset = (panel[h*j + i].vertical ? y : x) + (panel[h*j + i].vertical ? x : y) * Segment::maxWidth; customMappingTable[(p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x)] = pix;
customMappingTable[startL + offset] = startP + q;
}
} }
} }
} }
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:")); DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) { for (uint16_t i=0; i<customMappingSize; i++) {
@ -94,18 +100,18 @@ void WS2812FX::setUpMatrix() {
} }
DEBUG_PRINTLN(); DEBUG_PRINTLN();
#endif #endif
} else { } else { // memory allocation error
// memory allocation error DEBUG_PRINTLN(F("Ledmap alloc error."));
isMatrix = false;
panels = 0;
panel.clear();
Segment::maxWidth = _length; Segment::maxWidth = _length;
Segment::maxHeight = 1; Segment::maxHeight = 1;
isMatrix = false;
return; return;
} }
} else {
// not a matrix set up
Segment::maxWidth = _length;
Segment::maxHeight = 1;
} }
#else
isMatrix = false; // no matter what config says
#endif #endif
} }
@ -115,7 +121,7 @@ void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * Segment::maxWidth + x; uint16_t index = y * Segment::maxWidth + x;
if (index >= customMappingSize) return; // customMappingSize is always W * H of matrix in 2D setup if (index >= customMappingSize) return;
#else #else
uint16_t index = x; uint16_t index = x;
if (index >= _length) return; if (index >= _length) return;
@ -158,6 +164,7 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
if (leds) leds[XY(x,y)] = col; if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -265,7 +272,7 @@ void Segment::addPixelColorXY(int x, int y, uint32_t color) {
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColor(x, y, pix); setPixelColorXY(x, y, pix);
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
@ -422,6 +429,29 @@ void Segment::move(uint8_t dir, uint8_t delta) {
} }
} }
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// Bresenhams Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
while (y >= x) {
setPixelColorXY(cx+x, cy+y, col);
setPixelColorXY(cx-x, cy+y, col);
setPixelColorXY(cx+x, cy-y, col);
setPixelColorXY(cx-x, cy-y, col);
setPixelColorXY(cx+y, cy+x, col);
setPixelColorXY(cx-y, cy+x, col);
setPixelColorXY(cx+y, cy-x, col);
setPixelColorXY(cx-y, cy-x, col);
x++;
if (d > 0) {
y--;
d += 4 * (x - y) + 10;
} else {
d += 4 * x + 6;
}
}
}
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
@ -431,7 +461,7 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (x * x + y * y <= radius * radius && if (x * x + y * y <= radius * radius &&
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 && int16_t(cx)+x>=0 && int16_t(cy)+y>=0 &&
int16_t(cx)+x<cols && int16_t(cy)+y<rows) int16_t(cx)+x<cols && int16_t(cy)+y<rows)
addPixelColorXY(cx + x, cy + y, col); setPixelColorXY(cx + x, cy + y, col);
} }
} }
} }
@ -450,10 +480,10 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
int16_t err = (dx>dy ? dx : -dy)/2, e2; int16_t err = (dx>dy ? dx : -dy)/2, e2;
for (;;) { for (;;) {
addPixelColorXY(x0,y0,c); setPixelColorXY(x0,y0,c);
if (x0==x1 && y0==y1) break; if (x0==x1 && y0==y1) break;
e2 = err; e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; } if (e2 >-dx) { err -= dy; x0 += sx; }

View File

@ -174,18 +174,18 @@ void Segment::deallocateData() {
_dataLen = 0; _dataLen = 0;
} }
/** /**
* If reset of this segment was requested, clears runtime * If reset of this segment was requested, clears runtime
* settings of this segment. * settings of this segment.
* Must not be called while an effect mode function is running * Must not be called while an effect mode function is running
* because it could access the data buffer and this method * because it could access the data buffer and this method
* may free that data buffer. * may free that data buffer.
*/ */
void Segment::resetIfRequired() { void Segment::resetIfRequired() {
if (reset) { if (reset) {
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; } if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
//if (_t) { delete _t; _t = nullptr; transitional = false; } //if (_t) { delete _t; _t = nullptr; transitional = false; }
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false); reset = false; // setOption(SEG_OPTION_RESET, false);
} }
} }
@ -211,6 +211,7 @@ void Segment::setUpLeds() {
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment
static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR); static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR);
static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK));
byte tcp[72]; byte tcp[72];
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0;
@ -230,16 +231,29 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) { switch (pal) {
case 0: //default palette. Exceptions for specific effects above case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break; targetPalette = PartyColors_p; break;
case 1: //periodically replace palette with a random one. Doesn't work with multiple FastLED segments case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
if (millis() - _lastPaletteChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) { uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) {
prevRandomPalette = randomPalette;
randomPalette = CRGBPalette16( randomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255))); CHSV(random8(), random8(160, 255), random8(128, 255)));
_lastPaletteChange = millis(); _lastPaletteChange = millis();
timeSinceLastChange = 0;
} }
targetPalette = randomPalette; break; if (timeSinceLastChange <= 250) {
targetPalette = prevRandomPalette;
// there needs to be 255 palette blends (48) for full blend but that is too resource intensive
// so 128 is a compromise (we need to perform full blend of the two palettes as each segment can have random
// palette selected but only 2 static palettes are used)
size_t noOfBlends = ((128U * timeSinceLastChange) / 250U);
for (size_t i=0; i<noOfBlends; i++) nblendPaletteTowardPalette(targetPalette, randomPalette, 48);
} else {
targetPalette = randomPalette;
}
break;}
case 2: {//primary color only case 2: {//primary color only
CRGB prim = gamma32(colors[0]); CRGB prim = gamma32(colors[0]);
targetPalette = CRGBPalette16(prim); break;} targetPalette = CRGBPalette16(prim); break;}
@ -294,7 +308,7 @@ void Segment::startTransition(uint16_t dur) {
// starting a transition has to occur before change so we get current values 1st // starting a transition has to occur before change so we get current values 1st
uint8_t _briT = currentBri(on ? opacity : 0); uint8_t _briT = currentBri(on ? opacity : 0);
uint8_t _cctT = currentBri(cct, true); uint8_t _cctT = currentBri(cct, true);
CRGBPalette16 _palT; loadPalette(_palT, palette); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
uint8_t _modeP = mode; uint8_t _modeP = mode;
uint32_t _colorT[NUM_COLORS]; uint32_t _colorT[NUM_COLORS];
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = currentColor(i, colors[i]); for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = currentColor(i, colors[i]);
@ -363,6 +377,42 @@ void Segment::handleTransition() {
} }
} }
void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
//return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D
#endif
if (boundsUnchanged
&& (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return;
if (stop) fill(BLACK); //turn old segment range off
if (i2 <= i1) { //disable segment
stop = 0;
markForReset();
return;
}
if (i1 < Segment::maxWidth) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2);
startY = 0;
stopY = 1;
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) { // 2D
if (i1Y < Segment::maxHeight) startY = i1Y;
stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y);
}
#endif
if (grp) {
grouping = grp;
spacing = spc;
}
if (ofs < UINT16_MAX) offset = ofs;
markForReset();
if (!boundsUnchanged) refreshLightCapabilities();
}
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot >= NUM_COLORS || c == colors[slot]) return false; if (slot >= NUM_COLORS || c == colors[slot]) return false;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
@ -409,18 +459,21 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
// load default values from effect string // load default values from effect string
if (loadDefaults) { if (loadDefaults) {
int16_t sOpt; int16_t sOpt;
sOpt = extractModeDefaults(fx, "sx"); if (sOpt >= 0) speed = sOpt; sOpt = extractModeDefaults(fx, "sx"); speed = (sOpt >= 0) ? sOpt : DEFAULT_SPEED;
sOpt = extractModeDefaults(fx, "ix"); if (sOpt >= 0) intensity = sOpt; sOpt = extractModeDefaults(fx, "ix"); intensity = (sOpt >= 0) ? sOpt : DEFAULT_INTENSITY;
sOpt = extractModeDefaults(fx, "c1"); if (sOpt >= 0) custom1 = sOpt; sOpt = extractModeDefaults(fx, "c1"); custom1 = (sOpt >= 0) ? sOpt : DEFAULT_C1;
sOpt = extractModeDefaults(fx, "c2"); if (sOpt >= 0) custom2 = sOpt; sOpt = extractModeDefaults(fx, "c2"); custom2 = (sOpt >= 0) ? sOpt : DEFAULT_C2;
sOpt = extractModeDefaults(fx, "c3"); if (sOpt >= 0) custom3 = sOpt; sOpt = extractModeDefaults(fx, "c3"); custom3 = (sOpt >= 0) ? sOpt : DEFAULT_C3;
sOpt = extractModeDefaults(fx, "o1"); check1 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7); sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 7); sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt; sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt; sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
} }
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
} }
@ -524,6 +577,20 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
int y = roundf(cos_t(rad) * i); int y = roundf(cos_t(rad) * i);
setPixelColorXY(x, y, col); setPixelColorXY(x, y, col);
} }
// Bresenhams Algorithm (may not fill every pixel)
//int d = 3 - (2*i);
//int y = i, x = 0;
//while (y >= x) {
// setPixelColorXY(x, y, col);
// setPixelColorXY(y, x, col);
// x++;
// if (d > 0) {
// y--;
// d += 4 * (x - y) + 10;
// } else {
// d += 4 * x + 6;
// }
//}
} }
break; break;
case M12_pCorner: case M12_pCorner:
@ -546,6 +613,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -570,7 +638,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t indexSet = i + ((reverse) ? -j : j); uint16_t indexSet = i + ((reverse) ? -j : j);
if (indexSet >= start && indexSet < stop) { if (indexSet >= start && indexSet < stop) {
if (mirror) { //set the corresponding mirrored pixel if (mirror) { //set the corresponding mirrored pixel
uint16_t indexMir = stop - indexSet + start - 1; uint16_t indexMir = stop - indexSet + start - 1;
indexMir += offset; // offset/phase indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap if (indexMir >= stop) indexMir -= len; // wrap
strip.setPixelColor(indexMir, col); strip.setPixelColor(indexMir, col);
@ -689,16 +757,16 @@ void Segment::refreshLightCapabilities() {
if (bus->getStart() + bus->getLength() <= start) continue; if (bus->getStart() + bus->getLength() <= start) continue;
uint8_t type = bus->getType(); uint8_t type = bus->getType();
if (type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB if (type == TYPE_ONOFF || type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB
if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel
if (!cctFromRgb) { if (!cctFromRgb) {
switch (type) { switch (type) {
case TYPE_ANALOG_5CH: case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH: case TYPE_ANALOG_2CH:
capabilities |= 0x04; //segment supports white CCT capabilities |= 0x04; //segment supports white CCT
} }
} }
if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider) if (correctWB && !(type == TYPE_ANALOG_1CH || type == TYPE_ONOFF)) capabilities |= 0x04; //white balance correction (uses CCT slider)
uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode(); uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode();
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB) if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB)
@ -858,7 +926,7 @@ uint8_t Segment::get_random_wheel_index(uint8_t pos) {
* Gets a single color from the currently selected palette. * Gets a single color from the currently selected palette.
* @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically. * @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically.
* @param mapping if true, LED position in segment is considered for color * @param mapping if true, LED position in segment is considered for color
* @param wrap FastLED palettes will usally wrap back to the start smoothly. Set false to get a hard edge * @param wrap FastLED palettes will usually wrap back to the start smoothly. Set false to get a hard edge
* @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead * @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
* @returns Single color from palette * @returns Single color from palette
@ -914,13 +982,13 @@ void WS2812FX::finalizeInit(void)
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
uint16_t prevLen = 0; uint16_t prevLen = 0;
for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES; i++) { for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]}; uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen; uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
prevLen += count; prevLen += count;
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY); BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY);
busses.add(defCfg); if (busses.add(defCfg) == -1) break;
} }
} }
@ -944,6 +1012,11 @@ void WS2812FX::finalizeInit(void)
#endif #endif
} }
if (!isMatrix) { // if 2D then max values defined in setUpMatrix() using panel set-up
Segment::maxWidth = _length;
Segment::maxHeight = 1;
}
//initialize leds array. TBD: realloc if nr of leds change //initialize leds array. TBD: realloc if nr of leds change
if (Segment::_globalLeds) { if (Segment::_globalLeds) {
purgeSegments(true); purgeSegments(true);
@ -1001,7 +1074,7 @@ void WS2812FX::service() {
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
delay = (*_mode[seg.currentMode(seg.mode)])(); delay = (*_mode[seg.currentMode(seg.mode)])();
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // foce faster updates during transition if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
seg.handleTransition(); seg.handleTransition();
} }
@ -1042,7 +1115,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
//Stay safe with high amperage and have a reasonable safety margin! //Stay safe with high amperage and have a reasonable safety margin!
//I am NOT to be held liable for burned down garages! //I am NOT to be held liable for burned down garages!
//fine tune power estimation constants for your setup //fine tune power estimation constants for your setup
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA) #define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
//you can set it to 0 if the ESP is powered by USB and the LEDs by external //you can set it to 0 if the ESP is powered by USB and the LEDs by external
@ -1101,7 +1174,7 @@ void WS2812FX::estimateCurrentAndLimitBri() {
uint32_t powerSum0 = powerSum; uint32_t powerSum0 = powerSum;
powerSum *= _brightness; powerSum *= _brightness;
if (powerSum > powerBudget) //scale brightness down to stay in current limit if (powerSum > powerBudget) //scale brightness down to stay in current limit
{ {
float scale = (float)powerBudget / (float)powerSum; float scale = (float)powerBudget / (float)powerSum;
@ -1125,7 +1198,7 @@ void WS2812FX::show(void) {
if (callback) callback(); if (callback) callback();
estimateCurrentAndLimitBri(); estimateCurrentAndLimitBri();
// some buses send asynchronously and this method will return before // some buses send asynchronously and this method will return before
// all of the data has been sent. // all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
@ -1162,7 +1235,7 @@ void WS2812FX::setTargetFps(uint8_t fps) {
void WS2812FX::setMode(uint8_t segid, uint8_t m) { void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (segid >= _segments.size()) return; if (segid >= _segments.size()) return;
if (m >= getModeCount()) m = getModeCount() - 1; if (m >= getModeCount()) m = getModeCount() - 1;
if (_segments[segid].mode != m) { if (_segments[segid].mode != m) {
@ -1306,54 +1379,7 @@ Segment& WS2812FX::getSegment(uint8_t id) {
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) { void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
if (n >= _segments.size()) return; if (n >= _segments.size()) return;
Segment& seg = _segments[n]; _segments[n].set(i1, i2, grouping, spacing, offset, startY, stopY);
//return if neither bounds nor grouping have changed
bool boundsUnchanged = (seg.start == i1 && seg.stop == i2);
if (isMatrix) {
boundsUnchanged &= (seg.startY == startY && seg.stopY == stopY);
}
if (boundsUnchanged
&& (!grouping || (seg.grouping == grouping && seg.spacing == spacing))
&& (offset == UINT16_MAX || offset == seg.offset)) return;
//if (seg.stop) setRange(seg.start, seg.stop -1, BLACK); //turn old segment range off
if (seg.stop) seg.fill(BLACK); //turn old segment range off
if (i2 <= i1) //disable segment
{
// disabled segments should get removed using purgeSegments()
DEBUG_PRINT(F("-- Segment ")); DEBUG_PRINT(n); DEBUG_PRINTLN(F(" marked inactive."));
seg.stop = 0;
seg.options = 0b0000000000000101; // on & selected
//if (seg.name) {
// delete[] seg.name;
// seg.name = nullptr;
//}
// if main segment is deleted, set first active as main segment
if (n == _mainSegment) setMainSegmentId(0);
seg.markForReset();
return;
}
if (isMatrix) {
#ifndef WLED_DISABLE_2D
if (i1 < Segment::maxWidth) seg.start = i1;
seg.stop = i2 > Segment::maxWidth ? Segment::maxWidth : i2;
if (startY < Segment::maxHeight) seg.startY = startY;
seg.stopY = stopY > Segment::maxHeight ? Segment::maxHeight : MAX(1,stopY);
#endif
} else {
if (i1 < _length) seg.start = i1;
seg.stop = i2 > _length ? _length : i2;
seg.startY = 0;
seg.stopY = 1;
}
if (grouping) {
seg.grouping = grouping;
seg.spacing = spacing;
}
if (offset < UINT16_MAX) seg.offset = offset;
seg.markForReset();
if (!boundsUnchanged) seg.refreshLightCapabilities();
} }
void WS2812FX::restartRuntime() { void WS2812FX::restartRuntime() {
@ -1511,15 +1537,28 @@ void WS2812FX::loadCustomPalettes()
if (readObjectFromFile(fileName, nullptr, &pDoc)) { if (readObjectFromFile(fileName, nullptr, &pDoc)) {
JsonArray pal = pDoc[F("palette")]; JsonArray pal = pDoc[F("palette")];
if (!pal.isNull() && pal.size()>7) { // not an empty palette (at least 2 entries) if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries)
size_t palSize = MIN(pal.size(), 72); if (pal[0].is<int>() && pal[1].is<const char *>()) {
palSize -= palSize % 4; // make sure size is multiple of 4 // we have an array of index & hex strings
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) { size_t palSize = MIN(pal.size(), 36);
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index palSize -= palSize % 2; // make sure size is multiple of 2
tcp[i+1] = (uint8_t) pal[i+1].as<int>(); // R for (size_t i=0, j=0; i<palSize && pal[i].as<int>()<256; i+=2, j+=4) {
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G uint8_t rgbw[] = {0,0,0,0};
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3])); colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires
for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3]));
}
} else {
size_t palSize = MIN(pal.size(), 72);
palSize -= palSize % 4; // make sure size is multiple of 4
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
tcp[i+1] = (uint8_t) pal[i+1].as<int>(); // R
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
}
} }
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp)); customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
} }

View File

@ -2,7 +2,7 @@
/* /*
* Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa. * Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa.
* *
* This was put together from these two excellent projects: * This was put together from these two excellent projects:
* https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch
* https://github.com/probonopd/ESP8266HueEmulator * https://github.com/probonopd/ESP8266HueEmulator
@ -21,11 +21,11 @@ void alexaInit()
espalexaDevice = new EspalexaDevice(alexaInvocationName, onAlexaChange, EspalexaDeviceType::extendedcolor); espalexaDevice = new EspalexaDevice(alexaInvocationName, onAlexaChange, EspalexaDeviceType::extendedcolor);
espalexa.addDevice(espalexaDevice); espalexa.addDevice(espalexaDevice);
// up to 9 devices (added second, third, ... i.e. index 1 to 9) serve for switching on up to nine presets (preset IDs 1 to 9 in WLED), // up to 9 devices (added second, third, ... i.e. index 1 to 9) serve for switching on up to nine presets (preset IDs 1 to 9 in WLED),
// names are identical as the preset names, switching off can be done by switching off any of them // names are identical as the preset names, switching off can be done by switching off any of them
if (alexaNumPresets) { if (alexaNumPresets) {
String name = ""; String name = "";
for (byte presetIndex = 1; presetIndex <= alexaNumPresets; presetIndex++) for (byte presetIndex = 1; presetIndex <= alexaNumPresets; presetIndex++)
{ {
if (!getPresetName(presetIndex, name)) break; // no more presets if (!getPresetName(presetIndex, name)) break; // no more presets
EspalexaDevice* dev = new EspalexaDevice(name.c_str(), onAlexaChange, EspalexaDeviceType::extendedcolor); EspalexaDevice* dev = new EspalexaDevice(name.c_str(), onAlexaChange, EspalexaDeviceType::extendedcolor);
@ -44,7 +44,7 @@ void handleAlexa()
void onAlexaChange(EspalexaDevice* dev) void onAlexaChange(EspalexaDevice* dev)
{ {
EspalexaDeviceProperty m = dev->getLastChangedProperty(); EspalexaDeviceProperty m = dev->getLastChangedProperty();
if (m == EspalexaDeviceProperty::on) if (m == EspalexaDeviceProperty::on)
{ {
if (dev->getId() == 0) // Device 0 is for on/off or macros if (dev->getId() == 0) // Device 0 is for on/off or macros
@ -56,7 +56,7 @@ void onAlexaChange(EspalexaDevice* dev)
bri = briLast; bri = briLast;
stateUpdated(CALL_MODE_ALEXA); stateUpdated(CALL_MODE_ALEXA);
} }
} else } else
{ {
applyPreset(macroAlexaOn, CALL_MODE_ALEXA); applyPreset(macroAlexaOn, CALL_MODE_ALEXA);
if (bri == 0) dev->setValue(briLast); //stop Alexa from complaining if macroAlexaOn does not actually turn on if (bri == 0) dev->setValue(briLast); //stop Alexa from complaining if macroAlexaOn does not actually turn on
@ -82,7 +82,7 @@ void onAlexaChange(EspalexaDevice* dev)
bri = 0; bri = 0;
stateUpdated(CALL_MODE_ALEXA); stateUpdated(CALL_MODE_ALEXA);
} }
} else } else
{ {
applyPreset(macroAlexaOff, CALL_MODE_ALEXA); applyPreset(macroAlexaOff, CALL_MODE_ALEXA);
// below for loop stops Alexa from complaining if macroAlexaOff does not actually turn off // below for loop stops Alexa from complaining if macroAlexaOff does not actually turn off

View File

@ -217,7 +217,7 @@ class Bus {
static uint8_t _gAWM; // definition in FX_fcn.cpp static uint8_t _gAWM; // definition in FX_fcn.cpp
static int16_t _cct; // definition in FX_fcn.cpp static int16_t _cct; // definition in FX_fcn.cpp
static uint8_t _cctBlend; // definition in FX_fcn.cpp static uint8_t _cctBlend; // definition in FX_fcn.cpp
uint32_t autoWhiteCalc(uint32_t c) { uint32_t autoWhiteCalc(uint32_t c) {
uint8_t aWM = _autoWhiteMode; uint8_t aWM = _autoWhiteMode;
if (_gAWM < 255) aWM = _gAWM; if (_gAWM < 255) aWM = _gAWM;
@ -271,7 +271,7 @@ class BusDigital : public Bus {
//Fix for turning off onboard LED breaking bus //Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN #ifdef LED_BUILTIN
if (_bri == 0 && b > 0) { if (_bri == 0 && b > 0) {
if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
} }
#endif #endif
Bus::setBrightness(b); Bus::setBrightness(b);
@ -343,7 +343,7 @@ class BusDigital : public Bus {
cleanup(); cleanup();
} }
private: private:
uint8_t _colorOrder = COL_ORDER_GRB; uint8_t _colorOrder = COL_ORDER_GRB;
uint8_t _pins[2] = {255, 255}; uint8_t _pins[2] = {255, 255};
uint8_t _iType = I_NONE; uint8_t _iType = I_NONE;
@ -477,7 +477,7 @@ class BusPwm : public Bus {
cleanup(); cleanup();
} }
private: private:
uint8_t _pins[5] = {255, 255, 255, 255, 255}; uint8_t _pins[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {0}; uint8_t _data[5] = {0};
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
@ -553,7 +553,7 @@ class BusOnOff : public Bus {
cleanup(); cleanup();
} }
private: private:
uint8_t _pin = 255; uint8_t _pin = 255;
uint8_t _data = 0; uint8_t _data = 0;
}; };
@ -684,9 +684,24 @@ class BusManager {
if (type == 44 || type == 45) return len*4; //RGBW if (type == 44 || type == 45) return len*4; //RGBW
return len*3; //RGB return len*3; //RGB
} }
/*
int add(BusConfig &bc);
void removeAll(); //do not call this method from system context (network callback)
void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setBrightness(uint8_t b);
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t getPixelColor(uint16_t pix);
bool canAllShow();
Bus* getBus(uint8_t busNr);
void show();
uint16_t getTotalLength(); //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
*/
// the following functions are inlined by compiler since they are defined within class definition
// they should be placed in cpp file instead
int add(BusConfig &bc) { int add(BusConfig &bc) {
if (numBusses >= WLED_MAX_BUSSES) return -1; if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) { if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
busses[numBusses] = new BusNetwork(bc); busses[numBusses] = new BusNetwork(bc);
} else if (IS_DIGITAL(bc.type)) { } else if (IS_DIGITAL(bc.type)) {
@ -702,7 +717,7 @@ class BusManager {
//do not call this method from system context (network callback) //do not call this method from system context (network callback)
void removeAll() { void removeAll() {
DEBUG_PRINTLN(F("Removing all.")); DEBUG_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use. //prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield(); while (!canAllShow()) yield();
for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
numBusses = 0; numBusses = 0;
@ -714,11 +729,11 @@ class BusManager {
} }
} }
void setStatusPixel(uint32_t c) { void setStatusPixel(uint32_t c) {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setStatusPixel(c); busses[i]->setStatusPixel(c);
} }
} }
void IRAM_ATTR 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++) { for (uint8_t i = 0; i < numBusses; i++) {
@ -766,10 +781,6 @@ class BusManager {
return busses[busNr]; return busses[busNr];
} }
inline uint8_t getNumBusses() {
return numBusses;
}
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t getTotalLength() { uint16_t getTotalLength() {
uint16_t len = 0; uint16_t len = 0;
@ -777,17 +788,27 @@ class BusManager {
return len; return len;
} }
void updateColorOrderMap(const ColorOrderMap &com) { inline void updateColorOrderMap(const ColorOrderMap &com) {
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
} }
const ColorOrderMap& getColorOrderMap() const { inline const ColorOrderMap& getColorOrderMap() const {
return colorOrderMap; return colorOrderMap;
} }
inline uint8_t getNumBusses() {
return numBusses;
}
private: private:
uint8_t numBusses = 0; uint8_t numBusses = 0;
Bus* busses[WLED_MAX_BUSSES]; Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap colorOrderMap; ColorOrderMap colorOrderMap;
inline uint8_t getNumVirtualBusses() {
int j = 0;
for (int i=0; i<numBusses; i++) if (busses[i]->getType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++;
return j;
}
}; };
#endif #endif

View File

@ -699,7 +699,7 @@ class PolyBus {
} }
}; };
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
RgbwColor col(0,0,0,0); RgbwColor col(0,0,0,0);
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
@ -771,7 +771,7 @@ class PolyBus {
case I_HS_P98_3: col = (static_cast<B_HS_P98_3*>(busPtr))->GetPixelColor(pix); break; case I_HS_P98_3: col = (static_cast<B_HS_P98_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_P98_3: col = (static_cast<B_SS_P98_3*>(busPtr))->GetPixelColor(pix); break; case I_SS_P98_3: col = (static_cast<B_SS_P98_3*>(busPtr))->GetPixelColor(pix); break;
} }
// upper nibble contains W swap information // upper nibble contains W swap information
uint8_t w = col.W; uint8_t w = col.W;
switch (co >> 4) { switch (co >> 4) {
@ -866,7 +866,7 @@ class PolyBus {
} }
} }
//gives back the internal type index (I_XX_XXX_X above) for the input //gives back the internal type index (I_XX_XXX_X above) for the input
static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) { static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) {
if (!IS_DIGITAL(busType)) return I_NONE; if (!IS_DIGITAL(busType)) return I_NONE;
if (IS_2PIN(busType)) { //SPI LED chips if (IS_2PIN(busType)) { //SPI LED chips

View File

@ -105,18 +105,18 @@ void handleSwitch(uint8_t b)
} }
if (buttonLongPressed[b] == buttonPressedBefore[b]) return; if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce) if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (!buttonPressedBefore[b]) { // on -> off if (!buttonPressedBefore[b]) { // on -> off
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
else { //turn on else { //turn on
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);} if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
} }
} else { // off -> on } else { // off -> on
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
else { //turn off else { //turn off
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);} if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
} }
} }
// publish MQTT message // publish MQTT message
@ -132,7 +132,7 @@ void handleSwitch(uint8_t b)
} }
#define ANALOG_BTN_READ_CYCLE 250 // min time between two analog reading cycles #define ANALOG_BTN_READ_CYCLE 250 // min time between two analog reading cycles
#define STRIP_WAIT_TIME 6 // max wait time in case of strip.isUpdating() #define STRIP_WAIT_TIME 6 // max wait time in case of strip.isUpdating()
#define POT_SMOOTHING 0.25f // smoothing factor for raw potentiometer readings #define POT_SMOOTHING 0.25f // smoothing factor for raw potentiometer readings
#define POT_SENSITIVITY 4 // changes below this amount are noise (POT scratching, or ADC noise) #define POT_SENSITIVITY 4 // changes below this amount are noise (POT scratching, or ADC noise)
@ -165,7 +165,7 @@ void handleAnalog(uint8_t b)
//while(strip.isUpdating() && (millis() - wait_started < STRIP_WAIT_TIME)) { //while(strip.isUpdating() && (millis() - wait_started < STRIP_WAIT_TIME)) {
// delay(1); // delay(1);
//} //}
//if (strip.isUpdating()) return; // give up //if (strip.isUpdating()) return; // give up
oldRead[b] = aRead; oldRead[b] = aRead;
@ -326,7 +326,7 @@ void esp32RMTInvertIdle()
void handleIO() void handleIO()
{ {
handleButton(); handleButton();
//set relay when LEDs turn on //set relay when LEDs turn on
if (strip.getBrightness()) if (strip.getBrightness())
{ {

View File

@ -64,7 +64,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (apHide > 1) apHide = 1; if (apHide > 1) apHide = 1;
CJSON(apBehavior, ap[F("behav")]); CJSON(apBehavior, ap[F("behav")]);
/* /*
JsonArray ap_ip = ap["ip"]; JsonArray ap_ip = ap["ip"];
for (byte i = 0; i < 4; i++) { for (byte i = 0; i < 4; i++) {
@ -97,32 +97,38 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject matrix = hw_led[F("matrix")]; JsonObject matrix = hw_led[F("matrix")];
if (!matrix.isNull()) { if (!matrix.isNull()) {
strip.isMatrix = true; strip.isMatrix = true;
CJSON(strip.panelH, matrix[F("ph")]); CJSON(strip.panels, matrix[F("mpc")]);
CJSON(strip.panelW, matrix[F("pw")]);
CJSON(strip.hPanels, matrix[F("mph")]);
CJSON(strip.vPanels, matrix[F("mpv")]);
CJSON(strip.matrix.bottomStart, matrix[F("pb")]); CJSON(strip.matrix.bottomStart, matrix[F("pb")]);
CJSON(strip.matrix.rightStart, matrix[F("pr")]); CJSON(strip.matrix.rightStart, matrix[F("pr")]);
CJSON(strip.matrix.vertical, matrix[F("pv")]); CJSON(strip.matrix.vertical, matrix[F("pv")]);
CJSON(strip.matrix.serpentine, matrix["ps"]); CJSON(strip.matrix.serpentine, matrix["ps"]);
strip.panel.clear();
JsonArray panels = matrix[F("panels")]; JsonArray panels = matrix[F("panels")];
uint8_t s = 0; uint8_t s = 0;
if (!panels.isNull()) { if (!panels.isNull()) {
strip.panel.reserve(max(1U,min((size_t)strip.panels,(size_t)WLED_MAX_PANELS))); // pre-allocate memory for panels
for (JsonObject pnl : panels) { for (JsonObject pnl : panels) {
CJSON(strip.panel[s].bottomStart, pnl["b"]); WS2812FX::Panel p;
CJSON(strip.panel[s].rightStart, pnl["r"]); CJSON(p.bottomStart, pnl["b"]);
CJSON(strip.panel[s].vertical, pnl["v"]); CJSON(p.rightStart, pnl["r"]);
CJSON(strip.panel[s].serpentine, pnl["s"]); CJSON(p.vertical, pnl["v"]);
if (++s >= WLED_MAX_PANELS) break; // max panels reached CJSON(p.serpentine, pnl["s"]);
CJSON(p.xOffset, pnl["x"]);
CJSON(p.yOffset, pnl["y"]);
CJSON(p.height, pnl["h"]);
CJSON(p.width, pnl["w"]);
strip.panel.push_back(p);
if (++s >= WLED_MAX_PANELS || s >= strip.panels) break; // max panels reached
} }
} } else {
// clear remaining panels // fallback
for (; s<WLED_MAX_PANELS; s++) { WS2812FX::Panel p;
strip.panel[s].bottomStart = 0; strip.panels = 1;
strip.panel[s].rightStart = 0; p.height = p.width = 8;
strip.panel[s].vertical = 0; p.xOffset = p.yOffset = 0;
strip.panel[s].serpentine = 0; p.options = 0;
strip.panel.push_back(p);
} }
strip.setUpMatrix(); strip.setUpMatrix();
@ -130,14 +136,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
#endif #endif
JsonArray ins = hw_led["ins"]; JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) { if (fromFS || !ins.isNull()) {
uint8_t s = 0; // bus iterator uint8_t s = 0; // bus iterator
if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback
uint32_t mem = 0; uint32_t mem = 0;
bool busesChanged = false; bool busesChanged = false;
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES) break; if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm["pin"]; JsonArray pinArr = elm["pin"];
if (pinArr.size() == 0) continue; if (pinArr.size() == 0) continue;
@ -161,7 +167,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS) { if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
mem += BusManager::memUsage(bc); mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip() if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else { } else {
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
@ -192,9 +198,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
// read multiple button configuration // read multiple button configuration
JsonObject btn_obj = hw["btn"]; JsonObject btn_obj = hw["btn"];
int pull = -1; // trick for inverted setting bool pull = btn_obj[F("pull")] | (!disablePullUp); // if true, pullup is enabled
CJSON(pull, btn_obj[F("pull")]); disablePullUp = !pull;
if (pull>=0) disablePullUp = pull;
JsonArray hw_btn_ins = btn_obj[F("ins")]; JsonArray hw_btn_ins = btn_obj[F("ins")];
if (!hw_btn_ins.isNull()) { if (!hw_btn_ins.isNull()) {
uint8_t s = 0; uint8_t s = 0;
@ -205,14 +210,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
btnPin[s] = pin; btnPin[s] = pin;
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// ESP32 only: check that analog button pin is a valid ADC gpio // ESP32 only: check that analog button pin is a valid ADC gpio
if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0)) if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0))
{ {
// not an ADC analog pin // not an ADC analog pin
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s); DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s);
btnPin[s] = -1; btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button); pinManager.deallocatePin(pin,PinOwner::Button);
} }
else else
#endif #endif
{ {
if (disablePullUp) { if (disablePullUp) {
@ -248,7 +253,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
// relies upon only being called once with fromFS == true, which is currently true. // relies upon only being called once with fromFS == true, which is currently true.
uint8_t s = 0; uint8_t s = 0;
if (pinManager.allocatePin(btnPin[0], false, PinOwner::Button)) { // initialized to #define value BTNPIN, or zero if not defined(!) if (pinManager.allocatePin(btnPin[0], false, PinOwner::Button)) { // initialized to #define value BTNPIN, or zero if not defined(!)
++s; // do not clear default button if allocated successfully ++s; // do not clear default button if allocated successfully
} }
for (; s<WLED_MAX_BUTTONS; s++) { for (; s<WLED_MAX_BUTTONS; s++) {
btnPin[s] = -1; btnPin[s] = -1;
@ -494,7 +499,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t it = 0; uint8_t it = 0;
for (JsonObject timer : timers) { for (JsonObject timer : timers) {
if (it > 9) break; if (it > 9) break;
if (it<8 && timer[F("hour")]==255) it=8; // hour==255 -> sunrise/sunset if (it<8 && timer[F("hour")]==255) it=8; // hour==255 -> sunrise/sunset
CJSON(timerHours[it], timer[F("hour")]); CJSON(timerHours[it], timer[F("hour")]);
CJSON(timerMinutes[it], timer["min"]); CJSON(timerMinutes[it], timer["min"]);
CJSON(timerMacro[it], timer["macro"]); CJSON(timerMacro[it], timer["macro"]);
@ -702,22 +707,23 @@ void serializeConfig() {
// 2D Matrix Settings // 2D Matrix Settings
if (strip.isMatrix) { if (strip.isMatrix) {
JsonObject matrix = hw_led.createNestedObject(F("matrix")); JsonObject matrix = hw_led.createNestedObject(F("matrix"));
matrix[F("ph")] = strip.panelH; matrix[F("mpc")] = strip.panels;
matrix[F("pw")] = strip.panelW;
matrix[F("mph")] = strip.hPanels;
matrix[F("mpv")] = strip.vPanels;
matrix[F("pb")] = strip.matrix.bottomStart; matrix[F("pb")] = strip.matrix.bottomStart;
matrix[F("pr")] = strip.matrix.rightStart; matrix[F("pr")] = strip.matrix.rightStart;
matrix[F("pv")] = strip.matrix.vertical; matrix[F("pv")] = strip.matrix.vertical;
matrix["ps"] = strip.matrix.serpentine; matrix["ps"] = strip.matrix.serpentine;
JsonArray panels = matrix.createNestedArray(F("panels")); JsonArray panels = matrix.createNestedArray(F("panels"));
for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) { for (uint8_t i=0; i<strip.panel.size(); i++) {
JsonObject pnl = panels.createNestedObject(); JsonObject pnl = panels.createNestedObject();
pnl["b"] = strip.panel[i].bottomStart; pnl["b"] = strip.panel[i].bottomStart;
pnl["r"] = strip.panel[i].rightStart; pnl["r"] = strip.panel[i].rightStart;
pnl["v"] = strip.panel[i].vertical; pnl["v"] = strip.panel[i].vertical;
pnl["s"] = strip.panel[i].serpentine; pnl["s"] = strip.panel[i].serpentine;
pnl["x"] = strip.panel[i].xOffset;
pnl["y"] = strip.panel[i].yOffset;
pnl["h"] = strip.panel[i].height;
pnl["w"] = strip.panel[i].width;
} }
} }
#endif #endif

View File

@ -91,7 +91,7 @@ void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
r = round(329.698727446 * pow((temp - 60), -0.1332047592)); r = round(329.698727446 * pow((temp - 60), -0.1332047592));
g = round(288.1221695283 * pow((temp - 60), -0.0755148492)); g = round(288.1221695283 * pow((temp - 60), -0.0755148492));
b = 255; b = 255;
} }
//g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish //g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish
rgb[0] = (uint8_t) constrain(r, 0, 255); rgb[0] = (uint8_t) constrain(r, 0, 255);
rgb[1] = (uint8_t) constrain(g, 0, 255); rgb[1] = (uint8_t) constrain(g, 0, 255);
@ -194,7 +194,7 @@ void colorFromDecOrHexString(byte* rgb, char* in)
if (in[0] == 0) return; if (in[0] == 0) return;
char first = in[0]; char first = in[0];
uint32_t c = 0; uint32_t c = 0;
if (first == '#' || first == 'h' || first == 'H') //is HEX encoded if (first == '#' || first == 'h' || first == 'H') //is HEX encoded
{ {
c = strtoul(in +1, NULL, 16); c = strtoul(in +1, NULL, 16);

View File

@ -25,25 +25,44 @@
#ifndef WLED_MAX_BUSSES #ifndef WLED_MAX_BUSSES
#ifdef ESP8266 #ifdef ESP8266
#define WLED_MAX_BUSSES 3 #define WLED_MAX_BUSSES 3
#define WLED_MIN_VIRTUAL_BUSSES 2
#else #else
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM
#define WLED_MAX_BUSSES 3 // will allow 2 digital & 1 analog (or the other way around) #define WLED_MAX_BUSSES 3 // will allow 2 digital & 1 analog (or the other way around)
#define WLED_MIN_VIRTUAL_BUSSES 3
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
#if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33 #if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog #define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog
#define WLED_MIN_VIRTUAL_BUSSES 4
#else #else
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog
#define WLED_MIN_VIRTUAL_BUSSES 3
#endif #endif
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog #define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog
#define WLED_MIN_VIRTUAL_BUSSES 4
#else #else
#if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33 #if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
#define WLED_MAX_BUSSES 8 #define WLED_MAX_BUSSES 8
#define WLED_MIN_VIRTUAL_BUSSES 2
#else #else
#define WLED_MAX_BUSSES 10 #define WLED_MAX_BUSSES 10
#define WLED_MIN_VIRTUAL_BUSSES 0
#endif #endif
#endif #endif
#endif #endif
#else
#ifdef ESP8266
#if WLED_MAX_BUSES > 5
#error Maximum number of buses is 5.
#endif
#define WLED_MIN_VIRTUAL_BUSSES (5-WLED_MAX_BUSSES)
#else
#if WLED_MAX_BUSES > 10
#error Maximum number of buses is 10.
#endif
#define WLED_MIN_VIRTUAL_BUSSES (10-WLED_MAX_BUSSES)
#endif
#endif #endif
#ifndef WLED_MAX_BUTTONS #ifndef WLED_MAX_BUTTONS
@ -79,7 +98,7 @@
#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h" #define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h"
#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h" #define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h"
#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h" #define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h"
#define USERMOD_ID_BATTERY_STATUS_BASIC 18 //Usermod "usermod_v2_battery_status_basic.h" #define USERMOD_ID_BATTERY 18 //Usermod "usermod_v2_battery.h"
#define USERMOD_ID_PWM_FAN 19 //Usermod "usermod_PWM_fan.h" #define USERMOD_ID_PWM_FAN 19 //Usermod "usermod_PWM_fan.h"
#define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h" #define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h"
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h" #define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
@ -294,7 +313,7 @@
#define NTP_PACKET_SIZE 48 #define NTP_PACKET_SIZE 48
//maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses //maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses
#ifndef MAX_LEDS #ifndef MAX_LEDS
#ifdef ESP8266 #ifdef ESP8266
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs #define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
@ -323,7 +342,7 @@
#ifdef ESP8266 #ifdef ESP8266
#define SETTINGS_STACK_BUF_SIZE 2048 #define SETTINGS_STACK_BUF_SIZE 2048
#else #else
#define SETTINGS_STACK_BUF_SIZE 3096 #define SETTINGS_STACK_BUF_SIZE 3096
#endif #endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET

View File

@ -436,6 +436,9 @@ button {
left: 50%; left: 50%;
margin-left: -92px; margin-left: -92px;
/* Ensure tooltip goes away when mouse leaves control */
pointer-events: none;
/* Fade in tooltip */ /* Fade in tooltip */
opacity: 0; opacity: 0;
transition: opacity 0.75s; transition: opacity 0.75s;
@ -865,8 +868,8 @@ select {
transition-duration: 0.5s; transition-duration: 0.5s;
-webkit-backface-visibility: hidden; -webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0); -webkit-transform:translate3d(0,0,0);
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
backface-visibility: hidden; backface-visibility: hidden;
transform:translate3d(0,0,0); transform:translate3d(0,0,0);
text-overflow: ellipsis; text-overflow: ellipsis;
@ -890,8 +893,8 @@ div.sel-p:after {
position: absolute; position: absolute;
right: 10px; right: 10px;
top: 22px; top: 22px;
width: 0; width: 0;
height: 0; height: 0;
border-left: 8px solid transparent; border-left: 8px solid transparent;
border-right: 8px solid transparent; border-right: 8px solid transparent;
border-top: 8px solid var(--c-f); border-top: 8px solid var(--c-f);
@ -1221,7 +1224,7 @@ TD .checkmark, TD .radiomark {
line-height: 24px; line-height: 24px;
vertical-align: middle; vertical-align: middle;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%); filter: grayscale(100%);
} }
.lbl-l { .lbl-l {
@ -1331,7 +1334,7 @@ TD .checkmark, TD .radiomark {
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%); filter: grayscale(100%);
} }
/* list item palette preview */ /* list item palette preview */
@ -1362,7 +1365,7 @@ TD .checkmark, TD .radiomark {
background: var(--c-2); background: var(--c-2);
border: 1px solid var(--c-3); border: 1px solid var(--c-3);
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%); filter: grayscale(100%);
} }
.fnd input[type="text"]:focus { .fnd input[type="text"]:focus {

View File

@ -273,9 +273,9 @@ function onLoad()
function updateTablinks(tabI) function updateTablinks(tabI)
{ {
var tablinks = gEBCN("tablinks"); var tablinks = gEBCN("tablinks");
for (var i of tablinks) i.classList.remove("active"); for (var i of tablinks) i.classList.remove('active');
if (pcMode) return; if (pcMode) return;
tablinks[tabI].classList.add("active"); tablinks[tabI].classList.add('active');
} }
function openTab(tabI, force = false) function openTab(tabI, force = false)
@ -291,13 +291,13 @@ var timeout;
function showToast(text, error = false) function showToast(text, error = false)
{ {
if (error) gId('connind').style.backgroundColor = "var(--c-r)"; if (error) gId('connind').style.backgroundColor = "var(--c-r)";
var x = gId("toast"); var x = gId('toast');
//if (error) text += '<i class="icons btn-icon" style="transform:rotate(45deg);position:absolute;top:10px;right:0px;" onclick="clearErrorToast(100);">&#xe18a;</i>'; //if (error) text += '<i class="icons btn-icon" style="transform:rotate(45deg);position:absolute;top:10px;right:0px;" onclick="clearErrorToast(100);">&#xe18a;</i>';
x.innerHTML = text; x.innerHTML = text;
x.classList.add(error ? "error":"show"); x.classList.add(error ? 'error':'show');
clearTimeout(timeout); clearTimeout(timeout);
x.style.animation = 'none'; x.style.animation = 'none';
timeout = setTimeout(()=>{ x.classList.remove("show"); }, 2900); timeout = setTimeout(()=>{ x.classList.remove('show'); }, 2900);
if (error) console.log(text); if (error) console.log(text);
} }
@ -308,12 +308,12 @@ function showErrorToast()
function clearErrorToast(n=5000) function clearErrorToast(n=5000)
{ {
var x = gId("toast"); var x = gId('toast');
if (x.classList.contains("error")) { if (x.classList.contains('error')) {
clearTimeout(timeout); clearTimeout(timeout);
timeout = setTimeout(()=>{ timeout = setTimeout(()=>{
x.classList.remove("show"); x.classList.remove('show');
x.classList.remove("error"); x.classList.remove('error');
}, n); }, n);
} }
} }
@ -544,8 +544,8 @@ function populateQL()
for (var key of (pQL||[])) { for (var key of (pQL||[])) {
cn += `<button class="btn btn-xs psts" id="p${key[0]}qlb" title="${key[2]?key[2]:''}" onclick="setPreset(${key[0]});">${key[1]}</button>`; cn += `<button class="btn btn-xs psts" id="p${key[0]}qlb" title="${key[2]?key[2]:''}" onclick="setPreset(${key[0]});">${key[1]}</button>`;
} }
gId('pql').classList.add("expanded"); gId('pql').classList.add('expanded');
} else gId('pql').classList.remove("expanded"); } else gId('pql').classList.remove('expanded');
gId('pql').innerHTML = cn; gId('pql').innerHTML = cn;
} }
@ -610,8 +610,8 @@ function parseInfo(i) {
mh = i.leds.matrix ? i.leds.matrix.h : 0; mh = i.leds.matrix ? i.leds.matrix.h : 0;
isM = mw>0 && mh>0; isM = mw>0 && mh>0;
if (!isM) { if (!isM) {
gId("filter1D").classList.add("hide"); gId("filter1D").classList.add('hide');
//gId("filter2D").classList.add("hide"); //gId("filter2D").classList.add('hide');
hideModes("2D"); hideModes("2D");
} }
// if (i.noaudio) { // if (i.noaudio) {
@ -697,7 +697,7 @@ function populateSegments(s)
if (i > lSeg) lSeg = i; if (i > lSeg) lSeg = i;
let sg = gId(`seg${i}`); let sg = gId(`seg${i}`);
let exp = sg ? (sg.classList.contains("expanded") || (i===0 && cfg.comp.segexp)) : false; let exp = sg ? (sg.classList.contains('expanded') || (i===0 && cfg.comp.segexp)) : false;
let segp = `<div id="segp${i}" class="sbs"> let segp = `<div id="segp${i}" class="sbs">
<i class="icons e-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i> <i class="icons e-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>
@ -807,14 +807,14 @@ function populateSegments(s)
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline"; if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
if (Array.isArray(li.maps) && li.maps.length>1) { if (!isM && Array.isArray(li.maps) && li.maps.length>1) {
let cont = `Ledmap:&nbsp;<select class="sel-sg" onchange="requestJson({'ledmap':parseInt(this.value)})"><option value="" selected>Unchanged</option>`; let cont = `Ledmap:&nbsp;<select class="sel-sg" onchange="requestJson({'ledmap':parseInt(this.value)})"><option value="" selected>Unchanged</option>`;
for (const k of (li.maps||[])) cont += `<option value="${k}">${k==0?'Default':'ledmap'+k+'.json'}</option>`; for (const k of (li.maps||[])) cont += `<option value="${k}">${k==0?'Default':'ledmap'+k+'.json'}</option>`;
cont += "</select></div>"; cont += "</select></div>";
gId("ledmap").innerHTML = cont; gId("ledmap").innerHTML = cont;
gId("ledmap").classList.remove("hide"); gId("ledmap").classList.remove('hide');
} else { } else {
gId("ledmap").classList.add("hide"); gId("ledmap").classList.add('hide');
} }
} }
@ -837,7 +837,7 @@ function populateEffects()
}); });
for (let ef of effects) { for (let ef of effects) {
// WLEDSR: add slider and color control to setFX (used by requestjson) // add slider and color control to setFX (used by requestjson)
let id = ef.id; let id = ef.id;
let nm = ef.name+" "; let nm = ef.name+" ";
let fd = ""; let fd = "";
@ -1057,20 +1057,20 @@ function updateLen(s)
let tPL = gId(`seg${s}lbtm`); let tPL = gId(`seg${s}lbtm`);
if (stop-start>1 && stopY-startY>1) { if (stop-start>1 && stopY-startY>1) {
// 2D segment // 2D segment
if (tPL) tPL.classList.remove("hide"); // unhide transpose checkbox if (tPL) tPL.classList.remove('hide'); // unhide transpose checkbox
let sE = gId('fxlist').querySelector(`.lstI[data-id="${selectedFx}"]`); let sE = gId('fxlist').querySelector(`.lstI[data-id="${selectedFx}"]`);
if (sE) { if (sE) {
let sN = sE.querySelector(".lstIname").innerText; let sN = sE.querySelector(".lstIname").innerText;
let seg = gId(`seg${s}map2D`); let seg = gId(`seg${s}map2D`);
if (seg) { if (seg) {
if(sN.indexOf("\u25A6")<0) seg.classList.remove("hide"); // unhide mapping for 1D effects (| in name) if(sN.indexOf("\u25A6")<0) seg.classList.remove('hide'); // unhide mapping for 1D effects (| in name)
else seg.classList.add("hide"); // hide mapping otherwise else seg.classList.add('hide'); // hide mapping otherwise
} }
} }
} else { } else {
// 1D segment in 2D set-up // 1D segment in 2D set-up
if (tPL) { if (tPL) {
tPL.classList.add("hide"); // hide transpose checkbox tPL.classList.add('hide'); // hide transpose checkbox
gId(`seg${s}tp`).checked = false; // and uncheck it gId(`seg${s}tp`).checked = false; // and uncheck it
} }
} }
@ -1102,7 +1102,7 @@ function updatePA()
ps = gEBCN("psts"); for (let p of ps) p.classList.remove('selected'); ps = gEBCN("psts"); for (let p of ps) p.classList.remove('selected');
if (currentPreset > 0) { if (currentPreset > 0) {
var acv = gId(`p${currentPreset}o`); var acv = gId(`p${currentPreset}o`);
if (acv /*&& !acv.classList.contains("expanded")*/) { if (acv /*&& !acv.classList.contains('expanded')*/) {
acv.classList.add('selected'); acv.classList.add('selected');
/* /*
// scroll selected preset into view (on WS refresh) // scroll selected preset into view (on WS refresh)
@ -1119,13 +1119,13 @@ function updatePA()
function updateUI() function updateUI()
{ {
gId('buttonPower').className = (isOn) ? "active":""; gId('buttonPower').className = (isOn) ? 'active':'';
gId('buttonNl').className = (nlA) ? "active":""; gId('buttonNl').className = (nlA) ? 'active':'';
gId('buttonSync').className = (syncSend) ? "active":""; gId('buttonSync').className = (syncSend) ? 'active':'';
showNodes(); showNodes();
updateSelectedPalette();
updateSelectedFx(); updateSelectedFx();
updateSelectedPalette(selectedPal); // must be after updateSelectedFx() to un-hide color slots for * palettes
updateTrail(gId('sliderBri')); updateTrail(gId('sliderBri'));
updateTrail(gId('sliderSpeed')); updateTrail(gId('sliderSpeed'));
@ -1159,17 +1159,27 @@ function updateUI()
updatePSliders(); updatePSliders();
} }
function updateSelectedPalette() function updateSelectedPalette(s)
{ {
var parent = gId('pallist'); var parent = gId('pallist');
var selPaletteInput = parent.querySelector(`input[name="palette"][value="${selectedPal}"]`); var selPaletteInput = parent.querySelector(`input[name="palette"][value="${s}"]`);
if (selPaletteInput) selPaletteInput.checked = true; if (selPaletteInput) selPaletteInput.checked = true;
var selElement = parent.querySelector('.selected'); var selElement = parent.querySelector('.selected');
if (selElement) selElement.classList.remove('selected'); if (selElement) selElement.classList.remove('selected');
var selectedPalette = parent.querySelector(`.lstI[data-id="${selectedPal}"]`); var selectedPalette = parent.querySelector(`.lstI[data-id="${s}"]`);
if (selectedPalette) parent.querySelector(`.lstI[data-id="${selectedPal}"]`).classList.add('selected'); if (selectedPalette) parent.querySelector(`.lstI[data-id="${s}"]`).classList.add('selected');
// in case of special palettes (* Colors...), force show color selectors (if hidden by effect data)
let cd = gId('csl').children; // color selectors
if (s > 1 && s < 6) {
cd[0].classList.remove('hide'); // * Color 1
if (s > 2) cd[1].classList.remove('hide'); // * Color 1 & 2
if (s == 5) cd[2].classList.remove('hide'); // all colors
} else {
for (let i of cd) if (i.dataset.hide == '1') i.classList.add('hide');
}
} }
function updateSelectedFx() function updateSelectedFx()
@ -1191,9 +1201,9 @@ function updateSelectedFx()
var selectedName = selectedEffect.querySelector(".lstIname").innerText; var selectedName = selectedEffect.querySelector(".lstIname").innerText;
var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`); var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`);
for (const seg of segs) if (selectedName.indexOf("\u25A6")<0) seg.classList.remove("hide"); else seg.classList.add("hide"); for (const seg of segs) if (selectedName.indexOf("\u25A6")<0) seg.classList.remove('hide'); else seg.classList.add('hide');
var segs = gId("segcont").querySelectorAll(`div[data-snd="si"]`); var segs = gId("segcont").querySelectorAll(`div[data-snd="si"]`);
for (const seg of segs) if (selectedName.indexOf("\u266A")<0 && selectedName.indexOf("\266B")<0) seg.classList.add("hide"); else seg.classList.remove("hide"); // also "♫ "? for (const seg of segs) if (selectedName.indexOf("\u266A")<0 && selectedName.indexOf("\266B")<0) seg.classList.add('hide'); else seg.classList.remove('hide'); // also "♫ "?
} }
} }
@ -1352,7 +1362,7 @@ function readState(s,command=false)
return true; return true;
} }
// WLEDSR: control HTML elements for Slider and Color Control // control HTML elements for Slider and Color Control (original ported form WLED-SR)
// Technical notes // Technical notes
// =============== // ===============
// If an effect name is followed by an @, slider and color control is effective. // If an effect name is followed by an @, slider and color control is effective.
@ -1394,9 +1404,9 @@ function setEffectParameters(idx)
else if (i==0) label.innerHTML = "Effect speed"; else if (i==0) label.innerHTML = "Effect speed";
else if (i==1) label.innerHTML = "Effect intensity"; else if (i==1) label.innerHTML = "Effect intensity";
else label.innerHTML = "Custom" + (i-1); else label.innerHTML = "Custom" + (i-1);
slider.classList.remove("hide"); slider.classList.remove('hide');
} else { } else {
slider.classList.add("hide"); slider.classList.add('hide');
} }
} }
if (slOnOff.length>5) { // up to 3 checkboxes if (slOnOff.length>5) { // up to 3 checkboxes
@ -1422,14 +1432,14 @@ function setEffectParameters(idx)
// set html color items on/off // set html color items on/off
var cslLabel = ''; var cslLabel = '';
var sep = ''; var sep = '';
var hide = true;
var cslCnt = 0, oCsel = csel; var cslCnt = 0, oCsel = csel;
for (let i=0; i<gId("csl").children.length; i++) { for (let i=0; i<gId("csl").children.length; i++) {
var btn = gId("csl" + i); var btn = gId("csl" + i);
// if no controlDefined or coOnOff has a value // if no controlDefined or coOnOff has a value
if (coOnOff.length>i && coOnOff[i] != "") { if (coOnOff.length>i && coOnOff[i] != "") {
btn.style.display = "inline"; btn.classList.remove('hide');
if (coOnOff.length>i && coOnOff[i] != "!") { btn.dataset.hide = 0;
if (coOnOff[i] != "!") {
var abbreviation = coOnOff[i].substr(0,2); var abbreviation = coOnOff[i].substr(0,2);
btn.innerHTML = abbreviation; btn.innerHTML = abbreviation;
if (abbreviation != coOnOff[i]) { if (abbreviation != coOnOff[i]) {
@ -1440,17 +1450,18 @@ function setEffectParameters(idx)
else if (i==0) btn.innerHTML = "Fx"; else if (i==0) btn.innerHTML = "Fx";
else if (i==1) btn.innerHTML = "Bg"; else if (i==1) btn.innerHTML = "Bg";
else btn.innerHTML = "Cs"; else btn.innerHTML = "Cs";
hide = false;
if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one
cslCnt++; cslCnt++;
} else if (!controlDefined) { // if no controls then all buttons should be shown for color 1..3 } else if (!controlDefined) { // if no controls then all buttons should be shown for color 1..3
btn.style.display = "inline"; btn.classList.remove('hide');
btn.dataset.hide = 0;
btn.innerHTML = `${i+1}`; btn.innerHTML = `${i+1}`;
hide = false;
if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one
cslCnt++; cslCnt++;
} else { } else {
btn.style.display = "none"; btn.classList.add('hide');
btn.dataset.hide = 1;
btn.innerHTML = `${i+1}`; // name hidden buttons 1..3 for * palettes
} }
} }
gId("cslLabel").innerHTML = cslLabel; gId("cslLabel").innerHTML = cslLabel;
@ -1475,12 +1486,13 @@ function setEffectParameters(idx)
palw.style.display = "none"; palw.style.display = "none";
} }
// not all color selectors shown, hide palettes created from color selectors // not all color selectors shown, hide palettes created from color selectors
for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) { // NOTE: this will disallow user to select "* Color ..." palettes which may be undesirable in some cases or for some users
let fltr = "* C"; //for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) {
if (cslCnt==1 && csel==0) fltr = "* Colors"; // let fltr = "* C";
else if (cslCnt==2) fltr = "* Colors Only"; // if (cslCnt==1 && csel==0) fltr = "* Colors";
if (cslCnt < 3 && e.querySelector('.lstIname').innerText.indexOf(fltr)>=0) e.classList.add('hide'); else e.classList.remove('hide'); // else if (cslCnt==2) fltr = "* Colors Only";
} // if (cslCnt < 3 && e.querySelector('.lstIname').innerText.indexOf(fltr)>=0) e.classList.add('hide'); else e.classList.remove('hide');
//}
} }
var jsonTimeout; var jsonTimeout;
@ -1836,7 +1848,7 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
<input type="checkbox" id="p${i}sbchk"> <input type="checkbox" id="p${i}sbchk">
<span class="checkmark"></span> <span class="checkmark"></span>
</label>`; </label>`;
if (Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) { if (!isM && Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
content += `<div class="lbl-l">Ledmap:&nbsp;<div class="sel-p"><select class="sel-p" id="p${i}lmp"><option value="">Unchanged</option>`; content += `<div class="lbl-l">Ledmap:&nbsp;<div class="sel-p"><select class="sel-p" id="p${i}lmp"><option value="">Unchanged</option>`;
for (const k of (lastinfo.maps||[])) content += `<option value="${k}"${(i>0 && pJson[i].ledmap==k)?" selected":""}>${k==0?'Default':'ledmap'+k+'.json'}</option>`; for (const k of (lastinfo.maps||[])) content += `<option value="${k}"${(i>0 && pJson[i].ledmap==k)?" selected":""}>${k==0?'Default':'ledmap'+k+'.json'}</option>`;
content += "</select></div></div>"; content += "</select></div></div>";
@ -1949,7 +1961,7 @@ function tglSegn(s)
{ {
let t = gId(s<100?`seg${s}t`:`p${s-100}txt`); let t = gId(s<100?`seg${s}t`:`p${s-100}txt`);
if (t) { if (t) {
t.classList.toggle("show"); t.classList.toggle('show');
t.focus(); t.focus();
t.select(); t.select();
} }
@ -1966,7 +1978,6 @@ function selSegAll(o)
function selSegEx(s) function selSegEx(s)
{ {
if (gId('selall')) gId('selall').checked = false;
var obj = {"seg":[]}; var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":(i==s)}); for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":(i==s)});
obj.mainseg = s; obj.mainseg = s;
@ -1975,7 +1986,6 @@ function selSegEx(s)
function selSeg(s) function selSeg(s)
{ {
if (gId('selall')) gId('selall').checked = false;
var sel = gId(`seg${s}sel`).checked; var sel = gId(`seg${s}sel`).checked;
var obj = {"seg": {"id": s, "sel": sel}}; var obj = {"seg": {"id": s, "sel": sel}};
requestJson(obj); requestJson(obj);
@ -1991,7 +2001,7 @@ function rptSeg(s)
var rev = gId(`seg${s}rev`).checked; var rev = gId(`seg${s}rev`).checked;
var mi = gId(`seg${s}mi`).checked; var mi = gId(`seg${s}mi`).checked;
var sel = gId(`seg${s}sel`).checked; var sel = gId(`seg${s}sel`).checked;
var pwr = gId(`seg${s}pwr`).classList.contains("act"); var pwr = gId(`seg${s}pwr`).classList.contains('act');
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": pwr, "bri": parseInt(gId(`seg${s}bri`).value), "sel": sel}}; var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": pwr, "bri": parseInt(gId(`seg${s}bri`).value), "sel": sel}};
if (gId(`seg${s}grp`)) { if (gId(`seg${s}grp`)) {
var grp = parseInt(gId(`seg${s}grp`).value); var grp = parseInt(gId(`seg${s}grp`).value);
@ -2102,7 +2112,7 @@ function setTp(s)
function setSegPwr(s) function setSegPwr(s)
{ {
var pwr = gId(`seg${s}pwr`).classList.contains("act"); var pwr = gId(`seg${s}pwr`).classList.contains('act');
var obj = {"seg": {"id": s, "on": !pwr}}; var obj = {"seg": {"id": s, "on": !pwr}};
requestJson(obj); requestJson(obj);
} }
@ -2296,7 +2306,7 @@ function delP(i) {
requestJson(obj); requestJson(obj);
delete pJson[i]; delete pJson[i];
populatePresets(); populatePresets();
gId('putil').classList.add("staybot"); gId('putil').classList.add('staybot');
} else { } else {
bt.style.color = "var(--c-r)"; bt.style.color = "var(--c-r)";
bt.innerHTML = "<i class='icons btn-icon'>&#xe037;</i>Delete!"; bt.innerHTML = "<i class='icons btn-icon'>&#xe037;</i>Delete!";
@ -2571,7 +2581,7 @@ function hideModes(txt)
let f = false; let f = false;
if (txt==="2D") f = iT.indexOf("\u25A6") >= 0 && iT.indexOf("\u22EE") < 0; // 2D && !1D if (txt==="2D") f = iT.indexOf("\u25A6") >= 0 && iT.indexOf("\u22EE") < 0; // 2D && !1D
else f = iT.indexOf(txt) >= 0; else f = iT.indexOf(txt) >= 0;
if (f) e.classList.add("hide"); //else e.classList.remove("hide"); if (f) e.classList.add('hide'); //else e.classList.remove('hide');
} }
} }
@ -2642,9 +2652,9 @@ function expand(i)
{ {
var seg = i<100 ? gId('seg' +i) : gId(`p${i-100}o`); var seg = i<100 ? gId('seg' +i) : gId(`p${i-100}o`);
let ps = gId("pcont").children; // preset wrapper let ps = gId("pcont").children; // preset wrapper
if (i>100) for (let p of ps) { p.classList.remove("selected"); if (p!==seg) p.classList.remove("expanded"); } // collapse all other presets & remove selected if (i>100) for (let p of ps) { p.classList.remove('selected'); if (p!==seg) p.classList.remove('expanded'); } // collapse all other presets & remove selected
seg.classList.toggle("expanded"); seg.classList.toggle('expanded');
// presets // presets
if (i >= 100) { if (i >= 100) {
@ -2666,11 +2676,11 @@ function expand(i)
gId(`p${p}api`).value = papi; gId(`p${p}api`).value = papi;
if (papi.indexOf("Please") == 0) gId(`p${p}cstgl`).checked = false; if (papi.indexOf("Please") == 0) gId(`p${p}cstgl`).checked = false;
tglCs(p); tglCs(p);
gId('putil').classList.remove("staybot"); gId('putil').classList.remove('staybot');
} else { } else {
updatePA(); updatePA();
gId('seg' +i).innerHTML = ""; gId('seg' +i).innerHTML = "";
gId('putil').classList.add("staybot"); gId('putil').classList.add('staybot');
} }
} }

View File

@ -8,6 +8,7 @@
<script> <script>
var d=document; var d=document;
var loc = false, locip; var loc = false, locip;
var maxPanels=64;
function H(){window.open("https://kno.wled.ge/features/2D");} function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open("/settings","_self");} function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);} function gId(n){return d.getElementById(n);}
@ -23,6 +24,7 @@
//console.log("File loaded"); //console.log("File loaded");
GetV(); GetV();
UI(); UI();
Sf.MPC.setAttribute("max",maxPanels);
}); });
// error event // error event
scE.addEventListener("error", (ev) => { scE.addEventListener("error", (ev) => {
@ -43,34 +45,29 @@
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
} }
var maxPanels=64; function UI() {
function UI(change=false)
{
if (gId("somp").value === "0") { if (gId("somp").value === "0") {
gId("mpdiv").style.display = "none"; gId("mpdiv").style.display = "none";
resetPanels(); resetPanels();
return; return;
} }
gId("mpdiv").style.display = "block"; gId("mpdiv").style.display = "block";
maxPanels = parseInt(d.Sf.MPH.value) * parseInt(d.Sf.MPV.value);
let i = gId("panels").children.length;
if (i<maxPanels) for (let j=i; j<maxPanels; j++) addPanel(j);
if (i>maxPanels) for (let j=i; j>maxPanels; j--) remPanel();
//btnPanel(gId("panels").children.length);
} }
function addPanels() { function addPanels() {
let h = parseInt(d.Sf.MPH.value); let c = parseInt(d.Sf.MPC.value);
let v = parseInt(d.Sf.MPV.value); let i = gId("panels").children.length;
for (let i=0; i<h*v; i++) addPanel(i); if (i<c) for (let j=i; j<c; j++) addPanel(j);
if (i>c) for (let j=i; j>c; j--) remPanel();
} }
function addPanel(i=0) { function addPanel(i=0) {
let p = gId("panels"); let p = gId("panels");
if (p.children.length >= maxPanels) return; if (p.children.length >= maxPanels) return;
let b = `<div id="pnl${i}">${i===0?"":'<hr class="sml">'}Panel ${i}<br>1<sup>st</sup> LED: <select name="P${i}B"> var pw = parseInt(d.Sf.PW.value);
var ph = parseInt(d.Sf.PH.value);
let b = `<div id="pnl${i}"><hr class="sml">Panel ${i}<br>
1<sup>st</sup> LED: <select name="P${i}B">
<option value="0">Top</option> <option value="0">Top</option>
<option value="1">Bottom</option> <option value="1">Bottom</option>
</select><select name="P${i}R"> </select><select name="P${i}R">
@ -81,7 +78,11 @@ Orientation: <select name="P${i}V">
<option value="0">Horizontal</option> <option value="0">Horizontal</option>
<option value="1">Vertical</option> <option value="1">Vertical</option>
</select><br> </select><br>
Serpentine: <input type="checkbox" name="P${i}S"></div>`; Serpentine: <input type="checkbox" name="P${i}S"><br>
Dimensions (WxH): <input id="P${i}W" name="P${i}W" type="number" min="1" max="128" value="${pw}"> x <input id="P${i}H" name="P${i}H" type="number" min="1" max="128" value="${ph}"><br>
Offset X:<input id="P${i}X" name="P${i}X" type="number" min="0" max="256" value="0">
Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br><i>(offset from top-left corner in # LEDs)</i>
</div>`;
p.insertAdjacentHTML("beforeend", b); p.insertAdjacentHTML("beforeend", b);
} }
@ -93,15 +94,46 @@ Serpentine: <input type="checkbox" name="P${i}S"></div>`;
} }
function resetPanels() { function resetPanels() {
d.Sf.MPH.value = 1; d.Sf.MPC.value = 1;
d.Sf.MPV.value = 1; let e = gId("panels").children
for (let e of gId("panels").children) e.remove(); for (let i = e.length; i>0; i--) e[i-1].remove();
} }
/*
function btnPanel(i) { function btnPanel(i) {
gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none"; gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none";
gId("pnl_rem").style.display = (i>1) ? "inline":"none"; gId("pnl_rem").style.display = (i>1) ? "inline":"none";
} }
*/
function gen() {
resetPanels();
var pansH = parseInt(d.Sf.MPH.value);
var pansV = parseInt(d.Sf.MPV.value);
var c = pansH*pansV;
d.Sf.MPC.value = c; // number of panels
var ps = d.Sf.PS.checked;
var pv = d.Sf.PV.value==="1";
var pb = d.Sf.PB.value==="1";
var pr = d.Sf.PR.value==="1";
var pw = parseInt(d.Sf.PW.value);
var ph = parseInt(d.Sf.PH.value);
var h = pv ? pansV : pansH;
var v = pv ? pansH : pansV;
for (let j = 0, p = 0; j < v; j++) {
for (let i = 0; i < h; i++, p++) {
if (j*i < maxPanels) addPanel(p);
var y = (pv?pr:pb) ? v-j-1: j;
var x = (pv?pb:pr) ? h-i-1 : i;
x = ps && j%2 ? h-x-1 : x;
gId("P"+p+"X").value = (pv?y:x) * pw;
gId("P"+p+"Y").value = (pv?x:y) * ph
gId("P"+p+"W").value = pw;
gId("P"+p+"H").value = ph;
}
}
}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>
@ -118,10 +150,11 @@ Serpentine: <input type="checkbox" name="P${i}S"></div>`;
<option value="1">2D Matrix</option> <option value="1">2D Matrix</option>
</select><br> </select><br>
<div id="mpdiv" style="display:none;"> <div id="mpdiv" style="display:none;">
<h3>Panel set-up</h3> <hr class="sml">
<h3>Matrix Generator</h3>
Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br> Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br>
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1" oninput="UI()"> Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1">
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1" oninput="UI()"><br> Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1"><br>
1<sup>st</sup> panel: <select name="PB"> 1<sup>st</sup> panel: <select name="PB">
<option value="0">Top</option> <option value="0">Top</option>
<option value="1">Bottom</option> <option value="1">Bottom</option>
@ -133,12 +166,15 @@ Serpentine: <input type="checkbox" name="P${i}S"></div>`;
<option value="0">Horizontal</option> <option value="0">Horizontal</option>
<option value="1">Vertical</option> <option value="1">Vertical</option>
</select><br> </select><br>
Serpentine: <input type="checkbox" name="PS"> Serpentine: <input type="checkbox" name="PS"><br>
<hr class="sml"> <i style="color:#fa0;">Can populate LED panel layout with pre-arranged matrix.<br>These values do not affect final layout.</i><br>
<i>A matrix is made of 1 or more physical LED panels of the same dimensions.<br> <button type="button" onclick="gen()">Populate</button>
Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<br>
Each panel can have different LED orientation and/or starting point and/or layout.</i><br>
<hr class="sml"> <hr class="sml">
<h3>Panel set-up</h3>
Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels()"><br>
<i>A matrix is made of 1 or more physical LED panels.<br>
<!--Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<br>-->
Each panel can be of different size and/or have different LED orientation and/or starting point and/or layout.</i><br>
<h3>LED panel layout</h3> <h3>LED panel layout</h3>
<div id="panels"> <div id="panels">
</div> </div>

View File

@ -6,7 +6,7 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>LED Settings</title> <title>LED Settings</title>
<script> <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 d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var customStarts=false,startsDirty=[],maxCOOverrides=5; var customStarts=false,startsDirty=[],maxCOOverrides=5;
var loc = false, locip; var loc = false, locip;
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");} function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
@ -46,8 +46,8 @@
x.style.animation = 'none'; x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
} }
function bLimits(b,p,m,l) { function bLimits(b,v,p,m,l) {
maxB = b; maxM = m; maxPB = p; maxL = l; maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l;
} }
function pinsOK() { function pinsOK() {
var LCs = d.getElementsByTagName("input"); var LCs = d.getElementsByTagName("input");
@ -316,7 +316,7 @@
var o = d.getElementsByClassName("iST"); var o = d.getElementsByClassName("iST");
var i = o.length; var i = o.length;
if ((n==1 && i>=maxB) || (n==-1 && i==0)) return; if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return;
var f = gId("mLC"); var f = gId("mLC");
if (n==1) { if (n==1) {
@ -324,24 +324,24 @@
var cn = `<div class="iST"> var cn = `<div class="iST">
<hr class="sml"> <hr class="sml">
${i+1}: ${i+1}:
<select name="LT${i}" onchange="UI(true)"> <select name="LT${i}" onchange="UI(true)">${i>=maxB ? '' :
<option value="22" selected>WS281x</option> '<option value="22" selected>WS281x</option>\
<option value="30">SK6812 RGBW</option> <option value="30">SK6812 RGBW</option>\
<option value="31">TM1814</option> <option value="31">TM1814</option>\
<option value="24">400kHz</option> <option value="24">400kHz</option>\
<option value="25">TM1829</option> <option value="25">TM1829</option>\
<option value="50">WS2801</option> <option value="50">WS2801</option>\
<option value="51">APA102</option> <option value="51">APA102</option>\
<option value="52">LPD8806</option> <option value="52">LPD8806</option>\
<option value="54">LPD6803</option> <option value="54">LPD6803</option>\
<option value="53">P9813</option> <option value="53">P9813</option>\
<option value="40">On/Off</option> <option value="40">On/Off</option>\
<option value="41">PWM White</option> <option value="41">PWM White</option>\
<option value="42">PWM CCT</option> <option value="42">PWM CCT</option>\
<option value="43">PWM RGB</option> <option value="43">PWM RGB</option>\
<option value="44">PWM RGBW</option> <option value="44">PWM RGBW</option>\
<option value="45">PWM RGB+CCT</option> <option value="45">PWM RGB+CCT</option>\
<!--option value="46">PWM RGB+DCCT</option--> <!--option value="46">PWM RGB+DCCT</option-->'}
<option value="80">DDP RGB (network)</option> <option value="80">DDP RGB (network)</option>
<!--option value="81">E1.31 RGB (network)</option--> <!--option value="81">E1.31 RGB (network)</option-->
<!--option value="82">ArtNet RGB (network)</option--> <!--option value="82">ArtNet RGB (network)</option-->
@ -377,7 +377,7 @@ ${i+1}:
o[--i].remove();--i; o[--i].remove();--i;
} }
gId("+").style.display = (i<maxB-1) ? "inline":"none"; gId("+").style.display = (i<maxB+maxV-1) ? "inline":"none";
gId("-").style.display = (i>0) ? "inline":"none"; gId("-").style.display = (i>0) ? "inline":"none";
if (!init) UI(); if (!init) UI();

View File

@ -89,7 +89,7 @@
</div> </div>
<h2>Security & Update setup</h2> <h2>Security & Update setup</h2>
Settings PIN: <input type="password" id="PIN" name="PIN" size="4" maxlength="4" minlength="4" onkeydown="checkNum(this)" pattern="[0-9]*" inputmode="numeric" title="Please enter a 4 digit number"><br> Settings PIN: <input type="password" id="PIN" name="PIN" size="4" maxlength="4" minlength="4" onkeydown="checkNum(this)" pattern="[0-9]*" inputmode="numeric" title="Please enter a 4 digit number"><br>
<div style="color: #fa0;">&#9888; Unencrypted transmission. Be prudent when selecting PIN, do NOT use your banking, door, SIM, etc. pin!</div><br><br> <div style="color: #fa0;">&#9888; Unencrypted transmission. Be prudent when selecting PIN, do NOT use your banking, door, SIM, etc. pin!</div><br>
Lock wireless (OTA) software update: <input type="checkbox" name="NO"><br> Lock wireless (OTA) software update: <input type="checkbox" name="NO"><br>
Passphrase: <input type="password" name="OP" maxlength="32"><br> Passphrase: <input type="password" name="OP" maxlength="32"><br>
To enable OTA, for security reasons you need to also enter the correct password!<br> To enable OTA, for security reasons you need to also enter the correct password!<br>
@ -118,7 +118,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/" 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 href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br> A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2022 Christian Schwinne <br> (c) 2016-2023 Christian Schwinne <br>
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br> <i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
Server message: <span class="sip"> Response error! </span><hr> Server message: <span class="sip"> Response error! </span><hr>
<div id="toast"></div> <div id="toast"></div>

View File

@ -80,7 +80,7 @@
<h3>WLED Broadcast</h3> <h3>WLED Broadcast</h3>
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br> UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br> 2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>
<h3>Sync grups</h3> <h3>Sync groups</h3>
<input name="GS" id="GS" type="number" style="display: none;"><!-- hidden inputs for bitwise group checkboxes --> <input name="GS" id="GS" type="number" style="display: none;"><!-- hidden inputs for bitwise group checkboxes -->
<input name="GR" id="GR" type="number" style="display: none;"> <input name="GR" id="GR" type="number" style="display: none;">
<table style="margin: 0 auto;"> <table style="margin: 0 auto;">

View File

@ -7,6 +7,10 @@
<title>Usermod Settings</title> <title>Usermod Settings</title>
<script> <script>
var d = document; var d = document;
d.max_gpio = 39;
d.um_p = [];
d.rsvd = [];
d.ro_pins = [];
var umCfg = {}; var umCfg = {};
var pins = [], pinO = [], owner; var pins = [], pinO = [], owner;
var loc = false, locip; var loc = false, locip;
@ -25,11 +29,6 @@
d.body.appendChild(scE); d.body.appendChild(scE);
// success event // success event
scE.addEventListener("load", () => { scE.addEventListener("load", () => {
//console.log("File loaded");
d.um_p = [];
d.rsvd = [];
d.ro_pins = [];
d.max_gpio = 39;
GetV(); GetV();
for (let k=0; k<d.rsvd.length; k++) { pins.push(d.rsvd[k]); pinO.push("rsvd"); } for (let k=0; k<d.rsvd.length; k++) { pins.push(d.rsvd[k]); pinO.push("rsvd"); }
if (d.um_p[0]==-1) d.um_p.shift(); if (d.um_p[0]==-1) d.um_p.shift();
@ -38,6 +37,8 @@
d.Sf.MOSI.max = d.max_gpio; d.Sf.MOSI.max = d.max_gpio;
d.Sf.SCLK.max = d.max_gpio; d.Sf.SCLK.max = d.max_gpio;
d.Sf.MISO.max = d.max_gpio; d.Sf.MISO.max = d.max_gpio;
let inp = d.getElementsByTagName("input");
for (let i of inp) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
}); });
// error event // error event
scE.addEventListener("error", (ev) => { scE.addEventListener("error", (ev) => {

View File

@ -73,7 +73,7 @@ void initDMX() {
dmx.init(512); // initialize with bus length dmx.init(512); // initialize with bus length
#else #else
dmx.initWrite(512); // initialize with bus length dmx.initWrite(512); // initialize with bus length
#endif #endif
} }
#else #else

View File

@ -12,7 +12,7 @@
//handles RGB data only //handles RGB data only
void handleDDPPacket(e131_packet_t* p) { void handleDDPPacket(e131_packet_t* p) {
int lastPushSeq = e131LastSequenceNumber[0]; int lastPushSeq = e131LastSequenceNumber[0];
//reject late packets belonging to previous frame (assuming 4 packets max. before push) //reject late packets belonging to previous frame (assuming 4 packets max. before push)
if (e131SkipOutOfSequence && lastPushSeq) { if (e131SkipOutOfSequence && lastPushSeq) {
int sn = p->sequenceNum & 0xF; int sn = p->sequenceNum & 0xF;
@ -35,7 +35,7 @@ void handleDDPPacket(e131_packet_t* p) {
if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP); realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) { if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) {
for (uint16_t i = start; i < stop; i++) { for (uint16_t i = start; i < stop; i++) {
setRealtimePixel(i, data[c], data[c+1], data[c+2], ddpChannelsPerLed >3 ? data[c+3] : 0); setRealtimePixel(i, data[c], data[c+1], data[c+2], ddpChannelsPerLed >3 ? data[c+3] : 0);
@ -96,11 +96,11 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
if (e131SkipOutOfSequence) if (e131SkipOutOfSequence)
if (seq < e131LastSequenceNumber[previousUniverses] && seq > 20 && e131LastSequenceNumber[previousUniverses] < 250){ if (seq < e131LastSequenceNumber[previousUniverses] && seq > 20 && e131LastSequenceNumber[previousUniverses] < 250){
DEBUG_PRINT("skipping E1.31 frame (last seq="); DEBUG_PRINT(F("skipping E1.31 frame (last seq="));
DEBUG_PRINT(e131LastSequenceNumber[previousUniverses]); DEBUG_PRINT(e131LastSequenceNumber[previousUniverses]);
DEBUG_PRINT(", current seq="); DEBUG_PRINT(F(", current seq="));
DEBUG_PRINT(seq); DEBUG_PRINT(seq);
DEBUG_PRINT(", universe="); DEBUG_PRINT(F(", universe="));
DEBUG_PRINT(uni); DEBUG_PRINT(uni);
DEBUG_PRINTLN(")"); DEBUG_PRINTLN(")");
return; return;
@ -281,7 +281,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
strip.setBrightness(bri, true); strip.setBrightness(bri, true);
} }
} }
if (!is4Chan) { if (!is4Chan) {
for (uint16_t i = previousLeds; i < ledsTotal; i++) { for (uint16_t i = previousLeds; i < ledsTotal; i++) {
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0); setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0);
@ -487,8 +487,8 @@ void sendArtnetPollReply(ArtPollReply *reply, IPAddress ipAddress, uint16_t port
reply->reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F); reply->reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F);
reply->reply_sw_out[0] = (uint8_t)(portAddress & 0x000F); reply->reply_sw_out[0] = (uint8_t)(portAddress & 0x000F);
sprintf((char *)reply->reply_node_report, "#0001 [%04u] OK - WLED v" TOSTRING(WLED_VERSION), pollReplyCount); snprintf_P((char *)reply->reply_node_report, sizeof(reply->reply_node_report)-1, PSTR("#0001 [%04u] OK - WLED v" TOSTRING(WLED_VERSION)), pollReplyCount);
if (pollReplyCount < 9999) { if (pollReplyCount < 9999) {
pollReplyCount++; pollReplyCount++;
} else { } else {

View File

@ -172,7 +172,7 @@ void publishMqtt();
void handleTime(); void handleTime();
void handleNetworkTime(); void handleNetworkTime();
void sendNTPPacket(); void sendNTPPacket();
bool checkNTPResponse(); bool checkNTPResponse();
void updateLocalTime(); void updateLocalTime();
void getTimeString(char* out); void getTimeString(char* out);
bool checkCountdown(); bool checkCountdown();
@ -202,7 +202,7 @@ inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject()); void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
inline void saveTemporaryPreset() {savePreset(255);}; inline void saveTemporaryPreset() {savePreset(255);};
void deletePreset(byte index); void deletePreset(byte index);
bool getPresetName(byte index, String& name); bool getPresetName(byte index, String& name);
//set.cpp //set.cpp
bool isAsterisksOnly(const char* str, byte maxLen); bool isAsterisksOnly(const char* str, byte maxLen);

View File

@ -102,7 +102,7 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE); uint16_t bufsize = f.read(buf, FS_BUFSIZE);
uint16_t count = 0; uint16_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if(buf[count] == ' ') { if(buf[count] == ' ') {
if(++index >= targetLen) { // return true if space long enough if(++index >= targetLen) { // return true if space long enough
@ -144,7 +144,7 @@ bool bufferedFindObjectEnd() {
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE); uint16_t bufsize = f.read(buf, FS_BUFSIZE);
uint16_t count = 0; uint16_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if (buf[count] == '{') objDepth++; if (buf[count] == '{') objDepth++;
if (buf[count] == '}') objDepth--; if (buf[count] == '}') objDepth--;
@ -194,7 +194,7 @@ bool appendObjectToFile(const char* key, JsonDocument* content, uint32_t s, uint
doCloseFile = true; doCloseFile = true;
return true; //nothing to append return true; //nothing to append
} }
//if there is enough empty space in file, insert there instead of appending //if there is enough empty space in file, insert there instead of appending
if (!contentLen) contentLen = measureJson(*content); if (!contentLen) contentLen = measureJson(*content);
DEBUGFS_PRINTF("CLen %d\n", contentLen); DEBUGFS_PRINTF("CLen %d\n", contentLen);
@ -211,18 +211,18 @@ bool appendObjectToFile(const char* key, JsonDocument* content, uint32_t s, uint
//permitted space for presets exceeded //permitted space for presets exceeded
updateFSInfo(); updateFSInfo();
if (f.size() + 9000 > (fsBytesTotal - fsBytesUsed)) { //make sure there is enough space to at least copy the file once if (f.size() + 9000 > (fsBytesTotal - fsBytesUsed)) { //make sure there is enough space to at least copy the file once
errorFlag = ERR_FS_QUOTA; errorFlag = ERR_FS_QUOTA;
doCloseFile = true; doCloseFile = true;
return false; return false;
} }
//check if last character in file is '}' (typical) //check if last character in file is '}' (typical)
uint32_t eof = f.size() -1; uint32_t eof = f.size() -1;
f.seek(eof, SeekSet); f.seek(eof, SeekSet);
if (f.read() == '}') pos = eof; if (f.read() == '}') pos = eof;
if (pos == 0) //not found if (pos == 0) //not found
{ {
DEBUGFS_PRINTLN("not }"); DEBUGFS_PRINTLN("not }");
@ -277,12 +277,12 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
DEBUGFS_PRINTLN(F("Failed to open!")); DEBUGFS_PRINTLN(F("Failed to open!"));
return false; return false;
} }
if (!bufferedFind(key)) //key does not exist in file if (!bufferedFind(key)) //key does not exist in file
{ {
return appendObjectToFile(key, content, s); return appendObjectToFile(key, content, s);
} }
//an object with this key already exists, replace or delete it //an object with this key already exists, replace or delete it
pos = f.position(); pos = f.position();
//measure out end of old object //measure out end of old object
@ -297,7 +297,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
//2. The new content is smaller than the old, overwrite and fill diff with spaces //2. The new content is smaller than the old, overwrite and fill diff with spaces
//3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new //3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new
//4. The new content is larger than old + trailing spaces, delete old and append //4. The new content is larger than old + trailing spaces, delete old and append
uint32_t contentLen = 0; uint32_t contentLen = 0;
if (!content->isNull()) contentLen = measureJson(*content); if (!content->isNull()) contentLen = measureJson(*content);

View File

@ -44,44 +44,44 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
const uint16_t PAGE_update_length = 615; const uint16_t PAGE_update_length = 615;
const uint8_t PAGE_update[] PROGMEM = { const uint8_t PAGE_update[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x75, 0x53, 0x5d, 0x6f, 0xd4, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x75, 0x53, 0x5d, 0x6f, 0xd4, 0x30,
0x10, 0x7c, 0xcf, 0xaf, 0x70, 0xfd, 0x74, 0x27, 0x71, 0x4e, 0x41, 0xbc, 0x50, 0x92, 0x14, 0x8e, 0x10, 0x7c, 0xcf, 0xaf, 0x70, 0xfd, 0x74, 0x27, 0x71, 0x4e, 0x8b, 0x78, 0xa1, 0x24, 0x29, 0x1c,
0x56, 0xa8, 0x12, 0x52, 0x4f, 0x6a, 0x0b, 0xe2, 0x09, 0x39, 0xf6, 0x26, 0x31, 0xe7, 0xd8, 0xa9, 0xad, 0x50, 0x25, 0xa4, 0x56, 0x6a, 0x0b, 0xe2, 0x09, 0x39, 0xf6, 0xe6, 0x62, 0xce, 0xb1, 0x53,
0xbd, 0xb9, 0xd3, 0x09, 0xf5, 0xbf, 0xb3, 0x71, 0xee, 0x0a, 0xe2, 0xe3, 0x25, 0x8a, 0xb3, 0xb3, 0x7b, 0x73, 0xa7, 0xa8, 0xea, 0x7f, 0x67, 0xe3, 0xdc, 0x15, 0xc4, 0xc7, 0x4b, 0x14, 0x67, 0x67,
0xe3, 0xdd, 0x99, 0x49, 0x71, 0x76, 0x75, 0xfb, 0xe1, 0xfe, 0xeb, 0xe6, 0x9a, 0x75, 0xd8, 0xdb, 0xc7, 0xbb, 0x33, 0x93, 0xe2, 0xe4, 0xf2, 0xe6, 0xe3, 0xfd, 0xb7, 0xdb, 0x2b, 0xd6, 0x62, 0x67,
0xaa, 0x38, 0x3e, 0x41, 0xea, 0xaa, 0xe8, 0x01, 0x25, 0x53, 0xde, 0x21, 0x38, 0x2c, 0xf9, 0xde, 0xab, 0xe2, 0xf0, 0x04, 0xa9, 0xab, 0xa2, 0x03, 0x94, 0x4c, 0x79, 0x87, 0xe0, 0xb0, 0xe4, 0x7b,
0x68, 0xec, 0x4a, 0x0d, 0x3b, 0xa3, 0x60, 0x95, 0x0e, 0x9c, 0x39, 0xd9, 0x43, 0xc9, 0x77, 0x06, 0xa3, 0xb1, 0x2d, 0x35, 0xec, 0x8c, 0x82, 0x55, 0x3a, 0x70, 0xe6, 0x64, 0x07, 0x25, 0xdf, 0x19,
0xf6, 0x83, 0x0f, 0xc8, 0xab, 0xac, 0x40, 0x83, 0x16, 0xaa, 0x2f, 0x9f, 0xae, 0xaf, 0xd8, 0xc3, 0xd8, 0xf7, 0x3e, 0x20, 0xaf, 0xb2, 0x02, 0x0d, 0x5a, 0xa8, 0xbe, 0x7e, 0xbe, 0xba, 0x64, 0x0f,
0xa0, 0x25, 0x42, 0x91, 0xcf, 0x9f, 0x8a, 0xa8, 0x82, 0x19, 0xb0, 0xca, 0x9a, 0xd1, 0x29, 0x34, 0xbd, 0x96, 0x08, 0x45, 0x3e, 0x7f, 0x2a, 0xa2, 0x0a, 0xa6, 0xc7, 0x2a, 0x6b, 0x06, 0xa7, 0xd0,
0xde, 0xb1, 0xf5, 0x62, 0xf9, 0x63, 0x6f, 0x9c, 0xf6, 0x7b, 0xd1, 0x99, 0x88, 0x3e, 0x1c, 0x44, 0x78, 0xc7, 0xd6, 0x8b, 0xe5, 0xd3, 0xde, 0x38, 0xed, 0xf7, 0xa2, 0x35, 0x11, 0x7d, 0x18, 0x45,
0x2d, 0xd5, 0x76, 0xb1, 0x7c, 0x7a, 0x86, 0x3c, 0x10, 0x44, 0x7b, 0x35, 0xf6, 0x34, 0x81, 0x68, 0x2d, 0xd5, 0x76, 0xb1, 0x7c, 0x7e, 0x81, 0x3c, 0x10, 0x44, 0x7b, 0x35, 0x74, 0x34, 0x81, 0xd8,
0x01, 0xaf, 0x2d, 0x4c, 0xaf, 0xeb, 0xc3, 0x8d, 0x5e, 0xf0, 0xb1, 0xe1, 0x4b, 0x11, 0xf1, 0x60, 0x00, 0x5e, 0x59, 0x98, 0x5e, 0xd7, 0xe3, 0xb5, 0x5e, 0xf0, 0xa1, 0xe1, 0x4b, 0x11, 0x71, 0xb4,
0x41, 0x68, 0x13, 0x07, 0x2b, 0x0f, 0x25, 0x77, 0xde, 0x01, 0x7f, 0xf1, 0xdf, 0x96, 0x3e, 0xb6, 0x20, 0xb4, 0x89, 0xbd, 0x95, 0x63, 0xc9, 0x9d, 0x77, 0xc0, 0x5f, 0xfd, 0xb7, 0xa5, 0x8b, 0x9b,
0x7f, 0xf7, 0xd4, 0xd6, 0xab, 0x2d, 0x7f, 0xca, 0x8a, 0xfc, 0x38, 0xe2, 0x71, 0x54, 0x16, 0x83, 0xbf, 0x7b, 0x6a, 0xeb, 0xd5, 0x96, 0x3f, 0x67, 0x45, 0x7e, 0x18, 0xf1, 0x30, 0x2a, 0x8b, 0x41,
0x2a, 0x79, 0x1e, 0x01, 0xd1, 0xb8, 0x36, 0xe6, 0x51, 0x7c, 0x8f, 0x97, 0x43, 0xf9, 0x86, 0x57, 0x95, 0x3c, 0x8f, 0x80, 0x68, 0xdc, 0x26, 0xe6, 0x51, 0xfc, 0x88, 0x17, 0x7d, 0xf9, 0x96, 0x57,
0xbf, 0x21, 0x27, 0xaa, 0x2a, 0x7b, 0x67, 0xfa, 0x49, 0x00, 0x36, 0x06, 0xbb, 0xe0, 0x33, 0xbd, 0xbf, 0x21, 0x27, 0xaa, 0x2a, 0x7b, 0x6f, 0xba, 0x49, 0x00, 0x36, 0x04, 0xbb, 0xe0, 0x33, 0xbd,
0x8a, 0x91, 0x2f, 0xdf, 0x12, 0x32, 0x21, 0x8a, 0x7c, 0x96, 0xb4, 0xf6, 0xfa, 0xc0, 0xbc, 0xb3, 0x8a, 0x91, 0x2f, 0xdf, 0x11, 0x32, 0x21, 0x8a, 0x7c, 0x96, 0xb4, 0xf6, 0x7a, 0x64, 0xde, 0x59,
0x5e, 0xea, 0x92, 0x7f, 0x04, 0xfc, 0xbc, 0x58, 0x12, 0x5d, 0xf7, 0xaa, 0xca, 0x92, 0x64, 0x77, 0x2f, 0x75, 0xc9, 0x3f, 0x01, 0x7e, 0x59, 0x2c, 0x89, 0xae, 0x7d, 0x5d, 0x65, 0x49, 0xb2, 0x3b,
0xbe, 0xc1, 0xbd, 0x0c, 0xf0, 0xac, 0x1d, 0x55, 0x8a, 0xc6, 0x87, 0x9e, 0x91, 0x17, 0x9d, 0xa7, 0xdf, 0xe0, 0x5e, 0x06, 0x78, 0xd1, 0x8e, 0x2a, 0x45, 0xe3, 0x43, 0xc7, 0xc8, 0x8b, 0xd6, 0x53,
0x9e, 0xcd, 0xed, 0xdd, 0x3d, 0x67, 0x32, 0xc9, 0x43, 0xc3, 0x8d, 0x09, 0xc7, 0x99, 0xa1, 0x12, 0xcf, 0xed, 0xcd, 0xdd, 0x3d, 0x67, 0x32, 0xc9, 0x43, 0xc3, 0x0d, 0x09, 0xc7, 0x99, 0xa1, 0x12,
0xe9, 0xc1, 0x32, 0x20, 0xe5, 0x0e, 0x03, 0x99, 0xd2, 0x8f, 0x16, 0xcd, 0x20, 0x03, 0xe6, 0x53, 0xe9, 0xc1, 0x32, 0x20, 0xe5, 0xc6, 0x9e, 0x4c, 0xe9, 0x06, 0x8b, 0xa6, 0x97, 0x01, 0xf3, 0xa9,
0xff, 0x8a, 0x60, 0x92, 0xd3, 0xcd, 0x71, 0xac, 0x7b, 0x43, 0x6e, 0x3e, 0x4c, 0x17, 0xdf, 0xb8, 0x7f, 0x45, 0x30, 0xc9, 0xe9, 0xe6, 0x38, 0xd4, 0x9d, 0x21, 0x37, 0x1f, 0xa6, 0x8b, 0xaf, 0x5d,
0x88, 0xd2, 0x5a, 0xd0, 0x6c, 0x07, 0x21, 0x12, 0xe3, 0x05, 0x2b, 0xe2, 0x20, 0x1d, 0xcb, 0x94, 0x44, 0x69, 0x2d, 0x68, 0xb6, 0x83, 0x10, 0x89, 0xf1, 0x9c, 0x15, 0xb1, 0x97, 0x8e, 0x65, 0xca,
0x95, 0x31, 0x96, 0x3c, 0x9a, 0x81, 0x57, 0xe7, 0xe2, 0xe5, 0x6b, 0x71, 0xbe, 0xaa, 0xcf, 0x69, 0xca, 0x18, 0x4b, 0x1e, 0x4d, 0xcf, 0xab, 0x53, 0x71, 0xf6, 0x46, 0x9c, 0xae, 0xea, 0x33, 0x5a,
0x19, 0x2a, 0xd2, 0x12, 0xa1, 0xba, 0xf2, 0xfb, 0xb4, 0x04, 0xc3, 0x0e, 0x98, 0xa5, 0x11, 0x22, 0x86, 0x8a, 0xb4, 0x44, 0xa8, 0x2e, 0xfd, 0x3e, 0x2d, 0xc1, 0xb0, 0x05, 0x66, 0x69, 0x84, 0x88,
0xb2, 0xda, 0x38, 0x19, 0x0e, 0x44, 0x21, 0x59, 0xd6, 0x05, 0x68, 0x4a, 0xde, 0x21, 0x0e, 0xf1, 0xac, 0x36, 0x4e, 0x86, 0x91, 0x28, 0x24, 0xcb, 0xda, 0x00, 0x4d, 0xc9, 0x5b, 0xc4, 0x3e, 0x9e,
0x22, 0xcf, 0x5b, 0x83, 0xdd, 0x58, 0x0b, 0xe5, 0xfb, 0xfc, 0xbd, 0x09, 0xca, 0x7b, 0xbf, 0x35, 0xe7, 0xf9, 0xc6, 0x60, 0x3b, 0xd4, 0x42, 0xf9, 0x2e, 0xff, 0x60, 0x82, 0xf2, 0xde, 0x6f, 0x0d,
0x90, 0x4f, 0x1b, 0xe7, 0x01, 0x2c, 0xc8, 0x08, 0x91, 0x33, 0x94, 0x81, 0xec, 0x2a, 0xf9, 0xb7, 0xe4, 0xd3, 0xc6, 0x79, 0x00, 0x0b, 0x32, 0x42, 0xe4, 0x0c, 0x65, 0x20, 0xbb, 0x4a, 0xfe, 0xbd,
0xda, 0x4a, 0xb7, 0x25, 0x55, 0x4c, 0xdf, 0xb2, 0x2c, 0x79, 0x70, 0xe2, 0xa1, 0x2f, 0x22, 0x76, 0xb6, 0xd2, 0x6d, 0x49, 0x15, 0xd3, 0x6d, 0x58, 0x96, 0x3c, 0x38, 0xf2, 0xd0, 0x17, 0x11, 0x5b,
0x06, 0xac, 0x8e, 0xc2, 0xf8, 0x23, 0xed, 0x89, 0xe2, 0x4f, 0x6a, 0x11, 0x77, 0xed, 0x65, 0x52, 0x03, 0x56, 0x47, 0x61, 0xfc, 0x81, 0xf6, 0x48, 0xf1, 0x27, 0xb5, 0x88, 0xbb, 0xcd, 0x45, 0x52,
0xbf, 0x6c, 0x68, 0xc2, 0x55, 0x7c, 0x1c, 0x49, 0xd9, 0x29, 0xa3, 0xb9, 0x4c, 0x3b, 0x14, 0xc6, 0xbf, 0x6c, 0x68, 0xc2, 0x55, 0x7c, 0x1c, 0x48, 0xd9, 0x29, 0xa3, 0xb9, 0x4c, 0x3b, 0x14, 0xc6,
0x0d, 0x23, 0xb2, 0x59, 0xae, 0xc6, 0x58, 0x38, 0xe5, 0xf9, 0x24, 0x6a, 0x80, 0xc7, 0xd1, 0x04, 0xf5, 0x03, 0xb2, 0x59, 0xae, 0xc6, 0x58, 0x38, 0xe6, 0xf9, 0x28, 0x6a, 0x80, 0xc7, 0xc1, 0x04,
0xd0, 0x33, 0xba, 0x1e, 0x11, 0x29, 0x92, 0x33, 0x7c, 0x96, 0x91, 0xc8, 0x66, 0xa3, 0xce, 0x8a, 0xd0, 0x33, 0xba, 0x1e, 0x10, 0x29, 0x92, 0x33, 0x7c, 0x96, 0x91, 0xc8, 0x66, 0xa3, 0x4e, 0x8a,
0x7c, 0x2e, 0xff, 0x03, 0x3a, 0x1f, 0x26, 0xed, 0x95, 0x35, 0x6a, 0x5b, 0xf2, 0xf5, 0x24, 0xfd, 0x7c, 0x2e, 0xff, 0x03, 0x3a, 0x1f, 0x26, 0xed, 0x95, 0x35, 0x6a, 0x5b, 0xf2, 0xf5, 0x24, 0xfd,
0x9a, 0x92, 0xfe, 0xab, 0x29, 0x79, 0x54, 0x15, 0xda, 0xec, 0xb2, 0x64, 0xe5, 0x94, 0x53, 0xa2, 0x9a, 0x92, 0xfe, 0xab, 0x29, 0x79, 0x54, 0x15, 0xda, 0xec, 0xb2, 0x64, 0xe5, 0x94, 0x53, 0xa2,
0xa9, 0x12, 0x3b, 0x85, 0x4f, 0x08, 0x41, 0xe0, 0x44, 0xbe, 0x49, 0xcb, 0x32, 0xed, 0x99, 0xf3, 0xa9, 0x12, 0x3b, 0x85, 0x4f, 0x08, 0x41, 0xe0, 0x44, 0x7e, 0x9b, 0x96, 0x65, 0xda, 0x33, 0xe7,
0xc8, 0x94, 0xf5, 0x74, 0xf0, 0x81, 0x66, 0x6d, 0x02, 0xc4, 0x2e, 0xf9, 0x31, 0xc8, 0x16, 0xd8, 0x91, 0x29, 0xeb, 0xe9, 0xe0, 0x03, 0xcd, 0xda, 0x04, 0x88, 0x6d, 0xf2, 0xa3, 0x97, 0x1b, 0x60,
0xc5, 0xb2, 0xc8, 0x89, 0x6f, 0x5a, 0x77, 0x0a, 0xdd, 0x94, 0xc0, 0xe9, 0xd7, 0xfe, 0x09, 0x43, 0xe7, 0xcb, 0x22, 0x27, 0xbe, 0x69, 0xdd, 0x29, 0x74, 0x53, 0x02, 0xa7, 0x5f, 0xfb, 0x27, 0xff,
0x44, 0x4f, 0x48, 0xf0, 0x03, 0x00, 0x00 0x66, 0x0a, 0x46, 0xf0, 0x03, 0x00, 0x00
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@ void handleHue()
hueNewKey = false; hueNewKey = false;
} }
} }
if (!WLED_CONNECTED || hueClient == nullptr || millis() - hueLastRequestSent < huePollIntervalMs) return; if (!WLED_CONNECTED || hueClient == nullptr || millis() - hueLastRequestSent < huePollIntervalMs) return;
hueLastRequestSent = millis(); hueLastRequestSent = millis();
@ -100,7 +100,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
{ {
hueError = HUE_ERROR_JSON_PARSING; return; hueError = HUE_ERROR_JSON_PARSING; return;
} }
int hueErrorCode = root[0][F("error")]["type"]; int hueErrorCode = root[0][F("error")]["type"];
if (hueErrorCode)//hue bridge returned error if (hueErrorCode)//hue bridge returned error
{ {
@ -113,7 +113,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
} }
return; return;
} }
if (hueAuthRequired) if (hueAuthRequired)
{ {
const char* apikey = root[0][F("success")][F("username")]; const char* apikey = root[0][F("success")][F("username")];
@ -131,7 +131,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
str = strstr(str,"state"); str = strstr(str,"state");
if (str == nullptr) return; if (str == nullptr) return;
str = strstr(str,"{"); str = strstr(str,"{");
auto error = deserializeJson(root, str); auto error = deserializeJson(root, str);
if (error) if (error)
{ {
@ -176,7 +176,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
} }
hueError = HUE_ERROR_ACTIVE; hueError = HUE_ERROR_ACTIVE;
//apply vals //apply vals
if (hueBri != hueBriLast) if (hueBri != hueBriLast)
{ {

View File

@ -99,7 +99,7 @@ void handleImprovPacket() {
uint8_t improvState = 0x02; //authorized uint8_t improvState = 0x02; //authorized
if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning
if (Network.isConnected()) improvState = 0x04; //provisioned if (Network.isConnected()) improvState = 0x04; //provisioned
sendImprovStateResponse(improvState, false); sendImprovStateResponse(improvState, false);
if (improvState == 0x04) sendImprovRPCResponse(ImprovRPCType::Request_State); if (improvState == 0x04) sendImprovRPCResponse(ImprovRPCType::Request_State);
break; break;
} }
@ -166,7 +166,7 @@ void sendImprovRPCResponse(byte commandId) {
out[11] = len; out[11] = len;
out[10] = 1 + len; out[10] = 1 + len;
out[8] = 3 + len; //RPC command type + data len + url len + url out[8] = 3 + len; //RPC command type + data len + url len + url
packetLen = 13 + len; packetLen = 13 + len;
} }
uint8_t checksum = 0; uint8_t checksum = 0;
@ -189,7 +189,7 @@ void sendImprovInfoResponse() {
out[11] = 4; //Firmware len ("WLED") out[11] = 4; //Firmware len ("WLED")
out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D'; out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D';
uint8_t lengthSum = 17; uint8_t lengthSum = 17;
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.14.0-b0/%i"),VERSION); uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.14.0-b1/%i"),VERSION);
out[16] = vlen; lengthSum += vlen; out[16] = vlen; lengthSum += vlen;
uint8_t hlen = 7; uint8_t hlen = 7;
#ifdef ESP8266 #ifdef ESP8266

View File

@ -71,11 +71,11 @@ void decBrightness()
} }
// apply preset or fallback to a effect and palette if it doesn't exist // apply preset or fallback to a effect and palette if it doesn't exist
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
{ {
applyPreset(presetID, CALL_MODE_BUTTON_PRESET); applyPreset(presetID, CALL_MODE_BUTTON_PRESET);
//these two will be overwritten if preset exists in handlePresets() //these two will be overwritten if preset exists in handlePresets()
effectCurrent = effectID; effectCurrent = effectID;
effectPalette = paletteID; effectPalette = paletteID;
} }
@ -282,7 +282,7 @@ void decodeIR(uint32_t code)
if (code > 0xFFFFFF) return; //invalid code if (code > 0xFFFFFF) return; //invalid code
switch (irEnabled) { switch (irEnabled) {
case 1: case 1:
if (code > 0xF80000) decodeIR24OLD(code); // white 24-key remote (old) - it sends 0xFF0000 values if (code > 0xF80000) decodeIR24OLD(code); // white 24-key remote (old) - it sends 0xFF0000 values
else decodeIR24(code); // 24-key remote - 0xF70000 to 0xF80000 else decodeIR24(code); // 24-key remote - 0xF70000 to 0xF80000
break; break;
@ -419,7 +419,7 @@ void decodeIR24CT(uint32_t code)
case IR24_CT_CTPLUS : changeColor(COLOR_COLDWHITE, strip.getSegment(strip.getMainSegmentId()).cct+1); changeEffect(FX_MODE_STATIC); break; case IR24_CT_CTPLUS : changeColor(COLOR_COLDWHITE, strip.getSegment(strip.getMainSegmentId()).cct+1); changeEffect(FX_MODE_STATIC); break;
case IR24_CT_CTMINUS : changeColor(COLOR_WARMWHITE, strip.getSegment(strip.getMainSegmentId()).cct-1); changeEffect(FX_MODE_STATIC); break; case IR24_CT_CTMINUS : changeColor(COLOR_WARMWHITE, strip.getSegment(strip.getMainSegmentId()).cct-1); changeEffect(FX_MODE_STATIC); break;
case IR24_CT_MEMORY : changeColor(COLOR_NEUTRALWHITE, 127); changeEffect(FX_MODE_STATIC); break; case IR24_CT_MEMORY : changeColor(COLOR_NEUTRALWHITE, 127); changeEffect(FX_MODE_STATIC); break;
default: return; default: return;
} }
lastValidCode = code; lastValidCode = code;
} }
@ -611,13 +611,13 @@ void decodeIR9(uint32_t code)
/* /*
This allows users to customize IR actions without the need to edit C code and compile. This allows users to customize IR actions without the need to edit C code and compile.
From the https://github.com/Aircoookie/WLED/wiki/Infrared-Control page, download the starter From the https://github.com/Aircoookie/WLED/wiki/Infrared-Control page, download the starter
ir.json file that corresponds to the number of buttons on your remote. ir.json file that corresponds to the number of buttons on your remote.
Many of the remotes with the same number of buttons emit the same codes, but will have Many of the remotes with the same number of buttons emit the same codes, but will have
different labels or colors. Once you edit the ir.json file, upload it to your controller different labels or colors. Once you edit the ir.json file, upload it to your controller
using the /edit page. using the /edit page.
Each key should be the hex encoded IR code. The "cmd" property should be the HTTP API Each key should be the hex encoded IR code. The "cmd" property should be the HTTP API
or JSON API command to execute on button press. If the command contains a relative change (SI=~16), or JSON API command to execute on button press. If the command contains a relative change (SI=~16),
it will register as a repeatable command. If the command doesn't contain a "~" but is repeatable, add "rpt" property it will register as a repeatable command. If the command doesn't contain a "~" but is repeatable, add "rpt" property
set to true. Other properties are ignored but having labels and positions can assist with editing set to true. Other properties are ignored but having labels and positions can assist with editing
@ -632,7 +632,7 @@ Sample:
"label": "Preset 1, fallback to Saw - Party if not found"}, "label": "Preset 1, fallback to Saw - Party if not found"},
} }
*/ */
void decodeIRJson(uint32_t code) void decodeIRJson(uint32_t code)
{ {
char objKey[10]; char objKey[10];
String cmdStr; String cmdStr;
@ -720,10 +720,10 @@ void handleIR()
if (irEnabled > 0) if (irEnabled > 0)
{ {
if (irrecv == NULL) if (irrecv == NULL)
{ {
initIR(); return; initIR(); return;
} }
if (irrecv->decode(&results)) if (irrecv->decode(&results))
{ {
if (results.value != 0) // only print results if anything is received ( != 0 ) if (results.value != 0) // only print results if anything is received ( != 0 )

View File

@ -45,9 +45,9 @@
#define IR24_PINK 0xF76897 #define IR24_PINK 0xF76897
#define IR24_WHITE 0xF7E01F #define IR24_WHITE 0xF7E01F
#define IR24_FLASH 0xF7D02F #define IR24_FLASH 0xF7D02F
#define IR24_STROBE 0xF7F00F #define IR24_STROBE 0xF7F00F
#define IR24_FADE 0xF7C837 #define IR24_FADE 0xF7C837
#define IR24_SMOOTH 0xF7E817 #define IR24_SMOOTH 0xF7E817
// 24-key defs for white remote control with CW / WW / CT+ and CT- keys (from ALDI LED pillar lamp) // 24-key defs for white remote control with CW / WW / CT+ and CT- keys (from ALDI LED pillar lamp)
#define IR24_CT_BRIGHTER 0xF700FF // BRI + #define IR24_CT_BRIGHTER 0xF700FF // BRI +
@ -75,7 +75,7 @@
#define IR24_CT_CTMINUS 0xF7C837 // CT- #define IR24_CT_CTMINUS 0xF7C837 // CT-
#define IR24_CT_MEMORY 0xF7E817 // MEMORY #define IR24_CT_MEMORY 0xF7E817 // MEMORY
// 24-key defs for old remote control // 24-key defs for old remote control
#define IR24_OLD_BRIGHTER 0xFF906F // Brightness Up #define IR24_OLD_BRIGHTER 0xFF906F // Brightness Up
#define IR24_OLD_DARKER 0xFFB847 // Brightness Down #define IR24_OLD_DARKER 0xFFB847 // Brightness Down
#define IR24_OLD_OFF 0xFFF807 // Power OFF #define IR24_OLD_OFF 0xFFF807 // Power OFF
@ -101,30 +101,30 @@
#define IR24_OLD_FADE 0xFF58A7 // FADE Mode #define IR24_OLD_FADE 0xFF58A7 // FADE Mode
#define IR24_OLD_SMOOTH 0xFF30CF // SMOOTH Mode #define IR24_OLD_SMOOTH 0xFF30CF // SMOOTH Mode
// 40-key defs for blue remote control // 40-key defs for blue remote control
#define IR40_BPLUS 0xFF3AC5 // #define IR40_BPLUS 0xFF3AC5 //
#define IR40_BMINUS 0xFFBA45 // #define IR40_BMINUS 0xFFBA45 //
#define IR40_OFF 0xFF827D // #define IR40_OFF 0xFF827D //
#define IR40_ON 0xFF02FD // #define IR40_ON 0xFF02FD //
#define IR40_RED 0xFF1AE5 // #define IR40_RED 0xFF1AE5 //
#define IR40_GREEN 0xFF9A65 // #define IR40_GREEN 0xFF9A65 //
#define IR40_BLUE 0xFFA25D // #define IR40_BLUE 0xFFA25D //
#define IR40_WHITE 0xFF22DD // natural white #define IR40_WHITE 0xFF22DD // natural white
#define IR40_REDDISH 0xFF2AD5 // #define IR40_REDDISH 0xFF2AD5 //
#define IR40_GREENISH 0xFFAA55 // #define IR40_GREENISH 0xFFAA55 //
#define IR40_DEEPBLUE 0xFF926D // #define IR40_DEEPBLUE 0xFF926D //
#define IR40_WARMWHITE2 0xFF12ED // warmest white #define IR40_WARMWHITE2 0xFF12ED // warmest white
#define IR40_ORANGE 0xFF0AF5 // #define IR40_ORANGE 0xFF0AF5 //
#define IR40_TURQUOISE 0xFF8A75 // #define IR40_TURQUOISE 0xFF8A75 //
#define IR40_PURPLE 0xFFB24D // #define IR40_PURPLE 0xFFB24D //
#define IR40_WARMWHITE 0xFF32CD // warm white #define IR40_WARMWHITE 0xFF32CD // warm white
#define IR40_YELLOWISH 0xFF38C7 // #define IR40_YELLOWISH 0xFF38C7 //
#define IR40_CYAN 0xFFB847 // #define IR40_CYAN 0xFFB847 //
#define IR40_MAGENTA 0xFF7887 // #define IR40_MAGENTA 0xFF7887 //
#define IR40_COLDWHITE 0xFFF807 // cold white #define IR40_COLDWHITE 0xFFF807 // cold white
#define IR40_YELLOW 0xFF18E7 // #define IR40_YELLOW 0xFF18E7 //
#define IR40_AQUA 0xFF9867 // #define IR40_AQUA 0xFF9867 //
#define IR40_PINK 0xFF58A7 // #define IR40_PINK 0xFF58A7 //
#define IR40_COLDWHITE2 0xFFD827 // coldest white #define IR40_COLDWHITE2 0xFFD827 // coldest white
#define IR40_WPLUS 0xFF28D7 // white chanel bright plus #define IR40_WPLUS 0xFF28D7 // white chanel bright plus
#define IR40_WMINUS 0xFFA857 // white chanel bright minus #define IR40_WMINUS 0xFFA857 // white chanel bright minus
@ -143,51 +143,51 @@
#define IR40_AUTO 0xFF50AF // AUTO #define IR40_AUTO 0xFF50AF // AUTO
#define IR40_SLOW 0xFFD02F // SLOW #define IR40_SLOW 0xFFD02F // SLOW
// 44-key defs, to be done later // 44-key defs
#define IR44_BPLUS 0xFF3AC5 // #define IR44_BPLUS 0xFF3AC5 //
#define IR44_BMINUS 0xFFBA45 // #define IR44_BMINUS 0xFFBA45 //
#define IR44_OFF 0xFF827D // #define IR44_OFF 0xFF827D //
#define IR44_ON 0xFF02FD // #define IR44_ON 0xFF02FD //
#define IR44_RED 0xFF1AE5 // #define IR44_RED 0xFF1AE5 //
#define IR44_GREEN 0xFF9A65 // #define IR44_GREEN 0xFF9A65 //
#define IR44_BLUE 0xFFA25D // #define IR44_BLUE 0xFFA25D //
#define IR44_WHITE 0xFF22DD // natural white #define IR44_WHITE 0xFF22DD // natural white
#define IR44_REDDISH 0xFF2AD5 // #define IR44_REDDISH 0xFF2AD5 //
#define IR44_GREENISH 0xFFAA55 // #define IR44_GREENISH 0xFFAA55 //
#define IR44_DEEPBLUE 0xFF926D // #define IR44_DEEPBLUE 0xFF926D //
#define IR44_WARMWHITE2 0xFF12ED // warmest white #define IR44_WARMWHITE2 0xFF12ED // warmest white
#define IR44_ORANGE 0xFF0AF5 // #define IR44_ORANGE 0xFF0AF5 //
#define IR44_TURQUOISE 0xFF8A75 // #define IR44_TURQUOISE 0xFF8A75 //
#define IR44_PURPLE 0xFFB24D // #define IR44_PURPLE 0xFFB24D //
#define IR44_WARMWHITE 0xFF32CD // warm white #define IR44_WARMWHITE 0xFF32CD // warm white
#define IR44_YELLOWISH 0xFF38C7 // #define IR44_YELLOWISH 0xFF38C7 //
#define IR44_CYAN 0xFFB847 // #define IR44_CYAN 0xFFB847 //
#define IR44_MAGENTA 0xFF7887 // #define IR44_MAGENTA 0xFF7887 //
#define IR44_COLDWHITE 0xFFF807 // cold white #define IR44_COLDWHITE 0xFFF807 // cold white
#define IR44_YELLOW 0xFF18E7 // #define IR44_YELLOW 0xFF18E7 //
#define IR44_AQUA 0xFF9867 // #define IR44_AQUA 0xFF9867 //
#define IR44_PINK 0xFF58A7 // #define IR44_PINK 0xFF58A7 //
#define IR44_COLDWHITE2 0xFFD827 // coldest white #define IR44_COLDWHITE2 0xFFD827 // coldest white
#define IR44_REDPLUS 0xFF28D7 // #define IR44_REDPLUS 0xFF28D7 //
#define IR44_GREENPLUS 0xFFA857 // #define IR44_GREENPLUS 0xFFA857 //
#define IR44_BLUEPLUS 0xFF6897 // #define IR44_BLUEPLUS 0xFF6897 //
#define IR44_QUICK 0xFFE817 // #define IR44_QUICK 0xFFE817 //
#define IR44_REDMINUS 0xFF08F7 // #define IR44_REDMINUS 0xFF08F7 //
#define IR44_GREENMINUS 0xFF8877 // #define IR44_GREENMINUS 0xFF8877 //
#define IR44_BLUEMINUS 0xFF48B7 // #define IR44_BLUEMINUS 0xFF48B7 //
#define IR44_SLOW 0xFFC837 // #define IR44_SLOW 0xFFC837 //
#define IR44_DIY1 0xFF30CF // #define IR44_DIY1 0xFF30CF //
#define IR44_DIY2 0xFFB04F // #define IR44_DIY2 0xFFB04F //
#define IR44_DIY3 0xFF708F // #define IR44_DIY3 0xFF708F //
#define IR44_AUTO 0xFFF00F // #define IR44_AUTO 0xFFF00F //
#define IR44_DIY4 0xFF10EF // #define IR44_DIY4 0xFF10EF //
#define IR44_DIY5 0xFF906F // #define IR44_DIY5 0xFF906F //
#define IR44_DIY6 0xFF50AF // #define IR44_DIY6 0xFF50AF //
#define IR44_FLASH 0xFFD02F // #define IR44_FLASH 0xFFD02F //
#define IR44_JUMP3 0xFF20DF // #define IR44_JUMP3 0xFF20DF //
#define IR44_JUMP7 0xFFA05F // #define IR44_JUMP7 0xFFA05F //
#define IR44_FADE3 0xFF609F // #define IR44_FADE3 0xFF609F //
#define IR44_FADE7 0xFFE01F // #define IR44_FADE7 0xFFE01F //
//Infrared codes for 21-key remote https://images-na.ssl-images-amazon.com/images/I/51NMA0XucnL.jpg //Infrared codes for 21-key remote https://images-na.ssl-images-amazon.com/images/I/51NMA0XucnL.jpg
#define IR21_BRIGHTER 0xFFE01F #define IR21_BRIGHTER 0xFFE01F

View File

@ -105,7 +105,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs; of = offsetAbs;
} }
if (stop > start && of > len -1) of = len -1; if (stop > start && of > len -1) of = len -1;
strip.setSegment(id, start, stop, grp, spc, of, startY, stopY); seg.set(start, stop, grp, spc, of, startY, stopY);
byte segbri = seg.opacity; byte segbri = seg.opacity;
if (getVal(elem["bri"], &segbri)) { if (getVal(elem["bri"], &segbri)) {
@ -170,13 +170,20 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
#endif #endif
#ifndef WLED_DISABLE_2D
bool reverse = seg.reverse;
bool mirror = seg.mirror;
#endif
seg.selected = elem["sel"] | seg.selected; seg.selected = elem["sel"] | seg.selected;
seg.reverse = elem["rev"] | seg.reverse; seg.reverse = elem["rev"] | seg.reverse;
seg.mirror = elem["mi"] | seg.mirror; seg.mirror = elem["mi"] | seg.mirror;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
seg.reverse_y = elem["rY"] | seg.reverse_y; bool reverse_y = seg.reverse_y;
seg.mirror_y = elem["mY"] | seg.mirror_y; bool mirror_y = seg.mirror_y;
seg.transpose = elem[F("tp")] | seg.transpose; seg.reverse_y = elem["rY"] | seg.reverse_y;
seg.mirror_y = elem["mY"] | seg.mirror_y;
seg.transpose = elem[F("tp")] | seg.transpose;
if (seg.is2D() && seg.map1D2D == M12_pArc && (reverse != seg.reverse || reverse_y != seg.reverse_y || mirror != seg.mirror || mirror_y != seg.mirror_y)) seg.fill(BLACK); // clear entire segment (in case of Arc 1D to 2D expansion)
#endif #endif
byte fx = seg.mode; byte fx = seg.mode;
@ -201,7 +208,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.check1 = elem["o1"] | seg.check1; seg.check1 = elem["o1"] | seg.check1;
seg.check2 = elem["o2"] | seg.check2; seg.check2 = elem["o2"] | seg.check2;
seg.check3 = elem["o3"] | seg.check3; seg.check3 = elem["o3"] | seg.check3;
JsonArray iarr = elem[F("i")]; //set individual LEDs JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) { if (!iarr.isNull()) {
// set brightness immediately and disable transition // set brightness immediately and disable transition
@ -221,11 +228,11 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
for (size_t i = 0; i < iarr.size(); i++) { for (size_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) { if(iarr[i].is<JsonInteger>()) {
if (!set) { if (!set) {
start = iarr[i]; start = abs(iarr[i].as<int>());
set = 1; set++;
} else { } else {
stop = iarr[i]; stop = abs(iarr[i].as<int>());
set = 2; set++;
} }
} else { //color } else { //color
uint8_t rgbw[] = {0,0,0,0}; uint8_t rgbw[] = {0,0,0,0};
@ -241,16 +248,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
} }
if (set < 2) stop = start + 1; if (set < 2 || stop <= start) stop = start + 1;
uint32_t c = gamma32(RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3])); uint32_t c = gamma32(RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]));
for (int i = start; i < stop; i++) { while (start < stop) seg.setPixelColor(start++, c);
seg.setPixelColor(i, c);
}
if (!set) start++;
set = 0; set = 0;
} }
} }
strip.trigger(); strip.trigger(); // force segment update
} }
// send UDP/WS if segment options changed (except selection; will also deselect current preset) // send UDP/WS if segment options changed (except selection; will also deselect current preset)
if (seg.differs(prev) & 0x7F) stateChanged = true; if (seg.differs(prev) & 0x7F) stateChanged = true;
@ -477,15 +481,17 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
root["sel"] = seg.isSelected(); root["sel"] = seg.isSelected();
root["rev"] = seg.reverse; root["rev"] = seg.reverse;
root["mi"] = seg.mirror; root["mi"] = seg.mirror;
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) { if (strip.isMatrix) {
root["rY"] = seg.reverse_y; root["rY"] = seg.reverse_y;
root["mY"] = seg.mirror_y; root["mY"] = seg.mirror_y;
root[F("tp")] = seg.transpose; root[F("tp")] = seg.transpose;
} }
root["o1"] = seg.check1; #endif
root["o2"] = seg.check2; root["o1"] = seg.check1;
root["o3"] = seg.check3; root["o2"] = seg.check2;
root["si"] = seg.soundSim; root["o3"] = seg.check3;
root["si"] = seg.soundSim;
root["m12"] = seg.map1D2D; root["m12"] = seg.map1D2D;
} }
@ -650,7 +656,7 @@ void serializeInfo(JsonObject root)
fs_info[F("pmt")] = presetsModifiedTime; fs_info[F("pmt")] = presetsModifiedTime;
root[F("ndc")] = nodeListEnabled ? (int)Nodes.size() : -1; root[F("ndc")] = nodeListEnabled ? (int)Nodes.size() : -1;
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
wifi_info[F("txPower")] = (int) WiFi.getTxPower(); wifi_info[F("txPower")] = (int) WiFi.getTxPower();
@ -800,7 +806,7 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
JsonArray curPalette = palettes.createNestedArray(String(i>=palettesCount ? 255 - i + palettesCount : i)); JsonArray curPalette = palettes.createNestedArray(String(i>=palettesCount ? 255 - i + palettesCount : i));
switch (i) { switch (i) {
case 0: //default palette case 0: //default palette
setPaletteColors(curPalette, PartyColors_p); setPaletteColors(curPalette, PartyColors_p);
break; break;
case 1: //random case 1: //random
curPalette.add("r"); curPalette.add("r");

View File

@ -102,19 +102,19 @@ void stateUpdated(byte callMode) {
if (bri != briOld || stateChanged) { if (bri != briOld || stateChanged) {
if (stateChanged) 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
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode); if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
//set flag to update blynk, ws and mqtt //set flag to update blynk, ws and mqtt
interfaceUpdateCallMode = callMode; interfaceUpdateCallMode = callMode;
stateChanged = false; stateChanged = false;
} else { } 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); notify(CALL_MODE_NIGHTLIGHT);
interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT; interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT;
} }
} }
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; briNlT = bri;
nightlightDelayMs -= (millis() - nightlightStartTime); nightlightDelayMs -= (millis() - nightlightStartTime);
@ -128,7 +128,7 @@ void stateUpdated(byte callMode) {
//deactivate nightlight if target brightness is reached //deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false; 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 //set correct delay if not using notification delay
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; // load actual transition duration if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; // load actual transition duration
@ -160,7 +160,7 @@ void updateInterfaces(uint8_t callMode)
sendDataWs(); sendDataWs();
lastInterfaceUpdate = millis(); lastInterfaceUpdate = millis();
if (callMode == CALL_MODE_WS_SEND) return; if (callMode == CALL_MODE_WS_SEND) return;
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) { if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
espalexaDevice->setValue(bri); espalexaDevice->setValue(bri);
@ -168,7 +168,7 @@ void updateInterfaces(uint8_t callMode)
} }
#endif #endif
#ifndef WLED_DISABLE_BLYNK #ifndef WLED_DISABLE_BLYNK
if (callMode != CALL_MODE_BLYNK && if (callMode != CALL_MODE_BLYNK &&
callMode != CALL_MODE_NO_NOTIFY) updateBlynk(); callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
#endif #endif
doPublishMqtt = true; doPublishMqtt = true;
@ -181,7 +181,7 @@ void handleTransitions()
//handle still pending interface update //handle still pending interface update
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode); if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode);
if (doPublishMqtt) publishMqtt(); if (doPublishMqtt) publishMqtt();
if (transitionActive && transitionDelayTemp > 0) if (transitionActive && transitionDelayTemp > 0)
{ {
float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp; float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp;
@ -196,7 +196,7 @@ void handleTransitions()
if (tper - tperLast < 0.004) return; if (tper - tperLast < 0.004) return;
tperLast = tper; tperLast = tper;
briT = briOld + ((bri - briOld) * tper); briT = briOld + ((bri - briOld) * tper);
applyBri(); applyBri();
} }
} }

View File

@ -13,13 +13,13 @@ bool parseLx(int lxValue, byte rgbw[4])
bool ok = false; bool ok = false;
float lxRed = 0, lxGreen = 0, lxBlue = 0; float lxRed = 0, lxGreen = 0, lxBlue = 0;
if (lxValue < 200000000) { if (lxValue < 200000000) {
// Loxone RGB // Loxone RGB
ok = true; ok = true;
lxRed = round((lxValue % 1000) * 2.55); lxRed = round((lxValue % 1000) * 2.55);
lxGreen = round(((lxValue / 1000) % 1000) * 2.55); lxGreen = round(((lxValue / 1000) % 1000) * 2.55);
lxBlue = round(((lxValue / 1000000) % 1000) * 2.55); lxBlue = round(((lxValue / 1000000) % 1000) * 2.55);
} else if ((lxValue >= 200000000) && (lxValue <= 201006500)) { } else if ((lxValue >= 200000000) && (lxValue <= 201006500)) {
// Loxone Lumitech // Loxone Lumitech
ok = true; ok = true;
float tmpBri = floor((lxValue - 200000000) / 10000); ; float tmpBri = floor((lxValue - 200000000) / 10000); ;

View File

@ -1,15 +1,15 @@
#pragma once #pragma once
/* /*
* Welcome! * Welcome!
* You can use the file "my_config.h" to make changes to the way WLED is compiled! * You can use the file "my_config.h" to make changes to the way WLED is compiled!
* It is possible to enable and disable certain features as well as set defaults for some runtime changeable settings. * It is possible to enable and disable certain features as well as set defaults for some runtime changeable settings.
* *
* How to use: * How to use:
* PlatformIO: Just compile the unmodified code once! The file "my_config.h" will be generated automatically and now you can make your changes. * PlatformIO: Just compile the unmodified code once! The file "my_config.h" will be generated automatically and now you can make your changes.
* *
* ArduinoIDE: Make a copy of this file and name it "my_config.h". Go to wled.h and uncomment "#define WLED_USE_MY_CONFIG" in the top of the file. * ArduinoIDE: Make a copy of this file and name it "my_config.h". Go to wled.h and uncomment "#define WLED_USE_MY_CONFIG" in the top of the file.
* *
* DO NOT make changes to the "my_config_sample.h" file directly! Your changes will not be applied. * DO NOT make changes to the "my_config_sample.h" file directly! Your changes will not be applied.
*/ */

View File

@ -20,27 +20,27 @@ const ethernet_settings ethernetBoards[] = {
// None // None
{ {
}, },
// WT32-EHT01 // WT32-EHT01
// Please note, from my testing only these pins work for LED outputs: // Please note, from my testing only these pins work for LED outputs:
// IO2, IO4, IO12, IO14, IO15 // IO2, IO4, IO12, IO14, IO15
// These pins do not appear to work from my testing: // These pins do not appear to work from my testing:
// IO35, IO36, IO39 // IO35, IO36, IO39
{ {
1, // eth_address, 1, // eth_address,
16, // eth_power, 16, // eth_power,
23, // eth_mdc, 23, // eth_mdc,
18, // eth_mdio, 18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type, ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO0_IN // eth_clk_mode ETH_CLOCK_GPIO0_IN // eth_clk_mode
}, },
// ESP32-POE // ESP32-POE
{ {
0, // eth_address, 0, // eth_address,
12, // eth_power, 12, // eth_power,
23, // eth_mdc, 23, // eth_mdc,
18, // eth_mdio, 18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type, ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode ETH_CLOCK_GPIO17_OUT // eth_clk_mode
}, },
@ -77,20 +77,20 @@ const ethernet_settings ethernetBoards[] = {
// ESP3DEUXQuattro // ESP3DEUXQuattro
{ {
1, // eth_address, 1, // eth_address,
-1, // eth_power, -1, // eth_power,
23, // eth_mdc, 23, // eth_mdc,
18, // eth_mdio, 18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type, ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode ETH_CLOCK_GPIO17_OUT // eth_clk_mode
}, },
// ESP32-ETHERNET-KIT-VE // ESP32-ETHERNET-KIT-VE
{ {
0, // eth_address, 0, // eth_address,
5, // eth_power, 5, // eth_power,
23, // eth_mdc, 23, // eth_mdc,
18, // eth_mdio, 18, // eth_mdio,
ETH_PHY_IP101, // eth_type, ETH_PHY_IP101, // eth_type,
ETH_CLOCK_GPIO0_IN // eth_clk_mode ETH_CLOCK_GPIO0_IN // eth_clk_mode
}, },
@ -135,7 +135,7 @@ void WiFiEvent(WiFiEvent_t event)
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
char hostname[25]; char hostname[25];
#endif #endif
switch (event) { switch (event) {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
case SYSTEM_EVENT_ETH_START: case SYSTEM_EVENT_ETH_START:

View File

@ -162,7 +162,7 @@ void updateTimezone() {
void handleTime() { void handleTime() {
handleNetworkTime(); handleNetworkTime();
toki.millisecond(); toki.millisecond();
toki.setTick(); toki.setTick();
@ -363,7 +363,7 @@ void checkTimers()
{ {
if (timerMacro[i] != 0 if (timerMacro[i] != 0
&& (timerWeekday[i] & 0x01) //timer is enabled && (timerWeekday[i] & 0x01) //timer is enabled
&& (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour && (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour
&& timerMinutes[i] == minute(localTime) && timerMinutes[i] == minute(localTime)
&& ((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]) && isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])
@ -416,24 +416,24 @@ int getSunriseUTC(int year, int month, int day, float lat, float lon, bool sunse
float N = N1 - (N2 * N3) + day - 30; float N = N1 - (N2 * N3) + day - 30;
//2. convert the longitude to hour value and calculate an approximate time //2. convert the longitude to hour value and calculate an approximate time
float lngHour = lon / 15.0f; float lngHour = lon / 15.0f;
float t = N + (((sunset ? 18 : 6) - lngHour) / 24); float t = N + (((sunset ? 18 : 6) - lngHour) / 24);
//3. calculate the Sun's mean anomaly //3. calculate the Sun's mean anomaly
float M = (0.9856f * t) - 3.289f; float M = (0.9856f * t) - 3.289f;
//4. calculate the Sun's true longitude //4. calculate the Sun's true longitude
float L = fmod_t(M + (1.916f * sin_t(DEG_TO_RAD*M)) + (0.02f * sin_t(2*DEG_TO_RAD*M)) + 282.634f, 360.0f); float L = fmod_t(M + (1.916f * sin_t(DEG_TO_RAD*M)) + (0.02f * sin_t(2*DEG_TO_RAD*M)) + 282.634f, 360.0f);
//5a. calculate the Sun's right ascension //5a. calculate the Sun's right ascension
float RA = fmod_t(RAD_TO_DEG*atan_t(0.91764f * tan_t(DEG_TO_RAD*L)), 360.0f); float RA = fmod_t(RAD_TO_DEG*atan_t(0.91764f * tan_t(DEG_TO_RAD*L)), 360.0f);
//5b. right ascension value needs to be in the same quadrant as L //5b. right ascension value needs to be in the same quadrant as L
float Lquadrant = floor_t( L/90) * 90; float Lquadrant = floor_t( L/90) * 90;
float RAquadrant = floor_t(RA/90) * 90; float RAquadrant = floor_t(RA/90) * 90;
RA = RA + (Lquadrant - RAquadrant); RA = RA + (Lquadrant - RAquadrant);
//5c. right ascension value needs to be converted into hours //5c. right ascension value needs to be converted into hours
RA /= 15.0f; RA /= 15.0f;
//6. calculate the Sun's declination //6. calculate the Sun's declination
@ -449,7 +449,7 @@ int getSunriseUTC(int year, int month, int day, float lat, float lon, bool sunse
float H = sunset ? RAD_TO_DEG*acos_t(cosH) : 360 - RAD_TO_DEG*acos_t(cosH); float H = sunset ? RAD_TO_DEG*acos_t(cosH) : 360 - RAD_TO_DEG*acos_t(cosH);
H /= 15.0f; H /= 15.0f;
//8. calculate local mean time of rising/setting //8. calculate local mean time of rising/setting
float T = H + RA - (0.06571f * t) - 6.622f; float T = H + RA - (0.06571f * t) - 6.622f;
//9. adjust back to UTC //9. adjust back to UTC

View File

@ -18,7 +18,7 @@ void _overlayAnalogClock()
int hourPixel = floor(analogClock12pixel + overlaySize*hourP); int hourPixel = floor(analogClock12pixel + overlaySize*hourP);
if (hourPixel > overlayMax) hourPixel = overlayMin -1 + hourPixel - overlayMax; if (hourPixel > overlayMax) hourPixel = overlayMin -1 + hourPixel - overlayMax;
int minutePixel = floor(analogClock12pixel + overlaySize*minuteP); int minutePixel = floor(analogClock12pixel + overlaySize*minuteP);
if (minutePixel > overlayMax) minutePixel = overlayMin -1 + minutePixel - overlayMax; if (minutePixel > overlayMax) minutePixel = overlayMin -1 + minutePixel - overlayMax;
int secondPixel = floor(analogClock12pixel + overlaySize*secondP); int secondPixel = floor(analogClock12pixel + overlaySize*secondP);
if (secondPixel > overlayMax) secondPixel = overlayMin -1 + secondPixel - overlayMax; if (secondPixel > overlayMax) secondPixel = overlayMin -1 + secondPixel - overlayMax;
if (analogClockSecondsTrail) if (analogClockSecondsTrail)

View File

@ -514,7 +514,7 @@ const byte Tiamat_gp[] PROGMEM = {
220, 39,252,135, //gc from 177,254,255 220, 39,252,135, //gc from 177,254,255
240, 193,213,253, //gc from 203,239,253 240, 193,213,253, //gc from 203,239,253
255, 255,249,255}; 255, 255,249,255};
//Custom palette by Aircoookie //Custom palette by Aircoookie
const byte April_Night_gp[] PROGMEM = { const byte April_Night_gp[] PROGMEM = {
@ -598,16 +598,16 @@ const byte Atlantica_gp[] PROGMEM = {
0, 255, 5, 0, //red 0, 255, 5, 0, //red
60, 255, 5, 0, 60, 255, 5, 0,
60, 196, 57, 2, //amber (start 61?) 60, 196, 57, 2, //amber (start 61?)
120, 196, 57, 2, 120, 196, 57, 2,
120, 6, 126, 2, //green (start 126?) 120, 6, 126, 2, //green (start 126?)
180, 6, 126, 2, 180, 6, 126, 2,
180, 4, 30, 114, //blue (start 191?) 180, 4, 30, 114, //blue (start 191?)
255, 4, 30, 114}; 255, 4, 30, 114};
// Gradient palette "temperature_gp", originally from // Gradient palette "temperature_gp", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/arendal/tn/temperature.png.index.html // http://soliton.vm.bytemark.co.uk/pub/cpt-city/arendal/tn/temperature.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5) // converted for FastLED with gammas (2.6, 2.2, 2.5)
// Size: 144 bytes of program space. // Size: 144 bytes of program space.
const byte temperature_gp[] PROGMEM = { const byte temperature_gp[] PROGMEM = {
0, 1, 27,105, 0, 1, 27,105,

View File

@ -31,7 +31,7 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
#endif #endif
return false; return false;
} }
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - 8*by; byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, false); bitWrite(pinAlloc[by], bi, false);
@ -119,7 +119,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
continue; continue;
} else if (isPinAllocated(gpio)) { } else if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: FAIL: IO ")); DEBUG_PRINT(F("PIN ALLOC: FAIL: IO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by ")); DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
@ -143,7 +143,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
// as this can greatly simplify configuration arrays // as this can greatly simplify configuration arrays
continue; continue;
} }
if (gpio >= WLED_NUM_PINS) if (gpio >= WLED_NUM_PINS)
continue; // other unexpected GPIO => avoid array bounds violation continue; // other unexpected GPIO => avoid array bounds violation
byte by = gpio >> 3; byte by = gpio >> 3;
@ -151,7 +151,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
bitWrite(pinAlloc[by], bi, true); bitWrite(pinAlloc[by], bi, true);
ownerTag[gpio] = tag; ownerTag[gpio] = tag;
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by ")); DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(tag); DebugPrintOwnerTag(tag);
@ -183,7 +183,7 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
} }
if (isPinAllocated(gpio)) { if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by ")); DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
@ -197,12 +197,12 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
bitWrite(pinAlloc[by], bi, true); bitWrite(pinAlloc[by], bi, true);
ownerTag[gpio] = tag; ownerTag[gpio] = tag;
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" successfully allocated by ")); DEBUG_PRINT(F(" successfully allocated by "));
DebugPrintOwnerTag(tag); DebugPrintOwnerTag(tag);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif #endif
return true; return true;
} }
@ -224,9 +224,9 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
* Strapping pins: GPIO0, GPIO3, GPIO45 and GPIO46 are strapping pins. For more infomation, please refer to ESP32-S3 datasheet. * Strapping pins: GPIO0, GPIO3, GPIO45 and GPIO46 are strapping pins. For more infomation, please refer to ESP32-S3 datasheet.
* Serial TX = GPIO43, RX = GPIO44; LED BUILTIN is usually GPIO39 * Serial TX = GPIO43, RX = GPIO44; LED BUILTIN is usually GPIO39
* USB-JTAG: GPIO 19 and 20 are used by USB-JTAG by default. In order to use them as GPIOs, USB-JTAG will be disabled by the drivers. * USB-JTAG: GPIO 19 and 20 are used by USB-JTAG by default. In order to use them as GPIOs, USB-JTAG will be disabled by the drivers.
* SPI0/1: GPIO26-32 are usually used for SPI flash and PSRAM and not recommended for other uses. * SPI0/1: GPIO26-32 are usually used for SPI flash and PSRAM and not recommended for other uses.
* When using Octal Flash or Octal PSRAM or both, GPIO33~37 are connected to SPIIO4 ~ SPIIO7 and SPIDQS. Therefore, on boards embedded with ESP32-S3R8 / ESP32-S3R8V chip, GPIO33~37 are also not recommended for other uses. * When using Octal Flash or Octal PSRAM or both, GPIO33~37 are connected to SPIIO4 ~ SPIIO7 and SPIDQS. Therefore, on boards embedded with ESP32-S3R8 / ESP32-S3R8V chip, GPIO33~37 are also not recommended for other uses.
* *
* see https://docs.espressif.com/projects/esp-idf/en/v4.4.2/esp32s3/api-reference/peripherals/adc.html * see https://docs.espressif.com/projects/esp-idf/en/v4.4.2/esp32s3/api-reference/peripherals/adc.html
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/adc_oneshot.html * https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/adc_oneshot.html
* ADC1: GPIO1 - GPIO10 (channel 0..9) * ADC1: GPIO1 - GPIO10 (channel 0..9)

View File

@ -15,7 +15,7 @@ typedef struct PinManagerPinType {
* Allows PinManager to "lock" an allocation to a specific * Allows PinManager to "lock" an allocation to a specific
* owner, so someone else doesn't accidentally de-allocate * owner, so someone else doesn't accidentally de-allocate
* a pin it hasn't allocated. Also enhances debugging. * a pin it hasn't allocated. Also enhances debugging.
* *
* RAM Cost: * RAM Cost:
* 17 bytes on ESP8266 * 17 bytes on ESP8266
* 40 bytes on ESP32 * 40 bytes on ESP32
@ -50,16 +50,17 @@ enum struct PinOwner : uint8_t {
// #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" HW_I2C pins // #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_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h"
UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h" UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h"
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins UM_Battery = USERMOD_ID_BATTERY, //
// #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_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 // #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_BH1750 = USERMOD_ID_BH1750, // 0x14 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.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" UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
UM_BME280 = USERMOD_ID_BME280, // 0x18 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins UM_BME280 = USERMOD_ID_BME280, // 0x1E // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
UM_BH1750 = USERMOD_ID_BH1750, // 0x19 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE, // 0x20 // Usermod "audio_reactive.h"
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE, // 0x1E // Usermod "audio_reactive.h" UM_SdCard = USERMOD_ID_SD_CARD, // 0x25 // Usermod "usermod_sd_card.h"
UM_SdCard = USERMOD_ID_SD_CARD, // 0x24 // Usermod "usermod_sd_card.h" UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS // 0x26 // Usermod "usermod_pwm_outputs.h"
UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS // 0x21 // Usermod "usermod_pwm_outputs.h"
}; };
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
@ -91,7 +92,7 @@ class PinManagerClass {
// De-allocation requires the same owner tag (or override) // De-allocation requires the same owner tag (or override)
bool allocatePin(byte gpio, bool output, PinOwner tag); bool allocatePin(byte gpio, bool output, PinOwner tag);
// Allocates all the pins, or allocates none of the pins, with owner tag. // Allocates all the pins, or allocates none of the pins, with owner tag.
// Provided to simplify error condition handling in clients // Provided to simplify error condition handling in clients
// using more than one pin, such as I2C, SPI, rotary encoders, // using more than one pin, such as I2C, SPI, rotary encoders,
// ethernet, etc.. // ethernet, etc..
bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag ); bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag );

View File

@ -55,7 +55,7 @@ void unloadPlaylist() {
int16_t loadPlaylist(JsonObject playlistObj, byte presetId) { int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
unloadPlaylist(); unloadPlaylist();
JsonArray presets = playlistObj["ps"]; JsonArray presets = playlistObj["ps"];
playlistLen = presets.size(); playlistLen = presets.size();
if (playlistLen == 0) return -1; if (playlistLen == 0) return -1;

View File

@ -79,12 +79,12 @@ static void doSaveState() {
playlistSave = false; playlistSave = false;
} }
bool getPresetName(byte index, String& name) bool getPresetName(byte index, String& name)
{ {
if (!requestJSONBufferLock(9)) return false; if (!requestJSONBufferLock(9)) return false;
bool presetExists = false; bool presetExists = false;
if (readObjectFromFileUsingId(getFileName(), index, &doc)) if (readObjectFromFileUsingId(getFileName(), index, &doc))
{ {
JsonObject fdo = doc.as<JsonObject>(); JsonObject fdo = doc.as<JsonObject>();
if (fdo["n"]) { if (fdo["n"]) {
name = (const char*)(fdo["n"]); name = (const char*)(fdo["n"]);

View File

@ -88,7 +88,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strip.useLedsArray = request->hasArg(F("LD")); strip.useLedsArray = request->hasArg(F("LD"));
bool busesChanged = false; bool busesChanged = false;
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) { for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order
@ -171,14 +171,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
buttonType[i] = request->arg(be).toInt(); buttonType[i] = request->arg(be).toInt();
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// ESP32 only: check that analog button pin is a valid ADC gpio // ESP32 only: check that analog button pin is a valid ADC gpio
if (((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[i]) < 0)) if (((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[i]) < 0))
{ {
// not an ADC analog pin // not an ADC analog pin
if (btnPin[i] >= 0) DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[i], i); if (btnPin[i] >= 0) DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[i], i);
btnPin[i] = -1; btnPin[i] = -1;
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button); pinManager.deallocatePin(hw_btn_pin,PinOwner::Button);
} }
else else
#endif #endif
{ {
if (disablePullUp) { if (disablePullUp) {
@ -200,7 +200,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strip.ablMilliampsMax = request->arg(F("MA")).toInt(); strip.ablMilliampsMax = request->arg(F("MA")).toInt();
strip.milliampsPerLed = request->arg(F("LA")).toInt(); strip.milliampsPerLed = request->arg(F("LA")).toInt();
briS = request->arg(F("CA")).toInt(); briS = request->arg(F("CA")).toInt();
turnOnAtBoot = request->hasArg(F("BO")); turnOnAtBoot = request->hasArg(F("BO"));
@ -364,7 +364,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
longitude = request->arg(F("LN")).toFloat(); longitude = request->arg(F("LN")).toFloat();
latitude = request->arg(F("LT")).toFloat(); latitude = request->arg(F("LT")).toFloat();
// force a sunrise/sunset re-calculation // force a sunrise/sunset re-calculation
calculateSunriseAndSunset(); calculateSunriseAndSunset();
overlayCurrent = request->hasArg(F("OL")) ? 1 : 0; overlayCurrent = request->hasArg(F("OL")) ? 1 : 0;
@ -645,22 +645,37 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (subPage == 10) if (subPage == 10)
{ {
strip.isMatrix = request->arg(F("SOMP")).toInt(); strip.isMatrix = request->arg(F("SOMP")).toInt();
strip.panelH = MAX(1,MIN(128,request->arg(F("PH")).toInt())); // strip.panelH = MAX(1,MIN(128,request->arg(F("PH")).toInt()));
strip.panelW = MAX(1,MIN(128,request->arg(F("PW")).toInt())); // strip.panelW = MAX(1,MIN(128,request->arg(F("PW")).toInt()));
strip.hPanels = MAX(1,MIN(8,request->arg(F("MPH")).toInt())); strip.panel.clear(); // release memory if allocated
strip.vPanels = MAX(1,MIN(8,request->arg(F("MPV")).toInt())); if (strip.isMatrix) {
strip.matrix.bottomStart = request->arg(F("PB")).toInt(); strip.panels = MAX(1,MIN(WLED_MAX_PANELS,request->arg(F("MPC")).toInt()));
strip.matrix.rightStart = request->arg(F("PR")).toInt(); strip.matrix.bottomStart = request->arg(F("PB")).toInt();
strip.matrix.vertical = request->arg(F("PV")).toInt(); strip.matrix.rightStart = request->arg(F("PR")).toInt();
strip.matrix.serpentine = request->hasArg(F("PS")); strip.matrix.vertical = request->arg(F("PV")).toInt();
for (uint8_t i=0; i<WLED_MAX_PANELS; i++) { strip.matrix.serpentine = request->hasArg(F("PS"));
char pO[8]; sprintf_P(pO, PSTR("P%d"), i); strip.panel.reserve(strip.panels); // pre-allocate memory
uint8_t l = strlen(pO); pO[l+1] = 0; for (uint8_t i=0; i<strip.panels; i++) {
pO[l] = 'B'; if (!request->hasArg(pO)) break; WS2812FX::Panel p;
pO[l] = 'B'; strip.panel[i].bottomStart = request->arg(pO).toInt(); char pO[8] = { '\0' };
pO[l] = 'R'; strip.panel[i].rightStart = request->arg(pO).toInt(); snprintf_P(pO, 7, PSTR("P%d"), i); // MAX_PANELS is 64 so pO will always only be 4 characters or less
pO[l] = 'V'; strip.panel[i].vertical = request->arg(pO).toInt(); pO[7] = '\0';
pO[l] = 'S'; strip.panel[i].serpentine = request->hasArg(pO); uint8_t l = strlen(pO);
// create P0B, P1B, ..., P63B, etc for other PxxX
pO[l] = 'B'; if (!request->hasArg(pO)) break;
pO[l] = 'B'; p.bottomStart = request->arg(pO).toInt();
pO[l] = 'R'; p.rightStart = request->arg(pO).toInt();
pO[l] = 'V'; p.vertical = request->arg(pO).toInt();
pO[l] = 'S'; p.serpentine = request->hasArg(pO);
pO[l] = 'X'; p.xOffset = request->arg(pO).toInt();
pO[l] = 'Y'; p.yOffset = request->arg(pO).toInt();
pO[l] = 'W'; p.width = request->arg(pO).toInt();
pO[l] = 'H'; p.height = request->arg(pO).toInt();
strip.panel.push_back(p);
}
} else {
Segment::maxWidth = strip.getLengthTotal();
Segment::maxHeight = 1;
} }
strip.setUpMatrix(); // will check limits strip.setUpMatrix(); // will check limits
} }
@ -746,7 +761,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
spcI = getNumVal(&req, pos); spcI = getNumVal(&req, pos);
} }
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY); selseg.set(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0'; if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';

View File

@ -41,7 +41,7 @@ void notify(byte callMode, bool followUp)
udpOut[8] = mainseg.mode; udpOut[8] = mainseg.mode;
udpOut[9] = mainseg.speed; udpOut[9] = mainseg.speed;
udpOut[10] = W(col); udpOut[10] = W(col);
//compatibilityVersionByte: //compatibilityVersionByte:
//0: old 1: supports white 2: supports secondary color //0: old 1: supports white 2: supports secondary color
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette //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 //6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
@ -62,7 +62,7 @@ void notify(byte callMode, bool followUp)
udpOut[21] = G(col); udpOut[21] = G(col);
udpOut[22] = B(col); udpOut[22] = B(col);
udpOut[23] = W(col); udpOut[23] = W(col);
udpOut[24] = followUp; udpOut[24] = followUp;
uint32_t t = millis() + strip.timebase; uint32_t t = millis() + strip.timebase;
udpOut[25] = (t >> 24) & 0xFF; udpOut[25] = (t >> 24) & 0xFF;
@ -219,7 +219,7 @@ void handleNotifications()
if(udpConnected && (notificationCount < udpNumRetries) && ((millis()-notificationSentTime) > 250)){ if(udpConnected && (notificationCount < udpNumRetries) && ((millis()-notificationSentTime) > 250)){
notify(notificationSentCallMode,true); notify(notificationSentCallMode,true);
} }
if (e131NewData && millis() - strip.getLastShow() > 15) if (e131NewData && millis() - strip.getLastShow() > 15)
{ {
e131NewData = false; e131NewData = false;
@ -231,7 +231,7 @@ void handleNotifications()
//receive UDP notifications //receive UDP notifications
if (!udpConnected) return; if (!udpConnected) return;
bool isSupp = false; bool isSupp = false;
size_t packetSize = notifierUdp.parsePacket(); size_t packetSize = notifierUdp.parsePacket();
if (!packetSize && udp2Connected) { if (!packetSize && udp2Connected) {
@ -260,11 +260,11 @@ void handleNotifications()
} }
if (!(realtimeMode && useMainSegmentOnly)) strip.show(); if (!(realtimeMode && useMainSegmentOnly)) strip.show();
return; return;
} }
} }
if (!(receiveNotifications || receiveDirect)) return; if (!(receiveNotifications || receiveDirect)) return;
localIP = Network.localIP(); localIP = Network.localIP();
//notifier and UDP realtime //notifier and UDP realtime
if (!packetSize || packetSize > UDP_IN_MAXSIZE) return; if (!packetSize || packetSize > UDP_IN_MAXSIZE) return;
@ -313,7 +313,7 @@ void handleNotifications()
if (millis() - notificationSentTime < 1000) return; if (millis() - notificationSentTime < 1000) return;
if (udpIn[1] > 199) return; //do not receive custom versions if (udpIn[1] > 199) return; //do not receive custom versions
//compatibilityVersionByte: //compatibilityVersionByte:
byte version = udpIn[11]; byte version = udpIn[11];
// if we are not part of any sync group ignore message // if we are not part of any sync group ignore message
@ -321,7 +321,7 @@ void handleNotifications()
// legacy senders are treated as if sending in sync group 1 only // legacy senders are treated as if sending in sync group 1 only
if (!(receiveGroups & 0x01)) return; if (!(receiveGroups & 0x01)) return;
} else if (!(receiveGroups & udpIn[36])) return; } else if (!(receiveGroups & udpIn[36])) return;
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification to main segment, only if not syncing full segments //apply colors from notification to main segment, only if not syncing full segments
@ -336,7 +336,7 @@ void handleNotifications()
if (version > 9 && version < 200 && udpIn[37] < 255) { // valid CCT/Kelvin value if (version > 9 && version < 200 && udpIn[37] < 255) { // valid CCT/Kelvin value
uint16_t cct = udpIn[38]; uint16_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin if (udpIn[37] > 0) { //Kelvin
cct |= (udpIn[37] << 8); cct |= (udpIn[37] << 8);
} }
strip.setCCT(cct); strip.setCCT(cct);
} }
@ -363,7 +363,7 @@ void handleNotifications()
uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]); uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]); uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
if (!receiveSegmentOptions) { if (!receiveSegmentOptions) {
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); selseg.set(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
continue; continue;
} }
//for (size_t j = 1; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, on, reversed; ignore selected //for (size_t j = 1; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, on, reversed; ignore selected
@ -396,16 +396,15 @@ void handleNotifications()
startY = (udpIn[32+ofs] << 8 | udpIn[33+ofs]); startY = (udpIn[32+ofs] << 8 | udpIn[33+ofs]);
stopY = (udpIn[34+ofs] << 8 | udpIn[35+ofs]); stopY = (udpIn[34+ofs] << 8 | udpIn[35+ofs]);
} }
//setSegment() also properly resets segments
if (receiveSegmentBounds) { if (receiveSegmentBounds) {
strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY); selseg.set(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
} else { } else {
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY); selseg.set(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
} }
} }
stateChanged = true; stateChanged = true;
} }
// simple effect sync, applies to all selected segments // simple effect sync, applies to all selected segments
if (applyEffects && (version < 11 || !receiveSegmentOptions)) { if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
for (size_t i = 0; i < strip.getSegmentsNum(); i++) { for (size_t i = 0; i < strip.getSegmentsNum(); i++) {
@ -451,7 +450,7 @@ void handleNotifications()
} }
} }
} }
if (version > 3) if (version > 3)
{ {
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00); transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
@ -459,14 +458,14 @@ void handleNotifications()
nightlightActive = udpIn[6]; nightlightActive = udpIn[6];
if (nightlightActive) nightlightDelayMins = udpIn[7]; if (nightlightActive) nightlightDelayMins = udpIn[7];
if (receiveNotificationBrightness || !someSel) bri = udpIn[2]; if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
stateUpdated(CALL_MODE_NOTIFICATION); stateUpdated(CALL_MODE_NOTIFICATION);
return; return;
} }
if (!receiveDirect) return; if (!receiveDirect) return;
//TPM2.NET //TPM2.NET
if (udpIn[0] == 0x9c) if (udpIn[0] == 0x9c)
{ {
@ -544,7 +543,7 @@ void handleNotifications()
for (size_t i = 2; i < packetSize -3; i += 4) for (size_t i = 2; i < packetSize -3; i += 4)
{ {
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
id++; if (id >= totalLen) break; id++; if (id >= totalLen) break;
} }
} else if (udpIn[0] == 4) //dnrgb } else if (udpIn[0] == 4) //dnrgb
@ -653,7 +652,7 @@ void sendSysInfoUDP()
uint8_t data[44] = {0}; uint8_t data[44] = {0};
data[0] = 255; data[0] = 255;
data[1] = 1; data[1] = 1;
for (size_t x = 0; x < 4; x++) { for (size_t x = 0; x < 4; x++) {
data[x + 2] = ip[x]; data[x + 2] = ip[x];
} }
@ -718,7 +717,7 @@ void sendSysInfoUDP()
uint8_t sequenceNumber = 0; // this needs to be shared across all outputs uint8_t sequenceNumber = 0; // this needs to be shared across all outputs
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri, bool isRGBW) { uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri, bool isRGBW) {
if (!(apActive || interfacesInited) || !client[0] || !length) return 1; // network not initialised or dummy/unset IP address 031522 ajn added check for ap if (!(apActive || interfacesInited) || !client[0] || !length) return 1; // network not initialised or dummy/unset IP address 031522 ajn added check for ap
WiFiUDP ddpUdp; WiFiUDP ddpUdp;
@ -731,7 +730,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
// there are 3 channels per RGB pixel // there are 3 channels per RGB pixel
uint32_t channel = 0; // TODO: allow specifying the start channel uint32_t channel = 0; // TODO: allow specifying the start channel
// the current position in the buffer // the current position in the buffer
size_t bufferOffset = 0; size_t bufferOffset = 0;
for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) {
@ -769,7 +768,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
/*8*/ddpUdp.write(0xFF & (packetSize >> 8)); /*8*/ddpUdp.write(0xFF & (packetSize >> 8));
/*9*/ddpUdp.write(0xFF & (packetSize )); /*9*/ddpUdp.write(0xFF & (packetSize ));
// write the colors, the write write(const uint8_t *buffer, size_t size) // write the colors, the write write(const uint8_t *buffer, size_t size)
// function is just a loop internally too // function is just a loop internally too
for (size_t i = 0; i < packetSize; i += 3) { for (size_t i = 0; i < packetSize; i += 3) {
ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // R ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // R
@ -778,7 +777,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W
} }
if (!ddpUdp.endPacket()) { if (!ddpUdp.endPacket()) {
DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error")); DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error"));
return 1; // problem return 1; // problem
} }

View File

@ -9,14 +9,14 @@ void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) um
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); } void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); }
void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); } void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); }
void UsermodManager::appendConfigData() { for (byte i = 0; i < numMods; i++) ums[i]->appendConfigData(); } void UsermodManager::appendConfigData() { for (byte i = 0; i < numMods; i++) ums[i]->appendConfigData(); }
bool UsermodManager::handleButton(uint8_t b) { bool UsermodManager::handleButton(uint8_t b) {
bool overrideIO = false; bool overrideIO = false;
for (byte i = 0; i < numMods; i++) { for (byte i = 0; i < numMods; i++) {
if (ums[i]->handleButton(b)) overrideIO = true; if (ums[i]->handleButton(b)) overrideIO = true;
} }
return overrideIO; return overrideIO;
} }
bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) { bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) {
for (byte i = 0; i < numMods; i++) { for (byte i = 0; i < numMods; i++) {
if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided
if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can povide data at one time) if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can povide data at one time)
@ -27,7 +27,7 @@ void UsermodManager::addToJsonState(JsonObject& obj) { for (byte i = 0; i < n
void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); } void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); }
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); } void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); } void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
bool UsermodManager::readFromConfig(JsonObject& obj) { bool UsermodManager::readFromConfig(JsonObject& obj) {
bool allComplete = true; bool allComplete = true;
for (byte i = 0; i < numMods; i++) { for (byte i = 0; i < numMods; i++) {
if (!ums[i]->readFromConfig(obj)) allComplete = false; if (!ums[i]->readFromConfig(obj)) allComplete = false;

View File

@ -4,7 +4,7 @@
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality * 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) * 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) * 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! * Consider the v2 usermod API if you need a more advanced feature set!
*/ */
@ -13,7 +13,7 @@
//gets called once at boot. Do all initialization that doesn't depend on network here //gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup() void userSetup()
{ {
} }
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here //gets called every time WiFi is (re-)connected. Initialize own network interfaces here
@ -25,5 +25,5 @@ void userConnected()
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection //loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop() void userLoop()
{ {
} }

View File

@ -6,13 +6,13 @@
class UsermodRenameMe : public Usermod { class UsermodRenameMe : public Usermod {
private: private:
public: public:
void setup() { void setup() {
} }
void loop() { void loop() {
} }
}; };

View File

@ -11,8 +11,8 @@
*/ */
//#include "../usermods/EXAMPLE_v2/usermod_v2_example.h" //#include "../usermods/EXAMPLE_v2/usermod_v2_example.h"
#ifdef USERMOD_BATTERY_STATUS_BASIC #ifdef USERMOD_BATTERY
#include "../usermods/battery_status_basic/usermod_v2_battery_status_basic.h" #include "../usermods/Battery/usermod_v2_Battery.h"
#endif #endif
#ifdef USERMOD_DALLASTEMPERATURE #ifdef USERMOD_DALLASTEMPERATURE
@ -55,7 +55,7 @@
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
#ifdef USE_ALT_DISPlAY #ifdef USE_ALT_DISPlAY
#include "../usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h" #include "../usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h"
#else #else
#include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h" #include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h"
#endif #endif
#endif #endif
@ -169,7 +169,7 @@
// resolved correctly (when included in mod's header only) // resolved correctly (when included in mod's header only)
#ifdef WLED_USE_SD_MMC #ifdef WLED_USE_SD_MMC
#include "SD_MMC.h" #include "SD_MMC.h"
#elif defined(WLED_USE_SD_SPI) #elif defined(WLED_USE_SD_SPI)
#include "SD.h" #include "SD.h"
#include "SPI.h" #include "SPI.h"
#endif #endif
@ -193,122 +193,122 @@ void registerUsermods()
* \/ \/ \/ * \/ \/ \/
*/ */
//usermods.add(new MyExampleUsermod()); //usermods.add(new MyExampleUsermod());
#ifdef USERMOD_BATTERY_STATUS_BASIC #ifdef USERMOD_BATTERY
usermods.add(new UsermodBatteryBasic()); usermods.add(new UsermodBattery());
#endif #endif
#ifdef USERMOD_DALLASTEMPERATURE #ifdef USERMOD_DALLASTEMPERATURE
usermods.add(new UsermodTemperature()); usermods.add(new UsermodTemperature());
#endif #endif
#ifdef USERMOD_SN_PHOTORESISTOR #ifdef USERMOD_SN_PHOTORESISTOR
usermods.add(new Usermod_SN_Photoresistor()); usermods.add(new Usermod_SN_Photoresistor());
#endif #endif
#ifdef USERMOD_PWM_FAN #ifdef USERMOD_PWM_FAN
usermods.add(new PWMFanUsermod()); usermods.add(new PWMFanUsermod());
#endif #endif
#ifdef USERMOD_BUZZER #ifdef USERMOD_BUZZER
usermods.add(new BuzzerUsermod()); usermods.add(new BuzzerUsermod());
#endif #endif
#ifdef USERMOD_BH1750 #ifdef USERMOD_BH1750
usermods.add(new Usermod_BH1750()); usermods.add(new Usermod_BH1750());
#endif #endif
#ifdef USERMOD_BME280 #ifdef USERMOD_BME280
usermods.add(new UsermodBME280()); usermods.add(new UsermodBME280());
#endif #endif
#ifdef USERMOD_SENSORSTOMQTT #ifdef USERMOD_SENSORSTOMQTT
usermods.add(new UserMod_SensorsToMQTT()); usermods.add(new UserMod_SensorsToMQTT());
#endif #endif
#ifdef USERMOD_PIRSWITCH #ifdef USERMOD_PIRSWITCH
usermods.add(new PIRsensorSwitch()); usermods.add(new PIRsensorSwitch());
#endif #endif
#ifdef USERMOD_MODE_SORT #ifdef USERMOD_MODE_SORT
usermods.add(new ModeSortUsermod()); usermods.add(new ModeSortUsermod());
#endif #endif
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
usermods.add(new FourLineDisplayUsermod()); usermods.add(new FourLineDisplayUsermod());
#endif #endif
#ifdef USERMOD_ROTARY_ENCODER_UI #ifdef USERMOD_ROTARY_ENCODER_UI
usermods.add(new RotaryEncoderUIUsermod()); // can use USERMOD_FOUR_LINE_DISPLAY usermods.add(new RotaryEncoderUIUsermod()); // can use USERMOD_FOUR_LINE_DISPLAY
#endif #endif
#ifdef USERMOD_AUTO_SAVE #ifdef USERMOD_AUTO_SAVE
usermods.add(new AutoSaveUsermod()); // can use USERMOD_FOUR_LINE_DISPLAY usermods.add(new AutoSaveUsermod()); // can use USERMOD_FOUR_LINE_DISPLAY
#endif #endif
#ifdef USERMOD_DHT #ifdef USERMOD_DHT
usermods.add(new UsermodDHT()); usermods.add(new UsermodDHT());
#endif #endif
#ifdef USERMOD_VL53L0X_GESTURES #ifdef USERMOD_VL53L0X_GESTURES
usermods.add(new UsermodVL53L0XGestures()); usermods.add(new UsermodVL53L0XGestures());
#endif #endif
#ifdef USERMOD_ANIMATED_STAIRCASE #ifdef USERMOD_ANIMATED_STAIRCASE
usermods.add(new Animated_Staircase()); usermods.add(new Animated_Staircase());
#endif #endif
#ifdef USERMOD_MULTI_RELAY #ifdef USERMOD_MULTI_RELAY
usermods.add(new MultiRelay()); usermods.add(new MultiRelay());
#endif #endif
#ifdef USERMOD_RTC #ifdef USERMOD_RTC
usermods.add(new RTCUsermod()); usermods.add(new RTCUsermod());
#endif #endif
#ifdef USERMOD_ELEKSTUBE_IPS #ifdef USERMOD_ELEKSTUBE_IPS
usermods.add(new ElekstubeIPSUsermod()); usermods.add(new ElekstubeIPSUsermod());
#endif #endif
#ifdef USERMOD_ROTARY_ENCODER_BRIGHTNESS_COLOR #ifdef USERMOD_ROTARY_ENCODER_BRIGHTNESS_COLOR
usermods.add(new RotaryEncoderBrightnessColor()); usermods.add(new RotaryEncoderBrightnessColor());
#endif #endif
#ifdef RGB_ROTARY_ENCODER #ifdef RGB_ROTARY_ENCODER
usermods.add(new RgbRotaryEncoderUsermod()); usermods.add(new RgbRotaryEncoderUsermod());
#endif #endif
#ifdef USERMOD_ST7789_DISPLAY #ifdef USERMOD_ST7789_DISPLAY
usermods.add(new St7789DisplayUsermod()); usermods.add(new St7789DisplayUsermod());
#endif #endif
#ifdef USERMOD_SEVEN_SEGMENT #ifdef USERMOD_SEVEN_SEGMENT
usermods.add(new SevenSegmentDisplay()); usermods.add(new SevenSegmentDisplay());
#endif #endif
#ifdef USERMOD_SSDR #ifdef USERMOD_SSDR
usermods.add(new UsermodSSDR()); usermods.add(new UsermodSSDR());
#endif #endif
#ifdef USERMOD_CRONIXIE #ifdef USERMOD_CRONIXIE
usermods.add(new UsermodCronixie()); usermods.add(new UsermodCronixie());
#endif #endif
#ifdef QUINLED_AN_PENTA #ifdef QUINLED_AN_PENTA
usermods.add(new QuinLEDAnPentaUsermod()); usermods.add(new QuinLEDAnPentaUsermod());
#endif #endif
#ifdef USERMOD_WIZLIGHTS #ifdef USERMOD_WIZLIGHTS
usermods.add(new WizLightsUsermod()); usermods.add(new WizLightsUsermod());
#endif #endif
#ifdef USERMOD_WORDCLOCK #ifdef USERMOD_WORDCLOCK
usermods.add(new WordClockUsermod()); usermods.add(new WordClockUsermod());
#endif #endif
#ifdef USERMOD_MY9291 #ifdef USERMOD_MY9291
usermods.add(new MY9291Usermod()); usermods.add(new MY9291Usermod());
#endif #endif
#ifdef USERMOD_SI7021_MQTT_HA #ifdef USERMOD_SI7021_MQTT_HA
usermods.add(new Si7021_MQTT_HA()); usermods.add(new Si7021_MQTT_HA());
#endif #endif
@ -316,7 +316,7 @@ void registerUsermods()
#ifdef USERMOD_SMARTNEST #ifdef USERMOD_SMARTNEST
usermods.add(new Smartnest()); usermods.add(new Smartnest());
#endif #endif
#ifdef USERMOD_AUDIOREACTIVE #ifdef USERMOD_AUDIOREACTIVE
usermods.add(new AudioReactive()); usermods.add(new AudioReactive());
#endif #endif
@ -324,15 +324,15 @@ void registerUsermods()
#ifdef USERMOD_ANALOG_CLOCK #ifdef USERMOD_ANALOG_CLOCK
usermods.add(new AnalogClockUsermod()); usermods.add(new AnalogClockUsermod());
#endif #endif
#ifdef USERMOD_PING_PONG_CLOCK #ifdef USERMOD_PING_PONG_CLOCK
usermods.add(new PingPongClockUsermod()); usermods.add(new PingPongClockUsermod());
#endif #endif
#ifdef USERMOD_ADS1115 #ifdef USERMOD_ADS1115
usermods.add(new ADS1115Usermod()); usermods.add(new ADS1115Usermod());
#endif #endif
#ifdef USERMOD_BOBLIGHT #ifdef USERMOD_BOBLIGHT
usermods.add(new BobLightUsermod()); usermods.add(new BobLightUsermod());
#endif #endif
@ -340,7 +340,7 @@ void registerUsermods()
#ifdef SD_ADAPTER #ifdef SD_ADAPTER
usermods.add(new UsermodSdCard()); usermods.add(new UsermodSdCard());
#endif #endif
#ifdef USERMOD_PWM_OUTPUTS #ifdef USERMOD_PWM_OUTPUTS
usermods.add(new PwmOutputsUsermod()); usermods.add(new PwmOutputsUsermod());
#endif #endif

View File

@ -29,7 +29,7 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
} else { } else {
if (wrap && *val == maxv && out > 0) out = minv; if (wrap && *val == maxv && out > 0) out = minv;
else if (wrap && *val == minv && out < 0) out = maxv; else if (wrap && *val == minv && out < 0) out = maxv;
else { else {
out += *val; out += *val;
if (out > maxv) out = maxv; if (out > maxv) out = maxv;
if (out < minv) out = minv; if (out < minv) out = minv;
@ -335,7 +335,7 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
} }
// we have slider name (including default value) in the dest buffer // we have slider name (including default value) in the dest buffer
for (size_t i=0; i<strlen(dest); i++) if (dest[i]=='=') { dest[i]='\0'; break; } // truncate default value for (size_t i=0; i<strlen(dest); i++) if (dest[i]=='=') { dest[i]='\0'; break; } // truncate default value
} else { } else {
// defaults to just speed and intensity since there is no slider data // defaults to just speed and intensity since there is no slider data
switch (slider) { switch (slider) {
@ -397,7 +397,7 @@ typedef enum UM_SoundSimulations {
UMS_14_3 UMS_14_3
} um_soundSimulations_t; } um_soundSimulations_t;
um_data_t* simulateSound(uint8_t simulationId) um_data_t* simulateSound(uint8_t simulationId)
{ {
static uint8_t samplePeak; static uint8_t samplePeak;
static float FFT_MajorPeak; static float FFT_MajorPeak;
@ -426,7 +426,7 @@ um_data_t* simulateSound(uint8_t simulationId)
um_data->u_data = new void*[um_data->u_size]; um_data->u_data = new void*[um_data->u_size];
um_data->u_data[0] = &volumeSmth; um_data->u_data[0] = &volumeSmth;
um_data->u_data[1] = &volumeRaw; um_data->u_data[1] = &volumeRaw;
um_data->u_data[2] = fftResult; um_data->u_data[2] = fftResult;
um_data->u_data[3] = &samplePeak; um_data->u_data[3] = &samplePeak;
um_data->u_data[4] = &FFT_MajorPeak; um_data->u_data[4] = &FFT_MajorPeak;
um_data->u_data[5] = &my_magnitude; um_data->u_data[5] = &my_magnitude;

View File

@ -150,14 +150,14 @@ void WLED::loop()
} }
//LED settings have been saved, re-init busses //LED settings have been saved, re-init busses
//This code block causes severe FPS drop on ESP32 with the original "if (busConfigs[0] != nullptr)" conditional. Investigate! //This code block causes severe FPS drop on ESP32 with the original "if (busConfigs[0] != nullptr)" conditional. Investigate!
if (doInitBusses) { if (doInitBusses) {
doInitBusses = false; doInitBusses = false;
DEBUG_PRINTLN(F("Re-init busses.")); DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses) bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
busses.removeAll(); busses.removeAll();
uint32_t mem = 0; uint32_t mem = 0;
for (uint8_t i = 0; i < WLED_MAX_BUSSES; i++) { for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr) break; if (busConfigs[i] == nullptr) break;
mem += BusManager::memUsage(*busConfigs[i]); mem += BusManager::memUsage(*busConfigs[i]);
if (mem <= MAX_LED_MEMORY) { if (mem <= MAX_LED_MEMORY) {
@ -268,7 +268,10 @@ void WLED::setup()
#endif #endif
Serial.begin(115200); Serial.begin(115200);
Serial.setTimeout(50); #if !ARDUINO_USB_CDC_ON_BOOT
Serial.setTimeout(50); // this causes troubles on new MCUs that have a "virtual" USB Serial (HWCDC)
#else
#endif
#if defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && (defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || ARDUINO_USB_CDC_ON_BOOT) #if defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && (defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || ARDUINO_USB_CDC_ON_BOOT)
delay(2500); // allow CDC USB serial to initialise delay(2500); // allow CDC USB serial to initialise
#endif #endif
@ -361,7 +364,7 @@ void WLED::setup()
if (!fsinit) { if (!fsinit) {
DEBUGFS_PRINTLN(F("FS failed!")); DEBUGFS_PRINTLN(F("FS failed!"));
errorFlag = ERR_FS_BEGIN; errorFlag = ERR_FS_BEGIN;
} }
#ifdef WLED_ADD_EEPROM_SUPPORT #ifdef WLED_ADD_EEPROM_SUPPORT
else deEEP(); else deEEP();
#else #else
@ -566,7 +569,7 @@ bool WLED::initEthernet()
} else { } else {
DEBUG_PRINT(F("initE: Failing due to invalid eth_clk_mode (")); DEBUG_PRINT(F("initE: Failing due to invalid eth_clk_mode ("));
DEBUG_PRINT(es.eth_clk_mode); DEBUG_PRINT(es.eth_clk_mode);
DEBUG_PRINTLN(F(")")); DEBUG_PRINTLN(")");
return false; return false;
} }
@ -576,10 +579,10 @@ bool WLED::initEthernet()
} }
if (!ETH.begin( if (!ETH.begin(
(uint8_t) es.eth_address, (uint8_t) es.eth_address,
(int) es.eth_power, (int) es.eth_power,
(int) es.eth_mdc, (int) es.eth_mdc,
(int) es.eth_mdio, (int) es.eth_mdio,
(eth_phy_type_t) es.eth_type, (eth_phy_type_t) es.eth_type,
(eth_clock_mode_t) es.eth_clk_mode (eth_clock_mode_t) es.eth_clk_mode
)) { )) {

View File

@ -3,12 +3,12 @@
/* /*
Main sketch, global variable declarations Main sketch, global variable declarations
@title WLED project sketch @title WLED project sketch
@version 0.14.0-b0 @version 0.14.0-b1
@author Christian Schwinne @author Christian Schwinne
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2212180 #define VERSION 2301140
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -71,6 +71,7 @@
#include <user_interface.h> #include <user_interface.h>
} }
#else // ESP32 #else // ESP32
#include <HardwareSerial.h> // ensure we have the correct "Serial" on new MCUs (depends on ARDUINO_USB_MODE and ARDUINO_USB_CDC_ON_BOOT)
#include <WiFi.h> #include <WiFi.h>
#include <ETH.h> #include <ETH.h>
#include "esp_wifi.h" #include "esp_wifi.h"
@ -669,7 +670,7 @@ WLED_GLOBAL bool e131NewData _INIT(false);
// led fx library object // led fx library object
WLED_GLOBAL BusManager busses _INIT(BusManager()); WLED_GLOBAL BusManager busses _INIT(BusManager());
WLED_GLOBAL WS2812FX strip _INIT(WS2812FX()); WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after
WLED_GLOBAL bool doInitBusses _INIT(false); WLED_GLOBAL bool doInitBusses _INIT(false);
WLED_GLOBAL int8_t loadLedmap _INIT(-1); WLED_GLOBAL int8_t loadLedmap _INIT(-1);
WLED_GLOBAL uint16_t ledMaps _INIT(0); // bitfield representation of available ledmaps WLED_GLOBAL uint16_t ledMaps _INIT(0); // bitfield representation of available ledmaps

View File

@ -5,7 +5,7 @@
/* /*
* DEPRECATED, do not use for new settings * DEPRECATED, do not use for new settings
* Only used to restore config from pre-0.11 installations using the deEEP() methods * Only used to restore config from pre-0.11 installations using the deEEP() methods
* *
* Methods to handle saving and loading to non-volatile memory * Methods to handle saving and loading to non-volatile memory
* EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map
*/ */
@ -154,9 +154,9 @@ void loadSettingsFromEEPROM()
turnOnAtBoot = EEPROM.read(369); turnOnAtBoot = EEPROM.read(369);
//strip.isRgbw = EEPROM.read(372); //strip.isRgbw = EEPROM.read(372);
//374 - strip.paletteFade //374 - strip.paletteFade
apBehavior = EEPROM.read(376); apBehavior = EEPROM.read(376);
//377 = lastEEPROMversion //377 = lastEEPROMversion
if (lastEEPROMversion > 3) { if (lastEEPROMversion > 3) {
aOtaEnabled = EEPROM.read(390); aOtaEnabled = EEPROM.read(390);
@ -164,7 +164,7 @@ void loadSettingsFromEEPROM()
receiveNotificationEffects = EEPROM.read(392); receiveNotificationEffects = EEPROM.read(392);
} }
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
if (lastEEPROMversion > 4) { if (lastEEPROMversion > 4) {
#ifndef WLED_DISABLE_HUESYNC #ifndef WLED_DISABLE_HUESYNC
huePollingEnabled = EEPROM.read(2048); huePollingEnabled = EEPROM.read(2048);
@ -232,7 +232,7 @@ void loadSettingsFromEEPROM()
timerMacro[i] = EEPROM.read(2290 + i); timerMacro[i] = EEPROM.read(2290 + i);
if (timerMacro[i] > 0) timerMacro[i] += 16; //add 16 to work with macro --> preset mapping if (timerMacro[i] > 0) timerMacro[i] += 16; //add 16 to work with macro --> preset mapping
if (timerWeekday[i] == 0) timerWeekday[i] = 255; if (timerWeekday[i] == 0) timerWeekday[i] = 255;
if (timerMacro[i] == 0) timerWeekday[i] = timerWeekday[i] & 0b11111110; if (timerMacro[i] == 0) timerWeekday[i] = timerWeekday[i] & 0b11111110;
} }
} }
@ -319,8 +319,8 @@ void loadSettingsFromEEPROM()
if (lastEEPROMversion > 21) { if (lastEEPROMversion > 21) {
udpPort2 = EEPROM.read(378) + ((EEPROM.read(379) << 8) & 0xFF00); udpPort2 = EEPROM.read(378) + ((EEPROM.read(379) << 8) & 0xFF00);
} }
receiveDirect = !EEPROM.read(2200); receiveDirect = !EEPROM.read(2200);
notifyMacro = EEPROM.read(2201); notifyMacro = EEPROM.read(2201);
@ -349,7 +349,7 @@ void loadSettingsFromEEPROM()
DMXChannels = EEPROM.read(2530); DMXChannels = EEPROM.read(2530);
DMXGap = EEPROM.read(2531) + ((EEPROM.read(2532) << 8) & 0xFF00); DMXGap = EEPROM.read(2531) + ((EEPROM.read(2532) << 8) & 0xFF00);
DMXStart = EEPROM.read(2533) + ((EEPROM.read(2534) << 8) & 0xFF00); DMXStart = EEPROM.read(2533) + ((EEPROM.read(2534) << 8) & 0xFF00);
for (int i=0;i<15;i++) { for (int i=0;i<15;i++) {
DMXFixtureMap[i] = EEPROM.read(2535+i); DMXFixtureMap[i] = EEPROM.read(2535+i);
} //last used: 2549 } //last used: 2549
@ -372,7 +372,7 @@ void applyMacro(byte index) {
// De-EEPROM routine, upgrade from previous versions to v0.11 // De-EEPROM routine, upgrade from previous versions to v0.11
void deEEP() { void deEEP() {
if (WLED_FS.exists("/presets.json")) return; if (WLED_FS.exists("/presets.json")) return;
DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM")); DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM"));
DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP")); DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP"));
if (!requestJSONBufferLock(8)) return; if (!requestJSONBufferLock(8)) return;
@ -413,7 +413,7 @@ void deEEP() {
for (byte j = 0; j < numChannels; j++) colX.add(EEPROM.read(memloc + j)); for (byte j = 0; j < numChannels; j++) colX.add(EEPROM.read(memloc + j));
} }
segObj["fx"] = EEPROM.read(i+10); segObj["fx"] = EEPROM.read(i+10);
segObj["sx"] = EEPROM.read(i+11); segObj["sx"] = EEPROM.read(i+11);
segObj["ix"] = EEPROM.read(i+16); segObj["ix"] = EEPROM.read(i+16);

View File

@ -2,7 +2,7 @@
* Contains some trigonometric functions. * Contains some trigonometric functions.
* The ANSI C equivalents are likely faster, but using any sin/cos/tan function incurs a memory penalty of 460 bytes on ESP8266, likely for lookup tables. * The ANSI C equivalents are likely faster, but using any sin/cos/tan function incurs a memory penalty of 460 bytes on ESP8266, likely for lookup tables.
* This implementation has no extra static memory usage. * This implementation has no extra static memory usage.
* *
* Source of the cos_t() function: https://web.eecs.utk.edu/~azh/blog/cosine.html (cos_taylor_literal_6terms) * Source of the cos_t() function: https://web.eecs.utk.edu/~azh/blog/cosine.html (cos_taylor_literal_6terms)
*/ */

View File

@ -46,7 +46,7 @@ void sendJSON(){
if (i != used-1) Serial.write(','); if (i != used-1) Serial.write(',');
} }
Serial.println("]"); Serial.println("]");
} }
} }
// RGB LED data returned as bytes in TPM2 format. Faster, and slightly less easy to use on the other end. // RGB LED data returned as bytes in TPM2 format. Faster, and slightly less easy to use on the other end.
@ -64,13 +64,13 @@ void sendBytes(){
Serial.write(qadd8(W(c), B(c))); //B Serial.write(qadd8(W(c), B(c))); //B
} }
Serial.write(0x36); Serial.write('\n'); Serial.write(0x36); Serial.write('\n');
} }
} }
void handleSerial() void handleSerial()
{ {
if (pinManager.isPinAllocated(hardwareRX)) return; if (pinManager.isPinAllocated(hardwareRX)) return;
#ifdef WLED_ENABLE_ADALIGHT #ifdef WLED_ENABLE_ADALIGHT
static auto state = AdaState::Header_A; static auto state = AdaState::Header_A;
static uint16_t count = 0; static uint16_t count = 0;
@ -94,7 +94,7 @@ void handleSerial()
return; return;
} else if (next == 'v') { } else if (next == 'v') {
Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION); Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION);
} else if (next == 0xB0) {updateBaudRate( 115200); } else if (next == 0xB0) {updateBaudRate( 115200);
} else if (next == 0xB1) {updateBaudRate( 230400); } else if (next == 0xB1) {updateBaudRate( 230400);
} else if (next == 0xB2) {updateBaudRate( 460800); } else if (next == 0xB2) {updateBaudRate( 460800);
@ -103,11 +103,11 @@ void handleSerial()
} else if (next == 0xB5) {updateBaudRate( 921600); } else if (next == 0xB5) {updateBaudRate( 921600);
} else if (next == 0xB6) {updateBaudRate(1000000); } else if (next == 0xB6) {updateBaudRate(1000000);
} else if (next == 0xB7) {updateBaudRate(1500000); } else if (next == 0xB7) {updateBaudRate(1500000);
} else if (next == 'l') {sendJSON(); // Send LED data as JSON Array } else if (next == 'l') {sendJSON(); // Send LED data as JSON Array
} else if (next == 'L') {sendBytes(); // Send LED data as TPM2 Data Packet } else if (next == 'L') {sendBytes(); // Send LED data as TPM2 Data Packet
} else if (next == 'o') {continuousSendLED = false; // Disable Continuous Serial Streaming } else if (next == 'o') {continuousSendLED = false; // Disable Continuous Serial Streaming
} else if (next == 'O') {continuousSendLED = true; // Enable Continuous Serial Streaming } else if (next == 'O') {continuousSendLED = true; // Enable Continuous Serial Streaming
} else if (next == '{') { //JSON API } else if (next == '{') { //JSON API
@ -195,8 +195,8 @@ void handleSerial()
// All other received bytes will disable Continuous Serial Streaming // All other received bytes will disable Continuous Serial Streaming
if (continuousSendLED && next != 'O'){ if (continuousSendLED && next != 'O'){
continuousSendLED = false; continuousSendLED = false;
} }
Serial.read(); //discard the byte Serial.read(); //discard the byte
} }
#endif #endif

View File

@ -88,7 +88,7 @@ bool captivePortal(AsyncWebServerRequest *request)
String hostH; String hostH;
if (!request->hasHeader("Host")) return false; if (!request->hasHeader("Host")) return false;
hostH = request->getHeader("Host")->value(); hostH = request->getHeader("Host")->value();
if (!isIp(hostH) && hostH.indexOf("wled.me") < 0 && hostH.indexOf(cmDNS) < 0) { if (!isIp(hostH) && hostH.indexOf("wled.me") < 0 && hostH.indexOf(cmDNS) < 0) {
DEBUG_PRINTLN("Captive portal"); DEBUG_PRINTLN("Captive portal");
AsyncWebServerResponse *response = request->beginResponse(302); AsyncWebServerResponse *response = request->beginResponse(302);
@ -135,14 +135,14 @@ void initServer()
//request->send_P(200, "text/html", PAGE_liveview); //request->send_P(200, "text/html", PAGE_liveview);
}); });
#endif #endif
//settings page //settings page
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request); serveSettings(request);
}); });
// "/settings/settings.js&p=x" request also handled by serveSettings() // "/settings/settings.js&p=x" request also handled by serveSettings()
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return; if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); AsyncWebServerResponse *response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length);
@ -150,27 +150,27 @@ void initServer()
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
}); });
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
if(!handleFileRead(request, "/favicon.ico")) if(!handleFileRead(request, "/favicon.ico"))
{ {
request->send_P(200, "image/x-icon", favicon, 156); request->send_P(200, "image/x-icon", favicon, 156);
} }
}); });
server.on("/sliders", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/sliders", HTTP_GET, [](AsyncWebServerRequest *request){
serveIndex(request); serveIndex(request);
}); });
server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request); serveSettings(request);
}); });
server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 200,F("Rebooting now..."),F("Please wait ~10 seconds..."),129); serveMessage(request, 200,F("Rebooting now..."),F("Please wait ~10 seconds..."),129);
doReboot = true; doReboot = true;
}); });
server.on("/settings", HTTP_POST, [](AsyncWebServerRequest *request){ server.on("/settings", HTTP_POST, [](AsyncWebServerRequest *request){
serveSettings(request, true); serveSettings(request, true);
}); });
@ -214,7 +214,7 @@ void initServer()
} else { } else {
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
} }
} }
request->send(200, "application/json", F("{\"success\":true}")); request->send(200, "application/json", F("{\"success\":true}"));
}); });
server.addHandler(handler); server.addHandler(handler);
@ -222,15 +222,15 @@ void initServer()
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", (String)VERSION); request->send(200, "text/plain", (String)VERSION);
}); });
server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", (String)millis()); request->send(200, "text/plain", (String)millis());
}); });
server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", (String)ESP.getFreeHeap()); request->send(200, "text/plain", (String)ESP.getFreeHeap());
}); });
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return; if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_usermod, PAGE_usermod_length); AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_usermod, PAGE_usermod_length);
@ -239,12 +239,12 @@ void initServer()
request->send(response); request->send(response);
//request->send_P(200, "text/html", PAGE_usermod); //request->send_P(200, "text/html", PAGE_usermod);
}); });
//Deprecated, use of /json/state and presets recommended instead //Deprecated, use of /json/state and presets recommended instead
server.on("/url", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/url", HTTP_GET, [](AsyncWebServerRequest *request){
URL_response(request); URL_response(request);
}); });
server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254); serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254);
}); });
@ -271,14 +271,14 @@ void initServer()
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
}); });
server.on("/rangetouch.js", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/rangetouch.js", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncWebServerResponse *response = request->beginResponse_P(200, "application/javascript", rangetouchJs, rangetouchJs_length); AsyncWebServerResponse *response = request->beginResponse_P(200, "application/javascript", rangetouchJs, rangetouchJs_length);
response->addHeader(FPSTR(s_content_enc),"gzip"); response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
}); });
createEditHandler(correctPIN); createEditHandler(correctPIN);
#ifndef WLED_DISABLE_OTA #ifndef WLED_DISABLE_OTA
@ -289,7 +289,7 @@ void initServer()
} else } else
serveSettings(request); // checks for "upd" in URL and handles PIN serveSettings(request); // checks for "upd" in URL and handles PIN
}); });
server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){
if (!correctPIN) { if (!correctPIN) {
serveSettings(request, true); // handle PIN page POST request serveSettings(request, true); // handle PIN page POST request
@ -298,7 +298,7 @@ void initServer()
if (Update.hasError() || otaLock) { if (Update.hasError() || otaLock) {
serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254); serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254);
} else { } else {
serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131); serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131);
doReboot = true; doReboot = true;
} }
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
@ -348,7 +348,7 @@ void initServer()
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
server.addHandler(&ws); server.addHandler(&ws);
#endif #endif
//called when the url is not defined here, ajax-in; get-settings //called when the url is not defined here, ajax-in; get-settings
server.onNotFound([](AsyncWebServerRequest *request){ server.onNotFound([](AsyncWebServerRequest *request){
DEBUG_PRINTLN("Not-Found HTTP call:"); DEBUG_PRINTLN("Not-Found HTTP call:");
@ -363,7 +363,7 @@ void initServer()
request->send(response); request->send(response);
return; return;
} }
if(handleSet(request, request->url())) return; if(handleSet(request, request->url())) return;
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
if(espalexa.handleAlexaApiCall(request)) return; if(espalexa.handleAlexaApiCall(request)) return;
@ -471,7 +471,7 @@ void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& h
messageHead = headl; messageHead = headl;
messageSub = subl; messageSub = subl;
optionType = optionT; optionType = optionT;
request->send_P(code, "text/html", PAGE_msg, msgProcessor); request->send_P(code, "text/html", PAGE_msg, msgProcessor);
} }
@ -493,7 +493,7 @@ String dmxProcessor(const String& var)
mapJS += "0];"; mapJS += "0];";
} }
#endif #endif
return mapJS; return mapJS;
} }
#endif #endif
@ -526,7 +526,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
byte subPage = 0, originalSubPage = 0; byte subPage = 0, originalSubPage = 0;
const String& url = request->url(); const String& url = request->url();
if (url.indexOf("sett") >= 0) if (url.indexOf("sett") >= 0)
{ {
if (url.indexOf(".js") > 0) subPage = 254; if (url.indexOf(".js") > 0) subPage = 254;
else if (url.indexOf(".css") > 0) subPage = 253; else if (url.indexOf(".css") > 0) subPage = 253;
@ -589,7 +589,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
return; return;
} }
} }
AsyncWebServerResponse *response; AsyncWebServerResponse *response;
switch (subPage) switch (subPage)
{ {

View File

@ -92,7 +92,7 @@ void URL_response(AsyncWebServerRequest *request)
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
sprintf(s,"%02X", col[i]); sprintf(s,"%02X", col[i]);
oappend(s); oappend(s);
} }
oappend(SET_F("&C2=h")); oappend(SET_F("&C2=h"));
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
@ -115,7 +115,7 @@ void URL_response(AsyncWebServerRequest *request)
oappend(SET_F("<html><body><a href=\"")); oappend(SET_F("<html><body><a href=\""));
oappend(s2buf); oappend(s2buf);
oappend(SET_F("\" target=\"_blank\">")); oappend(SET_F("\" target=\"_blank\">"));
oappend(s2buf); oappend(s2buf);
oappend(SET_F("</a></body></html>")); oappend(SET_F("</a></body></html>"));
if (request != nullptr) request->send(200, "text/html", obuf); if (request != nullptr) request->send(200, "text/html", obuf);
@ -368,6 +368,7 @@ void getSettingsJS(byte subPage, char* dest)
// set limits // set limits
oappend(SET_F("bLimits(")); oappend(SET_F("bLimits("));
oappend(itoa(WLED_MAX_BUSSES,nS,10)); oappend(","); oappend(itoa(WLED_MAX_BUSSES,nS,10)); oappend(",");
oappend(itoa(WLED_MIN_VIRTUAL_BUSSES,nS,10)); oappend(",");
oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(","); oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(",");
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(","); oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(",");
oappend(itoa(MAX_LEDS,nS,10)); oappend(itoa(MAX_LEDS,nS,10));
@ -566,7 +567,7 @@ void getSettingsJS(byte subPage, char* dest)
case HUE_ERROR_TIMEOUT : strcpy_P(hueErrorString,PSTR("Timeout")); break; case HUE_ERROR_TIMEOUT : strcpy_P(hueErrorString,PSTR("Timeout")); break;
default: sprintf_P(hueErrorString,PSTR("Bridge Error %i"),hueError); default: sprintf_P(hueErrorString,PSTR("Bridge Error %i"),hueError);
} }
sappends('m',SET_F("(\"sip\")[0]"),hueErrorString); sappends('m',SET_F("(\"sip\")[0]"),hueErrorString);
#else #else
oappend(SET_F("toggle('Hue');")); // hide Hue Sync settings oappend(SET_F("toggle('Hue');")); // hide Hue Sync settings
@ -658,17 +659,17 @@ void getSettingsJS(byte subPage, char* dest)
oappend(serverDescription); oappend(serverDescription);
oappend(SET_F("\";")); oappend(SET_F("\";"));
} }
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled #ifdef WLED_ENABLE_DMX // include only if DMX is enabled
if (subPage == 7) if (subPage == 7)
{ {
sappend('v',SET_F("PU"),e131ProxyUniverse); sappend('v',SET_F("PU"),e131ProxyUniverse);
sappend('v',SET_F("CN"),DMXChannels); sappend('v',SET_F("CN"),DMXChannels);
sappend('v',SET_F("CG"),DMXGap); sappend('v',SET_F("CG"),DMXGap);
sappend('v',SET_F("CS"),DMXStart); sappend('v',SET_F("CS"),DMXStart);
sappend('v',SET_F("SL"),DMXStartLED); sappend('v',SET_F("SL"),DMXStartLED);
sappend('i',SET_F("CH1"),DMXFixtureMap[0]); sappend('i',SET_F("CH1"),DMXFixtureMap[0]);
sappend('i',SET_F("CH2"),DMXFixtureMap[1]); sappend('i',SET_F("CH2"),DMXFixtureMap[1]);
sappend('i',SET_F("CH3"),DMXFixtureMap[2]); sappend('i',SET_F("CH3"),DMXFixtureMap[2]);
@ -726,28 +727,37 @@ void getSettingsJS(byte subPage, char* dest)
{ {
sappend('v',SET_F("SOMP"),strip.isMatrix); sappend('v',SET_F("SOMP"),strip.isMatrix);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
oappend(SET_F("maxPanels=")); oappendi(WLED_MAX_PANELS); oappend(SET_F(";"));
oappend(SET_F("resetPanels();")); oappend(SET_F("resetPanels();"));
if (strip.isMatrix) { if (strip.isMatrix) {
sappend('v',SET_F("PH"),strip.panelH); if(strip.panels>0){
sappend('v',SET_F("PW"),strip.panelW); sappend('v',SET_F("PW"),strip.panel[0].width); //Set generator Width and Height to first panel size for convenience
sappend('v',SET_F("MPH"),strip.hPanels); sappend('v',SET_F("PH"),strip.panel[0].height);
sappend('v',SET_F("MPV"),strip.vPanels); }
sappend('v',SET_F("MPC"),strip.panels);
sappend('v',SET_F("PB"),strip.matrix.bottomStart); sappend('v',SET_F("PB"),strip.matrix.bottomStart);
sappend('v',SET_F("PR"),strip.matrix.rightStart); sappend('v',SET_F("PR"),strip.matrix.rightStart);
sappend('v',SET_F("PV"),strip.matrix.vertical); sappend('v',SET_F("PV"),strip.matrix.vertical);
sappend('c',SET_F("PS"),strip.matrix.serpentine); sappend('c',SET_F("PS"),strip.matrix.serpentine);
// panels // panels
for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) { for (uint8_t i=0; i<strip.panels; i++) {
char n[5]; char n[5];
oappend(SET_F("addPanel(")); oappend(SET_F("addPanel("));
oappend(itoa(i,n,10)); oappend(itoa(i,n,10));
oappend(SET_F(");")); oappend(SET_F(");"));
char pO[8]; sprintf_P(pO, PSTR("P%d"), i); char pO[8] = { '\0' };
uint8_t l = strlen(pO); pO[l+1] = 0; snprintf_P(pO, 7, PSTR("P%d"), i); // MAX_PANELS is 64 so pO will always only be 4 characters or less
pO[7] = '\0';
uint8_t l = strlen(pO);
// create P0B, P1B, ..., P63B, etc for other PxxX
pO[l] = 'B'; sappend('v',pO,strip.panel[i].bottomStart); pO[l] = 'B'; sappend('v',pO,strip.panel[i].bottomStart);
pO[l] = 'R'; sappend('v',pO,strip.panel[i].rightStart); pO[l] = 'R'; sappend('v',pO,strip.panel[i].rightStart);
pO[l] = 'V'; sappend('v',pO,strip.panel[i].vertical); pO[l] = 'V'; sappend('v',pO,strip.panel[i].vertical);
pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine); pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine);
pO[l] = 'X'; sappend('v',pO,strip.panel[i].xOffset);
pO[l] = 'Y'; sappend('v',pO,strip.panel[i].yOffset);
pO[l] = 'W'; sappend('v',pO,strip.panel[i].width);
pO[l] = 'H'; sappend('v',pO,strip.panel[i].height);
} }
} }
#else #else