diff --git a/CHANGELOG.md b/CHANGELOG.md
index baf49362..c99fe417 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
## 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
- Version bump to v0.13.3 "Toki"
diff --git a/package-lock.json b/package-lock.json
index ebdf5772..59c4b368 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.14.0-b0",
+ "version": "0.14.0-b1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 1e31f689..d9468756 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.14.0-b0",
+ "version": "0.14.0-b1",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
diff --git a/platformio.ini b/platformio.ini
index 77b7c38c..3b8489d8 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -214,6 +214,7 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_USE_WDT=0
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
+ ; -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
@@ -230,6 +231,9 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32S2
-D CONFIG_ASYNC_TCP_USE_WDT=0
-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 =
${env.lib_deps}
@@ -243,6 +247,9 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32C3
-D CONFIG_ASYNC_TCP_USE_WDT=0
-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 =
${env.lib_deps}
@@ -258,6 +265,8 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32S3
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
+ ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
+ ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT, ARDUINO_USB_MSC_ON_BOOT, ARDUINO_USB_DFU_ON_BOOT
lib_deps =
${env.lib_deps}
@@ -276,7 +285,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
-build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-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}
monitor_filters = esp8266_exception_decoder
@@ -286,7 +295,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
-build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
+build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 -D WLED_DISABLE_BLYNK
lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full]
@@ -295,7 +304,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
-build_flags = ${common.build_flags_esp8266} -D WLED_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}
[env:esp07]
@@ -341,7 +350,7 @@ board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
-build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
+build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
@@ -351,7 +360,7 @@ board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
-build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
+build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder
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}
[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
board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
+ ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual USB
upload_speed = 460800
build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps}
diff --git a/usermods/Analog_Clock/Analog_Clock.h b/usermods/Analog_Clock/Analog_Clock.h
index b8f44f78..596f0acb 100644
--- a/usermods/Analog_Clock/Analog_Clock.h
+++ b/usermods/Analog_Clock/Analog_Clock.h
@@ -56,7 +56,7 @@ private:
// runtime
bool initDone = false;
- uint32_t lastOverlayDraw = 0;
+ uint32_t lastOverlayDraw = 0;
void validateAndUpdate() {
mainSegment.validateAndUpdate();
@@ -110,9 +110,9 @@ private:
static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
return RGBW32(
- qadd8(R(c1), R(c2)),
- qadd8(G(c1), G(c2)),
- qadd8(B(c1), B(c2)),
+ qadd8(R(c1), R(c2)),
+ qadd8(G(c1), G(c2)),
+ qadd8(B(c1), B(c2)),
qadd8(W(c1), W(c2))
);
}
@@ -166,7 +166,7 @@ public:
double secondP = second(localTime) / 60.0;
double minuteP = minute(localTime) / 60.0;
double hourP = (hour(localTime) % 12) / 12.0 + minuteP / 12.0;
-
+
if (hourMarksEnabled) {
for (int Led = 0; Led <= 55; Led = Led + 5)
{
@@ -174,7 +174,7 @@ public:
setPixelColor(hourmarkled, hourMarkColor);
}
}
-
+
if (secondsEnabled) {
int16_t secondLed = adjustToSegment(secondP, secondsSegment);
@@ -203,45 +203,45 @@ public:
void addToConfig(JsonObject& root) override {
validateAndUpdate();
- JsonObject top = root.createNestedObject("Analog Clock");
- top["Overlay Enabled"] = enabled;
- top["First LED (Main Ring)"] = mainSegment.firstLed;
- top["Last LED (Main Ring)"] = mainSegment.lastLed;
- top["Center/12h LED (Main Ring)"] = mainSegment.centerLed;
- top["Hour Marks Enabled"] = hourMarksEnabled;
- top["Hour Mark Color (RRGGBB)"] = colorToHexString(hourMarkColor);
- top["Hour Color (RRGGBB)"] = colorToHexString(hourColor);
- top["Minute Color (RRGGBB)"] = colorToHexString(minuteColor);
- top["Show Seconds"] = secondsEnabled;
- top["First LED (Seconds Ring)"] = secondsSegment.firstLed;
- top["Last LED (Seconds Ring)"] = secondsSegment.lastLed;
- top["Center/12h LED (Seconds Ring)"] = secondsSegment.centerLed;
- top["Second Color (RRGGBB)"] = colorToHexString(secondColor);
- top["Seconds Effect (0-1)"] = secondsEffect;
- top["Blend Colors"] = blendColors;
+ JsonObject top = root.createNestedObject(F("Analog Clock"));
+ top[F("Overlay Enabled")] = enabled;
+ top[F("First LED (Main Ring)")] = mainSegment.firstLed;
+ top[F("Last LED (Main Ring)")] = mainSegment.lastLed;
+ top[F("Center/12h LED (Main Ring)")] = mainSegment.centerLed;
+ top[F("Hour Marks Enabled")] = hourMarksEnabled;
+ top[F("Hour Mark Color (RRGGBB)")] = colorToHexString(hourMarkColor);
+ top[F("Hour Color (RRGGBB)")] = colorToHexString(hourColor);
+ top[F("Minute Color (RRGGBB)")] = colorToHexString(minuteColor);
+ top[F("Show Seconds")] = secondsEnabled;
+ top[F("First LED (Seconds Ring)")] = secondsSegment.firstLed;
+ top[F("Last LED (Seconds Ring)")] = secondsSegment.lastLed;
+ top[F("Center/12h LED (Seconds Ring)")] = secondsSegment.centerLed;
+ top[F("Second Color (RRGGBB)")] = colorToHexString(secondColor);
+ top[F("Seconds Effect (0-1)")] = secondsEffect;
+ top[F("Blend Colors")] = blendColors;
}
bool readFromConfig(JsonObject& root) override {
- JsonObject top = root["Analog Clock"];
+ JsonObject top = root[F("Analog Clock")];
bool configComplete = !top.isNull();
String color;
- configComplete &= getJsonValue(top["Overlay Enabled"], enabled, false);
- configComplete &= getJsonValue(top["First LED (Main Ring)"], mainSegment.firstLed, 0);
- configComplete &= getJsonValue(top["Last LED (Main Ring)"], mainSegment.lastLed, 59);
- configComplete &= getJsonValue(top["Center/12h LED (Main Ring)"], mainSegment.centerLed, 0);
- configComplete &= getJsonValue(top["Hour marks Enabled"], hourMarksEnabled, false);
- configComplete &= getJsonValue(top["Hour mark Color (RRGGBB)"], color, "FF0000") && hexStringToColor(color, hourMarkColor, 0x0000FF);
- configComplete &= getJsonValue(top["Hour Color (RRGGBB)"], color, "0000FF") && hexStringToColor(color, hourColor, 0x0000FF);
- configComplete &= getJsonValue(top["Minute Color (RRGGBB)"], color, "00FF00") && hexStringToColor(color, minuteColor, 0x00FF00);
- configComplete &= getJsonValue(top["Show Seconds"], secondsEnabled, true);
- configComplete &= getJsonValue(top["First LED (Seconds Ring)"], secondsSegment.firstLed, 0);
- configComplete &= getJsonValue(top["Last LED (Seconds Ring)"], secondsSegment.lastLed, 59);
- configComplete &= getJsonValue(top["Center/12h LED (Seconds Ring)"], secondsSegment.centerLed, 0);
- configComplete &= getJsonValue(top["Second Color (RRGGBB)"], color, "FF0000") && hexStringToColor(color, secondColor, 0xFF0000);
- configComplete &= getJsonValue(top["Seconds Effect (0-1)"], secondsEffect, 0);
- configComplete &= getJsonValue(top["Blend Colors"], blendColors, true);
+ configComplete &= getJsonValue(top[F("Overlay Enabled")], enabled, false);
+ configComplete &= getJsonValue(top[F("First LED (Main Ring)")], mainSegment.firstLed, 0);
+ configComplete &= getJsonValue(top[F("Last LED (Main Ring)")], mainSegment.lastLed, 59);
+ configComplete &= getJsonValue(top[F("Center/12h LED (Main Ring)")], mainSegment.centerLed, 0);
+ configComplete &= getJsonValue(top[F("Hour Marks Enabled")], hourMarksEnabled, false);
+ configComplete &= getJsonValue(top[F("Hour Mark Color (RRGGBB)")], color, F("161616")) && hexStringToColor(color, hourMarkColor, 0x161616);
+ configComplete &= getJsonValue(top[F("Hour Color (RRGGBB)")], color, F("0000FF")) && hexStringToColor(color, hourColor, 0x0000FF);
+ configComplete &= getJsonValue(top[F("Minute Color (RRGGBB)")], color, F("00FF00")) && hexStringToColor(color, minuteColor, 0x00FF00);
+ configComplete &= getJsonValue(top[F("Show Seconds")], secondsEnabled, true);
+ configComplete &= getJsonValue(top[F("First LED (Seconds Ring)")], secondsSegment.firstLed, 0);
+ configComplete &= getJsonValue(top[F("Last LED (Seconds Ring)")], secondsSegment.lastLed, 59);
+ configComplete &= getJsonValue(top[F("Center/12h LED (Seconds Ring)")], secondsSegment.centerLed, 0);
+ configComplete &= getJsonValue(top[F("Second Color (RRGGBB)")], color, F("FF0000")) && hexStringToColor(color, secondColor, 0xFF0000);
+ configComplete &= getJsonValue(top[F("Seconds Effect (0-1)")], secondsEffect, 0);
+ configComplete &= getJsonValue(top[F("Blend Colors")], blendColors, true);
if (initDone) {
validateAndUpdate();
@@ -253,4 +253,4 @@ public:
uint16_t getId() override {
return USERMOD_ID_ANALOG_CLOCK;
}
-};
\ No newline at end of file
+};
diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h
index 1ef2c959..151cf1d4 100644
--- a/usermods/Animated_Staircase/Animated_Staircase.h
+++ b/usermods/Animated_Staircase/Animated_Staircase.h
@@ -92,12 +92,14 @@ class Animated_Staircase : public Usermod {
static const char _bottomEchoCm[];
void publishMqtt(bool bottom, const char* state) {
+#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)bottom);
mqtt->publish(subuf, 0, false, state);
}
+#endif
}
void updateSegments() {
@@ -345,6 +347,7 @@ class Animated_Staircase : public Usermod {
uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; }
+#ifndef WLED_DISABLE_MQTT
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
@@ -382,6 +385,7 @@ class Animated_Staircase : public Usermod {
mqtt->subscribe(subuf, 0);
}
}
+#endif
void addToJsonState(JsonObject& root) {
JsonObject staircase = root[FPSTR(_name)];
diff --git a/usermods/BH1750_v2/usermod_bh1750.h b/usermods/BH1750_v2/usermod_bh1750.h
index a69e2751..b65332bc 100644
--- a/usermods/BH1750_v2/usermod_bh1750.h
+++ b/usermods/BH1750_v2/usermod_bh1750.h
@@ -1,6 +1,10 @@
// force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_BH1750 ****
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "wled.h"
@@ -156,6 +160,7 @@ public:
{
lastLux = lux;
lastSend = millis();
+#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED)
{
if (!mqttInitialized)
@@ -170,6 +175,7 @@ public:
{
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
}
+#endif
}
}
diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h
index c27adfc8..e643e62b 100644
--- a/usermods/BME280_v2/usermod_bme280.h
+++ b/usermods/BME280_v2/usermod_bme280.h
@@ -1,6 +1,10 @@
// force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_BME280 version 2.0 ****
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "wled.h"
diff --git a/usermods/battery_status_basic/assets/battery_connection_schematic_01.png b/usermods/Battery/assets/battery_connection_schematic_01.png
similarity index 100%
rename from usermods/battery_status_basic/assets/battery_connection_schematic_01.png
rename to usermods/Battery/assets/battery_connection_schematic_01.png
diff --git a/usermods/battery_status_basic/assets/battery_connection_schematic_02.png b/usermods/Battery/assets/battery_connection_schematic_02.png
similarity index 100%
rename from usermods/battery_status_basic/assets/battery_connection_schematic_02.png
rename to usermods/Battery/assets/battery_connection_schematic_02.png
diff --git a/usermods/Battery/assets/battery_info_screen.png b/usermods/Battery/assets/battery_info_screen.png
new file mode 100644
index 00000000..5aa60a03
Binary files /dev/null and b/usermods/Battery/assets/battery_info_screen.png differ
diff --git a/usermods/Battery/assets/battery_usermod_logo.png b/usermods/Battery/assets/battery_usermod_logo.png
new file mode 100644
index 00000000..b1134eb3
Binary files /dev/null and b/usermods/Battery/assets/battery_usermod_logo.png differ
diff --git a/usermods/Battery/battery_defaults.h b/usermods/Battery/battery_defaults.h
new file mode 100644
index 00000000..c682cb45
--- /dev/null
+++ b/usermods/Battery/battery_defaults.h
@@ -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
\ No newline at end of file
diff --git a/usermods/Battery/readme.md b/usermods/Battery/readme.md
new file mode 100644
index 00000000..d55573ab
--- /dev/null
+++ b/usermods/Battery/readme.md
@@ -0,0 +1,112 @@
+
+
+
+
+# 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)
+
+
+
+
+
+## ⚙️ 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
+
+
+
+
+
+### 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
diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h
new file mode 100644
index 00000000..2dc85424
--- /dev/null
+++ b/usermods/Battery/usermod_v2_Battery.h
@@ -0,0 +1,773 @@
+#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();
+
+#ifndef WLED_DISABLE_MQTT
+ // 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());
+ }
+#endif
+
+ }
+
+ /*
+ * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
+ * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
+ * Below it is shown how this could be used for e.g. a light sensor
+ */
+ void addToJsonInfo(JsonObject& root)
+ {
+ 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";
diff --git a/usermods/Cronixie/usermod_cronixie.h b/usermods/Cronixie/usermod_cronixie.h
index 5702d8fa..534fd3a7 100644
--- a/usermods/Cronixie/usermod_cronixie.h
+++ b/usermods/Cronixie/usermod_cronixie.h
@@ -271,6 +271,7 @@ class UsermodCronixie : public Usermod {
{
if (root["nx"].is()) {
strncpy(cronixieDisplay, root["nx"], 6);
+ setCronixie();
}
}
diff --git a/usermods/DHT/usermod_dht.h b/usermods/DHT/usermod_dht.h
index 6253b85f..b6142f43 100644
--- a/usermods/DHT/usermod_dht.h
+++ b/usermods/DHT/usermod_dht.h
@@ -1,6 +1,10 @@
#pragma once
#include "wled.h"
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#include
diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
index 0a2662aa..1ca16050 100644
--- a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
+++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#include "wled.h"
#include
#include // from https://github.com/olikraus/u8g2/
diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
index 88d49790..d5fd4a0c 100644
--- a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
+++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#include "wled.h"
#include
#include // from https://github.com/olikraus/u8g2/
diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
index f716e48e..af81170a 100644
--- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
+++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
@@ -136,7 +136,7 @@ private:
}
} else {
if (m_offPreset) {
- if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(m_offPreset, NotifyUpdateMode);
+ applyPreset(m_offPreset, NotifyUpdateMode);
return;
} else if (prevPlaylist) {
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode);
@@ -159,6 +159,7 @@ private:
void publishMqtt(const char* state)
{
+ #ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) {
char subuf[64];
@@ -166,11 +167,13 @@ private:
strcat_P(subuf, PSTR("/motion"));
mqtt->publish(subuf, 0, false, state);
}
+ #endif
}
// Create an MQTT Binary Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void publishHomeAssistantAutodiscovery()
{
+ #ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED) {
StaticJsonDocument<600> doc;
char uid[24], json_str[1024], buf[128];
@@ -200,6 +203,7 @@ private:
mqtt->publish(buf, 0, true, json_str, payload_size); // do we really need to retain?
}
+ #endif
}
/**
diff --git a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h
index 9c3be7cc..60861e4c 100644
--- a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h
+++ b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h
@@ -109,6 +109,7 @@ public:
{
lastLDRValue = currentLDRValue;
+#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED)
{
char subuf[45];
@@ -121,6 +122,7 @@ public:
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
}
}
+#endif
}
uint16_t getLastLDRValue()
diff --git a/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h b/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h
index 71c22da1..4a42a7d5 100644
--- a/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h
+++ b/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
// this is remixed from usermod_v2_SensorsToMqtt.h (sensors_to_mqtt usermod)
diff --git a/usermods/Temperature/readme.md b/usermods/Temperature/readme.md
index 1515d000..c917461a 100644
--- a/usermods/Temperature/readme.md
+++ b/usermods/Temperature/readme.md
@@ -14,7 +14,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
### Define Your Options
* `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.
diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h
index a666639f..8950f928 100644
--- a/usermods/Temperature/usermod_temperature.h
+++ b/usermods/Temperature/usermod_temperature.h
@@ -134,6 +134,7 @@ class UsermodTemperature : public Usermod {
return false;
}
+#ifndef WLED_DISABLE_MQTT
void publishHomeAssistantAutodiscovery() {
if (!WLED_MQTT_CONNECTED) return;
@@ -155,6 +156,7 @@ class UsermodTemperature : public Usermod {
mqtt->publish(buf, 0, true, json_str, payload_size);
HApublished = true;
}
+#endif
public:
@@ -212,6 +214,7 @@ class UsermodTemperature : public Usermod {
}
errorCount = 0;
+#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED) {
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
@@ -227,6 +230,7 @@ class UsermodTemperature : public Usermod {
// publish something else to indicate status?
}
}
+#endif
}
}
@@ -236,6 +240,7 @@ class UsermodTemperature : public Usermod {
*/
//void connected() {}
+#ifndef WLED_DISABLE_MQTT
/**
* subscribe to MQTT topic if needed
*/
@@ -246,6 +251,7 @@ class UsermodTemperature : public Usermod {
publishHomeAssistantAutodiscovery();
}
}
+#endif
/*
* API calls te enable data exchange between WLED modules
diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp
index c7eb8ee0..78cc32a8 100644
--- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp
+++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp
@@ -101,6 +101,7 @@ void userLoop() {
if (temptimer - lastMeasure > 60000)
{
lastMeasure = temptimer;
+#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (mqtt != nullptr)
{
@@ -116,6 +117,7 @@ void userLoop() {
t += "/temperature";
mqtt->publish(t.c_str(), 0, true, String(board_temperature).c_str());
}
+ #endif
}
// Check if we time interval for redrawing passes.
diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp
index 05d4e77a..c9d9a527 100644
--- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp
+++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp
@@ -103,6 +103,7 @@ void userLoop() {
{
lastMeasure = tempTimer;
+#ifndef WLED_DISABLE_MQTT
// Check if MQTT Connected, otherwise it will crash the 8266
if (mqtt != nullptr)
{
@@ -122,6 +123,7 @@ void userLoop() {
h += "/humidity";
mqtt->publish(h.c_str(), 0, true, String(board_humidity).c_str());
}
+ #endif
}
// Check if we time interval for redrawing passes.
diff --git a/usermods/battery_status_basic/assets/battery_info_screen.png b/usermods/battery_status_basic/assets/battery_info_screen.png
deleted file mode 100644
index 50eb5346..00000000
Binary files a/usermods/battery_status_basic/assets/battery_info_screen.png and /dev/null differ
diff --git a/usermods/battery_status_basic/readme.md b/usermods/battery_status_basic/readme.md
deleted file mode 100644
index bada8d5f..00000000
--- a/usermods/battery_status_basic/readme.md
+++ /dev/null
@@ -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)
-
-
-
-### 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
-
diff --git a/usermods/battery_status_basic/usermod_v2_battery_status_basic.h b/usermods/battery_status_basic/usermod_v2_battery_status_basic.h
deleted file mode 100644
index cb3c0867..00000000
--- a/usermods/battery_status_basic/usermod_v2_battery_status_basic.h
+++ /dev/null
@@ -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 = voltagemaxBatteryVoltage?-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";
\ No newline at end of file
diff --git a/usermods/boblight/boblight.h b/usermods/boblight/boblight.h
index 263d8743..a1e25775 100644
--- a/usermods/boblight/boblight.h
+++ b/usermods/boblight/boblight.h
@@ -219,6 +219,7 @@ class BobLightUsermod : public Usermod {
void enable(bool en) { enabled = en; }
+#ifndef WLED_DISABLE_MQTT
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
@@ -249,6 +250,7 @@ class BobLightUsermod : public Usermod {
// mqtt->subscribe(subuf, 0);
//}
}
+#endif
void addToJsonInfo(JsonObject& root)
{
diff --git a/usermods/mpu6050_imu/usermod_mpu6050_imu.h b/usermods/mpu6050_imu/usermod_mpu6050_imu.h
index 4ce51c43..283c986e 100644
--- a/usermods/mpu6050_imu/usermod_mpu6050_imu.h
+++ b/usermods/mpu6050_imu/usermod_mpu6050_imu.h
@@ -138,7 +138,7 @@ class MPU6050Driver : public Usermod {
// (if it's going to break, usually the code will be 1)
DEBUG_PRINT(F("DMP Initialization failed (code "));
DEBUG_PRINT(devStatus);
- DEBUG_PRINTLN(F(")"));
+ DEBUG_PRINTLN(")");
}
}
diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h
index 7381a00d..b8bd4f6e 100644
--- a/usermods/multi_relay/usermod_multi_relay.h
+++ b/usermods/multi_relay/usermod_multi_relay.h
@@ -66,12 +66,14 @@ class MultiRelay : public Usermod {
static const char _HAautodiscovery[];
void publishMqtt(int relay) {
+#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay);
mqtt->publish(subuf, 0, false, _relay[relay].state ? "on" : "off");
}
+#endif
}
/**
@@ -232,6 +234,7 @@ class MultiRelay : public Usermod {
//Functions called by WLED
+#ifndef WLED_DISABLE_MQTT
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
@@ -313,6 +316,7 @@ class MultiRelay : public Usermod {
mqtt->publish(buf, 0, true, json_str, payload_size);
}
}
+#endif
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
diff --git a/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h b/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
index dd7aedc1..972e2c86 100644
--- a/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
+++ b/usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "wled.h"
diff --git a/usermods/seven_segment_display/usermod_v2_seven_segment_display.h b/usermods/seven_segment_display/usermod_v2_seven_segment_display.h
index 5c0022e0..e5b726e5 100644
--- a/usermods/seven_segment_display/usermod_v2_seven_segment_display.h
+++ b/usermods/seven_segment_display/usermod_v2_seven_segment_display.h
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "wled.h"
diff --git a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
index b1a271a6..27977405 100644
--- a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
+++ b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "wled.h"
diff --git a/usermods/sht/usermod_sht.h b/usermods/sht/usermod_sht.h
index 42b22a79..4637d7a3 100644
--- a/usermods/sht/usermod_sht.h
+++ b/usermods/sht/usermod_sht.h
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "SHT85.h"
@@ -26,9 +30,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
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
- float shtCurrentTempC = 0; // Last read temperature in Celsius
- float shtCurrentTempF = 0; // Last read temperature in Fahrenheit
- float shtCurrentHumidity = 0; // Last read humidity in RH%
+ float shtCurrentTempC = 0.0f; // Last read temperature in Celsius
+ float shtCurrentHumidity = 0.0f; // Last read humidity in RH%
void initShtTempHumiditySensor();
@@ -56,18 +59,22 @@ class ShtUsermod : public Usermod
bool readFromConfig(JsonObject &root);
void addToJsonInfo(JsonObject& root);
- float getTemperatureC();
- float getTemperatureF();
- float getHumidity();
+ bool isEnabled() { return enabled; }
+
+ 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; }
};
// Strings to reduce flash memory usage (used more than twice)
-const char ShtUsermod::_name[] PROGMEM = "SHT-Sensor";
-const char ShtUsermod::_enabled[] PROGMEM = "Enabled";
-const char ShtUsermod::_shtType[] PROGMEM = "SHT-Type";
-const char ShtUsermod::_unitOfTemp[] PROGMEM = "Unit";
+const char ShtUsermod::_name[] PROGMEM = "SHT-Sensor";
+const char ShtUsermod::_enabled[] PROGMEM = "Enabled";
+const char ShtUsermod::_shtType[] PROGMEM = "SHT-Type";
+const char ShtUsermod::_unitOfTemp[] PROGMEM = "Unit";
const char ShtUsermod::_haMqttDiscovery[] PROGMEM = "Add-To-HA-MQTT-Discovery";
/**
@@ -90,7 +97,6 @@ void ShtUsermod::initShtTempHumiditySensor()
shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl);
if (shtTempHumidSensor->readStatus() == 0xFFFF) {
DEBUG_PRINTF("[%s] SHT init failed!\n", _name);
- cleanupShtTempHumiditySensor();
cleanup();
return;
}
@@ -107,12 +113,8 @@ void ShtUsermod::initShtTempHumiditySensor()
*/
void ShtUsermod::cleanupShtTempHumiditySensor()
{
- if (isShtReady()) {
- shtTempHumidSensor->reset();
- }
-
+ if (isShtReady()) shtTempHumidSensor->reset();
delete shtTempHumidSensor;
-
shtInitDone = false;
}
@@ -126,9 +128,7 @@ void ShtUsermod::cleanupShtTempHumiditySensor()
*/
void ShtUsermod::cleanup()
{
- if (isShtReady()) {
- cleanupShtTempHumiditySensor();
- }
+ cleanupShtTempHumiditySensor();
if (pinAllocDone) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
@@ -162,9 +162,9 @@ void ShtUsermod::publishTemperatureAndHumidityViaMqtt() {
char buf[128];
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);
- mqtt->publish(buf, 0, false, String(shtCurrentHumidity).c_str());
+ mqtt->publish(buf, 0, false, String(getHumidity()).c_str());
}
/**
@@ -191,7 +191,7 @@ void ShtUsermod::publishHomeAssistantAutodiscovery() {
json[F("stat_cla")] = F("measurement");
snprintf_P(buf, 127, PSTR("%s-temperature"), escapedMac.c_str());
json[F("uniq_id")] = buf;
- json[F("unit_of_meas")] = F("°C");
+ json[F("unit_of_meas")] = unitOfTemp ? F("°F") : F("°C");
appendDeviceToMqttDiscoveryMessage(json);
payload_size = serializeJson(json, json_str);
snprintf_P(buf, 127, PSTR("homeassistant/sensor/%s/%s-temperature/config"), escapedMac.c_str(), escapedMac.c_str());
@@ -222,7 +222,7 @@ void ShtUsermod::publishHomeAssistantAutodiscovery() {
* @return void
*/
void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
- JsonObject device = root.createNestedObject("dev");
+ JsonObject device = root.createNestedObject(F("dev"));
device[F("ids")] = escapedMac.c_str();
device[F("name")] = serverDescription;
device[F("sw")] = versionString;
@@ -290,13 +290,11 @@ void ShtUsermod::loop()
if (shtTempHumidSensor->dataReady()) {
if (shtTempHumidSensor->readData(false)) {
shtCurrentTempC = shtTempHumidSensor->getTemperature();
- shtCurrentTempF = shtTempHumidSensor->getFahrenheit();
shtCurrentHumidity = shtTempHumidSensor->getHumidity();
publishTemperatureAndHumidityViaMqtt();
shtReadDataSuccess = true;
- }
- else {
+ } else {
shtReadDataSuccess = false;
}
@@ -387,6 +385,7 @@ bool ShtUsermod::readFromConfig(JsonObject &root)
bool oldEnabled = enabled;
byte oldShtType = shtType;
+ byte oldUnitOfTemp = unitOfTemp;
bool oldHaMqttDiscovery = haMqttDiscovery;
getJsonValue(top[FPSTR(_enabled)], enabled);
@@ -410,6 +409,11 @@ bool ShtUsermod::readFromConfig(JsonObject &root)
initShtTempHumiditySensor();
}
+ if (oldUnitOfTemp != unitOfTemp) {
+ publishTemperatureAndHumidityViaMqtt();
+ publishHomeAssistantAutodiscovery();
+ }
+
if (oldHaMqttDiscovery != haMqttDiscovery && haMqttDiscovery) {
publishHomeAssistantAutodiscovery();
}
@@ -448,45 +452,34 @@ void ShtUsermod::addToJsonInfo(JsonObject& root)
if (shtLastTimeUpdated == 0) {
jsonTemp.add(F(" Not read yet"));
jsonHumidity.add(F(" Not read yet"));
- }
- else {
+ } else {
jsonTemp.add(F(" Error"));
jsonHumidity.add(F(" Error"));
}
-
return;
}
- jsonHumidity.add(shtCurrentHumidity);
+ jsonHumidity.add(getHumidity());
jsonHumidity.add(F(" RH"));
- unitOfTemp ? jsonTemp.add(getTemperatureF()) : jsonTemp.add(getTemperatureC());
- unitOfTemp ? jsonTemp.add(F(" °F")) : jsonTemp.add(F(" °C"));
+ jsonTemp.add(getTemperature());
+ jsonTemp.add(unitOfTemp ? "°F" : "°C");
}
/**
- * Getter for last read temperature in Celsius.
+ * Getter for last read temperature for configured unit.
*
* @return float
*/
-float ShtUsermod::getTemperatureC() {
- return shtCurrentTempC;
+float ShtUsermod::getTemperature() {
+ 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() {
- return shtCurrentTempF;
-}
-
-/**
- * Getter for last read humidity in RH%.
- *
- * @return float
- */
-float ShtUsermod::getHumidity() {
- return shtCurrentHumidity;
+const char* ShtUsermod::getUnitString() {
+ return unitOfTemp ? "°F" : "°C";
}
\ No newline at end of file
diff --git a/usermods/smartnest/usermod_smartnest.h b/usermods/smartnest/usermod_smartnest.h
index 82673578..8d2b04ff 100644
--- a/usermods/smartnest/usermod_smartnest.h
+++ b/usermods/smartnest/usermod_smartnest.h
@@ -1,3 +1,7 @@
+#ifndef WLED_ENABLE_MQTT
+#error "This user mod requires MQTT to be enabled."
+#endif
+
#pragma once
#include "wled.h"
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 6a9ed447..cfd5d170 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -44,9 +44,9 @@ uint16_t triwave16(uint16_t in) {
}
/*
- * Generates a tristate square wave w/ attac & decay
+ * Generates a tristate square wave w/ attac & decay
* @param x input value 0-255
- * @param pulsewidth 0-127
+ * @param pulsewidth 0-127
* @param attdec attac & decay, max. pulsewidth / 2
* @returns signed waveform value
*/
@@ -62,7 +62,7 @@ int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
}
else if (x < pulsewidth - attdec) { //max
return a;
- }
+ }
else if (x < pulsewidth) { //dec to 0
return (int16_t) (pulsewidth - x) * a / attdec;
}
@@ -93,13 +93,13 @@ uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) {
cycleTime += FRAMETIME*2;
uint32_t it = strip.now / cycleTime;
uint32_t rem = strip.now % cycleTime;
-
+
bool on = false;
if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief
- || rem <= onTime) {
+ || rem <= onTime) {
on = true;
}
-
+
SEGENV.step = it; //save previous iteration
uint32_t color = on ? color1 : color2;
@@ -193,8 +193,8 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
{
uint16_t index = (rev && back)? SEGLEN -1 -i : i;
uint32_t col0 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux0) : SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0);
-
- if (i < ledIndex)
+
+ if (i < ledIndex)
{
SEGMENT.setPixelColor(index, back? col1 : col0);
} else
@@ -202,7 +202,7 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
SEGMENT.setPixelColor(index, back? col0 : col1);
if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem));
}
- }
+ }
return FRAMETIME;
}
@@ -283,7 +283,7 @@ static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade t
*/
uint16_t dynamic(boolean smooth=false) {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
-
+
if(SEGENV.call == 0) {
for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8();
}
@@ -297,7 +297,7 @@ uint16_t dynamic(boolean smooth=false) {
}
SEGENV.step = it;
}
-
+
if (smooth) {
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]),16); // TODO
@@ -306,7 +306,7 @@ uint16_t dynamic(boolean smooth=false) {
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]));
}
- }
+ }
return FRAMETIME;
}
@@ -340,7 +340,7 @@ uint16_t mode_breath(void) {
if (counter > 8192) counter = 8192 - (counter - 8192);
var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170
}
-
+
uint8_t lum = 30 + var;
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum));
@@ -378,7 +378,7 @@ uint16_t scan(bool dual)
uint16_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 9);
uint16_t ledIndex = (prog * ((SEGLEN *2) - size *2)) >> 16;
- SEGMENT.fill(SEGCOLOR(1));
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
int led_offset = ledIndex - (SEGLEN - size);
led_offset = abs(led_offset);
@@ -404,7 +404,7 @@ uint16_t scan(bool dual)
uint16_t mode_scan(void) {
return scan(false);
}
-static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots;!,!,!;!";
+static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots,,,,,Overlay;!,!,!;!";
/*
@@ -413,7 +413,7 @@ static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots;!,!,!;!";
uint16_t mode_dual_scan(void) {
return scan(true);
}
-static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots;!,!,!;!";
+static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots,,,,,Overlay;!,!,!;!";
/*
@@ -440,7 +440,7 @@ static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!";
uint16_t mode_rainbow_cycle(void) {
uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF;
counter = counter >> 8;
-
+
for (int i = 0; i < SEGLEN; i++) {
//intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16)
uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter;
@@ -460,7 +460,7 @@ uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) {
uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
uint32_t it = strip.now / cycleTime;
bool usePalette = color1 == SEGCOLOR(0);
-
+
for (int i = 0; i < SEGLEN; i++) {
uint32_t col = color2;
if (usePalette) color1 = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
@@ -530,6 +530,7 @@ uint16_t running_base(bool saw, bool dual=false) {
}
SEGMENT.setPixelColor(i, ca);
}
+
return FRAMETIME;
}
@@ -567,7 +568,6 @@ static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!";
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t mode_twinkle(void) {
- //SEGMENT.fill(SEGCOLOR(1));
SEGMENT.fade_out(224);
uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5;
@@ -583,7 +583,7 @@ uint16_t mode_twinkle(void) {
SEGENV.aux0++;
SEGENV.step = it;
}
-
+
uint16_t PRNG16 = SEGENV.aux1;
for (uint16_t i = 0; i < SEGENV.aux0; i++)
@@ -604,7 +604,7 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
*/
uint16_t dissolve(uint32_t color) {
bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness
-
+
for (int j = 0; j <= SEGLEN / 15; j++)
{
if (random8() <= SEGMENT.intensity) {
@@ -616,7 +616,7 @@ uint16_t dissolve(uint32_t color) {
if (color == SEGCOLOR(0))
{
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
- } else { SEGMENT.setPixelColor(i, color); }
+ } else { SEGMENT.setPixelColor(i, color); }
break; //only spawn 1 new pixel per frame per 50 LEDs
}
} else { //dissolve to secondary
@@ -626,12 +626,12 @@ uint16_t dissolve(uint32_t color) {
}
}
- if (SEGENV.call > (255 - SEGMENT.speed) + 15U)
+ if (SEGENV.call > (255 - SEGMENT.speed) + 15U)
{
SEGENV.aux0 = !SEGENV.aux0;
SEGENV.call = 0;
}
-
+
return FRAMETIME;
}
@@ -659,7 +659,7 @@ static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t mode_sparkle(void) {
- for(int i = 0; i < SEGLEN; i++) {
+ if (!SEGMENT.check2) for(int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
}
uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2;
@@ -669,11 +669,11 @@ uint16_t mode_sparkle(void) {
SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index
SEGENV.step = it;
}
-
+
SEGMENT.setPixelColor(SEGENV.aux0, SEGCOLOR(0));
return FRAMETIME;
}
-static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!;!,!;!;;m12=0";
+static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!;!;;m12=0";
/*
@@ -681,7 +681,7 @@ static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!;!,!;!;;m12=0";
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t mode_flash_sparkle(void) {
- for(uint16_t i = 0; i < SEGLEN; i++) {
+ if (!SEGMENT.check2) for(uint16_t i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
}
@@ -694,7 +694,7 @@ uint16_t mode_flash_sparkle(void) {
}
return FRAMETIME;
}
-static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!;Bg,Fx;!;;m12=0";
+static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!,,,,,Overlay;Bg,Fx;!;;m12=0";
/*
@@ -702,7 +702,7 @@ static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!;Bg,F
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t mode_hyper_sparkle(void) {
- for (int i = 0; i < SEGLEN; i++) {
+ if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
}
@@ -717,7 +717,7 @@ uint16_t mode_hyper_sparkle(void) {
}
return FRAMETIME;
}
-static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!;Bg,Fx;!;;m12=0";
+static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!,,,,,Overlay;Bg,Fx;!;;m12=0";
/*
@@ -754,7 +754,7 @@ static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!"
* Android loading circle
*/
uint16_t mode_android(void) {
-
+
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
}
@@ -768,7 +768,7 @@ uint16_t mode_android(void) {
}
uint16_t a = SEGENV.step;
-
+
if (SEGENV.aux0 == 0)
{
if (SEGENV.call %3 == 1) {a++;}
@@ -778,7 +778,7 @@ uint16_t mode_android(void) {
a++;
if (SEGENV.call %3 != 1) SEGENV.aux1--;
}
-
+
if (a >= SEGLEN) a = 0;
if (a + SEGENV.aux1 < SEGLEN)
@@ -825,7 +825,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett
// Use intensity setting to vary chase up to 1/2 string length
uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10);
- uint16_t b = a + size; //"trail" of chase, filled with color1
+ uint16_t b = a + size; //"trail" of chase, filled with color1
if (b > SEGLEN) b -= SEGLEN;
uint16_t c = b + size;
if (c > SEGLEN) c -= SEGLEN;
@@ -945,7 +945,7 @@ uint16_t mode_colorful(void) {
cols[3] = 0x0077F0F0;
}
for (size_t i = numColors; i < numColors*2 -1U; i++) cols[i] = cols[i-numColors];
-
+
uint32_t cycleTime = 50 + (8 * (uint32_t)(255 - SEGMENT.speed));
uint32_t it = strip.now / cycleTime;
if (it != SEGENV.step)
@@ -954,12 +954,12 @@ uint16_t mode_colorful(void) {
if (SEGENV.aux0 >= numColors) SEGENV.aux0 = 0;
SEGENV.step = it;
}
-
+
for (int i = 0; i < SEGLEN; i+= numColors)
{
for (int j = 0; j < numColors; j++) SEGMENT.setPixelColor(i + j, cols[SEGENV.aux0 + j]);
}
-
+
return FRAMETIME;
}
static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,3;!";
@@ -990,7 +990,7 @@ uint16_t mode_traffic_light(void) {
if (SEGENV.aux0 > 3) SEGENV.aux0 = 0;
SEGENV.step = strip.now;
}
-
+
return FRAMETIME;
}
static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US style;,!;!";
@@ -1116,7 +1116,7 @@ uint16_t larson_scanner(bool dual) {
if (SEGENV.step > index && SEGENV.step - index > SEGLEN/2) {
SEGENV.aux0 = !SEGENV.aux0;
}
-
+
for (int i = SEGENV.step; i < index; i++) {
uint16_t j = (SEGENV.aux0)?i:SEGLEN-1-i;
SEGMENT.setPixelColor( j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0));
@@ -1177,7 +1177,7 @@ uint16_t mode_comet(void) {
} else if (index < SEGENV.aux0 && index < 10) {
for (int i = 0; i < index ; i++) {
SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
- }
+ }
}
SEGENV.aux0 = index++;
@@ -1335,13 +1335,13 @@ uint16_t mode_loading(void) {
static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16";
-//American Police Light with all LEDs Red and Blue
+//American Police Light with all LEDs Red and Blue
uint16_t police_base(uint32_t color1, uint32_t color2)
{
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
uint16_t offset = it % SEGLEN;
-
+
uint16_t width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip
if (!width) width = 1;
for (int i = 0; i < width; i++) {
@@ -1354,7 +1354,7 @@ uint16_t police_base(uint32_t color1, uint32_t color2)
}
-//Police Lights Red and Blue
+//Police Lights Red and Blue
//uint16_t mode_police()
//{
// SEGMENT.fill(SEGCOLOR(1));
@@ -1363,15 +1363,14 @@ uint16_t police_base(uint32_t color1, uint32_t color2)
//static const char _data_FX_MODE_POLICE[] PROGMEM = "Police@!,Width;,Bg;0";
-//Police Lights with custom colors
+//Police Lights with custom colors
uint16_t mode_two_dots()
{
- SEGMENT.fill(SEGCOLOR(2));
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
-
return police_base(SEGCOLOR(0), color2);
}
-static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size;1,2,Bg;!";
+static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size,,,,,Overlay;1,2,Bg;!";
/*
@@ -1519,7 +1518,7 @@ uint16_t tricolor_chase(uint32_t color1, uint32_t color2) {
uint32_t it = strip.now / cycleTime; // iterator
uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
uint8_t index = it % (width*3);
-
+
for (int i = 0; i < SEGLEN; i++, index++) {
if (index > (width*3)-1) index = 0;
@@ -1549,7 +1548,7 @@ uint16_t mode_icu(void) {
uint16_t dest = SEGENV.step & 0xFFFF;
uint8_t space = (SEGMENT.intensity >> 3) +2;
- SEGMENT.fill(SEGCOLOR(1));
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
byte pindex = map(dest, 0, SEGLEN-SEGLEN/space, 0, 255);
uint32_t col = SEGMENT.color_from_palette(pindex, false, false, 0);
@@ -1580,7 +1579,7 @@ uint16_t mode_icu(void) {
return SPEED_FORMULA_L;
}
-static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!;!,!;!";
+static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!,,,,,Overlay;!,!;!";
/*
@@ -1598,7 +1597,7 @@ uint16_t mode_tricolor_wipe(void)
{
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2));
}
-
+
if(ledIndex < SEGLEN) { //wipe from 0 to 1
for (int i = 0; i < SEGLEN; i++)
{
@@ -1679,9 +1678,9 @@ uint16_t mode_multi_comet(void)
uint32_t it = strip.now / cycleTime;
if (SEGENV.step == it) return FRAMETIME;
if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed
-
+
SEGMENT.fade_out(SEGMENT.intensity);
-
+
uint16_t* comets = reinterpret_cast(SEGENV.data);
for (int i=0; i < 8; i++) {
@@ -1761,7 +1760,7 @@ uint16_t mode_oscillate(void)
uint16_t dataSize = sizeof(oscillator) * numOscillators;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
-
+
Oscillator* oscillators = reinterpret_cast(SEGENV.data);
if (SEGENV.call == 0)
@@ -1800,7 +1799,7 @@ uint16_t mode_oscillate(void)
}
SEGMENT.setPixelColor(i, color);
}
-
+
SEGENV.step = it;
return FRAMETIME;
}
@@ -1823,7 +1822,7 @@ uint16_t mode_lightning(void)
SEGENV.aux0 = 200; //200ms delay after leader
}
- SEGMENT.fill(SEGCOLOR(1));
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) { //flash on even number >2
for (int i = ledstart; i < ledstart + ledlen; i++)
@@ -1848,7 +1847,7 @@ uint16_t mode_lightning(void)
}
return FRAMETIME;
}
-static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!;!,!;!";
+static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!,,,,,Overlay;!,!;!";
// Pride2015
@@ -1892,6 +1891,7 @@ uint16_t mode_pride_2015(void)
}
SEGENV.step = sPseudotime;
SEGENV.aux0 = sHue16;
+
return FRAMETIME;
}
static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
@@ -1917,30 +1917,31 @@ static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=16,ix=
uint16_t mode_palette()
{
uint16_t counter = 0;
- if (SEGMENT.speed != 0)
+ if (SEGMENT.speed != 0)
{
counter = (strip.now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF;
counter = counter >> 8;
}
-
+
bool noWrap = (strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0));
for (int i = 0; i < SEGLEN; i++)
{
uint8_t colorIndex = (i * 255 / SEGLEN) - counter;
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, noWrap, 255));
}
+
return FRAMETIME;
}
-static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!";
+static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3=0,o2=0";
// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active
// Fire2012 by Mark Kriegsman, July 2012
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
-////
+////
// This basic one-dimensional 'fire' simulation works roughly as follows:
// There's a underlying array of 'heat' cells, that model the temperature
-// at each point along the line. Every cycle through the simulation,
+// at each point along the line. Every cycle through the simulation,
// four steps are performed:
// 1) All cells cool down a little bit, losing heat to the air
// 2) The heat from each cell drifts 'up' and diffuses a little
@@ -1951,7 +1952,7 @@ static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!";
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
//
// This simulation scales it self a bit depending on SEGLEN; it should look
-// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
+// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
//
// I recommend running this simulation at anywhere from 30-100 frames per second,
// meaning an interframe delay of about 10-35 milliseconds.
@@ -2069,6 +2070,7 @@ uint16_t mode_colorwaves()
}
SEGENV.step = sPseudotime;
SEGENV.aux0 = sHue16;
+
return FRAMETIME;
}
static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!";
@@ -2085,6 +2087,7 @@ uint16_t mode_bpm()
//SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10)));
}
+
return FRAMETIME;
}
static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64";
@@ -2165,7 +2168,7 @@ uint16_t mode_noise16_3()
uint16_t shift_y = 1234;
uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field
uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions
- uint32_t real_z = SEGENV.step*8;
+ uint32_t real_z = SEGENV.step*8;
uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down
uint8_t index = sin8(noise * 3); // map led color based on noise data
@@ -2200,7 +2203,7 @@ uint16_t mode_colortwinkle()
{
uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
-
+
CRGB fastled_col, prev;
fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness();
fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness();
@@ -2210,7 +2213,7 @@ uint16_t mode_colortwinkle()
uint16_t index = i >> 3;
uint8_t bitNum = i & 0x07;
bool fadeUp = bitRead(SEGENV.data[index], bitNum);
-
+
if (fadeUp) {
CRGB incrementalColor = fastled_col;
incrementalColor.nscale8_video(fadeUpAmount);
@@ -2261,12 +2264,13 @@ uint16_t mode_lake() {
for (int i = 0; i < SEGLEN; i++)
{
- int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2;
+ int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2;
uint8_t lum = (index > wave3) ? index - wave3 : 0;
//fastled_col = ColorFromPalette(SEGPALETTE, map(index,0,255,0,240), lum, LINEARBLEND);
//SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, 0, lum));
}
+
return FRAMETIME;
}
static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!";
@@ -2279,7 +2283,7 @@ uint16_t mode_meteor() {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* trail = SEGENV.data;
-
+
byte meteorSize= 1+ SEGLEN / 10;
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8);
uint16_t in = counter * SEGLEN >> 16;
@@ -2316,7 +2320,7 @@ uint16_t mode_meteor_smooth() {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* trail = SEGENV.data;
-
+
byte meteorSize= 1+ SEGLEN / 10;
uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1);
@@ -2331,7 +2335,7 @@ uint16_t mode_meteor_smooth() {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i]));
}
}
-
+
// draw meteor
for (int j = 0; j < meteorSize; j++) {
uint16_t index = in + j;
@@ -2351,7 +2355,7 @@ static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail
//Railway Crossing / Christmas Fairy lights
uint16_t mode_railway()
{
- uint16_t dur = 40 + (255 - SEGMENT.speed) * 10;
+ uint16_t dur = (256 - SEGMENT.speed) * 40;
uint16_t rampdur = (dur * SEGMENT.intensity) >> 8;
if (SEGENV.step > dur)
{
@@ -2368,16 +2372,16 @@ uint16_t mode_railway()
if (SEGENV.aux0) pos = 255 - pos;
for (int i = 0; i < SEGLEN; i += 2)
{
- SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255));
+ SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255)); // do not use color 1 or 2, always use palette
if (i < SEGLEN -1)
{
- SEGMENT.setPixelColor(i + 1, SEGMENT.color_from_palette(pos, false, false, 255));
+ SEGMENT.setPixelColor(i + 1, SEGMENT.color_from_palette(pos, false, false, 255)); // do not use color 1 or 2, always use palette
}
}
SEGENV.step += FRAMETIME;
return FRAMETIME;
}
-static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;;!";
+static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;1,2;!";
//Water ripple
@@ -2396,89 +2400,83 @@ typedef struct Ripple {
#else
#define MAX_RIPPLES 100
#endif
-uint16_t ripple_base(bool rainbow)
+uint16_t ripple_base()
{
uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
uint16_t dataSize = sizeof(ripple) * maxRipples;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
-
+
Ripple* ripples = reinterpret_cast(SEGENV.data);
- // ranbow background or chosen background, all very dim.
- if (rainbow) {
- if (SEGENV.call ==0) {
- SEGENV.aux0 = random8();
- SEGENV.aux1 = random8();
- }
- if (SEGENV.aux0 == SEGENV.aux1) {
- SEGENV.aux1 = random8();
- }
- else if (SEGENV.aux1 > SEGENV.aux0) {
- SEGENV.aux0++;
- } else {
- SEGENV.aux0--;
- }
- SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,235));
- } else {
- SEGMENT.fill(SEGCOLOR(1));
- }
-
//draw wave
- for (int i = 0; i < maxRipples; i++)
- {
+ for (int i = 0; i < maxRipples; i++) {
uint16_t ripplestate = ripples[i].state;
- if (ripplestate)
- {
+ if (ripplestate) {
uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation
uint16_t rippleorigin = ripples[i].pos;
uint32_t col = SEGMENT.color_from_palette(ripples[i].color, false, false, 255);
- uint16_t propagation = ((ripplestate/rippledecay -1) * SEGMENT.speed);
+ uint16_t propagation = ((ripplestate/rippledecay - 1) * (SEGMENT.speed + 1));
int16_t propI = propagation >> 8;
uint8_t propF = propagation & 0xFF;
- int16_t left = rippleorigin - propI -1;
uint8_t amp = (ripplestate < 17) ? triwave8((ripplestate-1)*8) : map(ripplestate,17,255,255,2);
- for (int16_t v = left; v < left +4; v++)
+ #ifndef WLED_DISABLE_2D
+ if (SEGMENT.is2D()) {
+ uint16_t cx = rippleorigin >> 8;
+ uint16_t cy = rippleorigin & 0xFF;
+ uint8_t mag = scale8(cubicwave8((propF>>2)), amp);
+ if (propI > 0) SEGMENT.draw_circle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag));
+ } else
+ #endif
{
- uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp);
- if (v < SEGLEN && v >= 0)
- {
+ int16_t left = rippleorigin - propI -1;
+ for (int16_t v = left; v < left +4; v++) {
+ uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp);
SEGMENT.setPixelColor(v, color_blend(SEGMENT.getPixelColor(v), col, mag)); // TODO
- }
- int16_t w = left + propI*2 + 3 -(v-left);
- if (w < SEGLEN && w >= 0)
- {
+ int16_t w = left + propI*2 + 3 -(v-left);
SEGMENT.setPixelColor(w, color_blend(SEGMENT.getPixelColor(w), col, mag)); // TODO
}
- }
+ }
ripplestate += rippledecay;
ripples[i].state = (ripplestate > 254) ? 0 : ripplestate;
- } else //randomly create new wave
- {
- if (random16(IBN + 10000) <= SEGMENT.intensity)
- {
+ } else {//randomly create new wave
+ if (random16(IBN + 10000) <= SEGMENT.intensity) {
ripples[i].state = 1;
- ripples[i].pos = random16(SEGLEN);
+ ripples[i].pos = SEGMENT.is2D() ? ((random8(SEGENV.virtualWidth())<<8) | (random8(SEGENV.virtualHeight()))) : random16(SEGLEN);
ripples[i].color = random8(); //color
}
}
}
+
return FRAMETIME;
}
#undef MAX_RIPPLES
uint16_t mode_ripple(void) {
- return ripple_base(false);
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
+ return ripple_base();
}
-static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #;,!;!";
+static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay;,!;!;12";
uint16_t mode_ripple_rainbow(void) {
- return ripple_base(true);
+ if (SEGENV.call ==0) {
+ SEGENV.aux0 = random8();
+ SEGENV.aux1 = random8();
+ }
+ if (SEGENV.aux0 == SEGENV.aux1) {
+ SEGENV.aux1 = random8();
+ } else if (SEGENV.aux1 > SEGENV.aux0) {
+ SEGENV.aux0++;
+ } else {
+ SEGENV.aux0--;
+ }
+ SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,235));
+ return ripple_base();
}
-static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!";
+static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!;12";
// TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a
@@ -2500,7 +2498,7 @@ CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
slowcycle16 += sin8(slowcycle16);
slowcycle16 = (slowcycle16 * 2053) + 1384;
uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
-
+
// Overall twinkle density.
// 0 (NONE lit) to 8 (ALL lit at once).
// Default is 5.
@@ -2533,7 +2531,7 @@ CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
// This code takes a pixel, and if its in the 'fading down'
// part of the cycle, it adjusts the color a little bit like the
// way that incandescent bulbs fade toward 'red' as they dim.
- if (fastcycle8 >= 128)
+ if (fastcycle8 >= 128)
{
uint8_t cooling = (fastcycle8 - 128) >> 4;
c.g = qsub8(c.g, cooling);
@@ -2577,7 +2575,7 @@ uint16_t twinklefox_base(bool cat)
uint8_t backgroundBrightness = bg.getAverageLight();
for (int i = 0; i < SEGLEN; i++) {
-
+
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
uint16_t myclockoffset16= PRNG16; // use that number as clock offset
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
@@ -2634,7 +2632,7 @@ uint16_t mode_halloween_eyes()
uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE;
if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short
- SEGMENT.fill(SEGCOLOR(1)); //fill background
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background
uint8_t state = SEGENV.aux1 >> 8;
uint16_t stateTime = SEGENV.call;
@@ -2646,15 +2644,15 @@ uint16_t mode_halloween_eyes()
if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices
state = 1;
}
-
+
if (state < 2) { //fade eyes
uint16_t startPos = SEGENV.aux0;
uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
-
+
uint32_t fadestage = (strip.now - SEGENV.step)*255 / stateTime;
if (fadestage > 255) fadestage = 255;
uint32_t c = color_blend(SEGMENT.color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage);
-
+
for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) {
if (strip.isMatrix) {
SEGMENT.setPixelColorXY(startPos + i, SEGMENT.offset, c);
@@ -2669,7 +2667,7 @@ uint16_t mode_halloween_eyes()
if (strip.now - SEGENV.step > stateTime) {
state++;
if (state > 2) state = 0;
-
+
if (state < 2) {
stateTime = 100 + SEGMENT.intensity*10; //eye fade time
} else {
@@ -2681,10 +2679,10 @@ uint16_t mode_halloween_eyes()
}
SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); //save state
-
+
return FRAMETIME;
}
-static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time;!,!;!;12";
+static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time,,,,,Overlay;!,!;!;12";
//Speed slider sets amount of LEDs lit, intensity sets unlit
@@ -2703,7 +2701,7 @@ uint16_t mode_static_pattern()
drawingLit = !drawingLit;
}
}
-
+
return FRAMETIME;
}
static const char _data_FX_MODE_STATIC_PATTERN[] PROGMEM = "Solid Pattern@Fg size,Bg size;Fg,!;!;;pal=0";
@@ -2737,8 +2735,8 @@ static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tr
uint16_t spots_base(uint16_t threshold)
{
- SEGMENT.fill(SEGCOLOR(1));
-
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
+
uint16_t maxZones = SEGLEN >> 2;
uint16_t zones = 1 + ((SEGMENT.intensity * maxZones) >> 8);
uint16_t zoneLen = SEGLEN / zones;
@@ -2757,7 +2755,7 @@ uint16_t spots_base(uint16_t threshold)
}
}
}
-
+
return FRAMETIME;
}
@@ -2767,7 +2765,7 @@ uint16_t mode_spots()
{
return spots_base((255 - SEGMENT.speed) << 8);
}
-static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@,Width;!,!;!";
+static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@,Width,,,,,Overlay;!,!;!";
//Intensity slider sets number of "lights", LEDs per light fade in and out
@@ -2778,7 +2776,7 @@ uint16_t mode_spots_fade()
uint16_t tr = (t >> 1) + (t >> 2);
return spots_base(tr);
}
-static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width;!,!;!";
+static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width,,,,,Overlay;!,!;!";
//each needs 12 bytes
@@ -2794,13 +2792,13 @@ typedef struct Ball {
uint16_t mode_bouncing_balls(void) {
//allocate segment data
const uint16_t strips = SEGMENT.nrOfVStrips(); // adapt for 2D
- const size_t maxNumBalls = 16;
+ const size_t maxNumBalls = 16;
uint16_t dataSize = sizeof(ball) * maxNumBalls;
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
-
+
Ball* balls = reinterpret_cast(SEGENV.data);
- SEGMENT.fill(SEGCOLOR(2) ? BLACK : SEGCOLOR(1));
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2) ? BLACK : SEGCOLOR(1));
// virtualStrip idea by @ewowi (Ewoud Wijma)
// requires virtual strip # to be embedded into upper 16 bits of index in setPixelColor()
@@ -2817,7 +2815,7 @@ uint16_t mode_bouncing_balls(void) {
if (SEGENV.call == 0) {
for (size_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time;
}
-
+
for (size_t i = 0; i < numBalls; i++) {
float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)/64 +1);
float timeSec = timeSinceLastBounce/1000.0f;
@@ -2837,7 +2835,7 @@ uint16_t mode_bouncing_balls(void) {
} else if (balls[i].height > 1.0f) {
continue; // do not draw OOB ball
}
-
+
uint32_t color = SEGCOLOR(0);
if (SEGMENT.palette) {
color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8)));
@@ -2857,7 +2855,7 @@ uint16_t mode_bouncing_balls(void) {
return FRAMETIME;
}
-static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls;!,!,!;!;1;m12=1"; //bar
+static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls,,,,,Overlay;!,!,!;!;1;m12=1"; //bar
/*
@@ -2878,7 +2876,7 @@ uint16_t sinelon_base(bool dual, bool rainbow=false) {
if (rainbow) color2 = color1; //rainbow
SEGMENT.setPixelColor(SEGLEN-1-pos, color2);
}
- if (SEGENV.aux0 != pos) {
+ if (SEGENV.aux0 != pos) {
if (SEGENV.aux0 < pos) {
for (int i = SEGENV.aux0; i < pos ; i++) {
SEGMENT.setPixelColor(i, color1);
@@ -2915,19 +2913,35 @@ uint16_t mode_sinelon_rainbow(void) {
static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow@!,Trail;,,!;!";
-//Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
+// utility function that will add random glitter to SEGMENT
+void glitter_base(uint8_t intensity, uint32_t col = ULTRAWHITE) {
+ if (intensity > random8()) {
+ if (SEGMENT.is2D()) {
+ SEGMENT.setPixelColorXY(random16(SEGMENT.virtualWidth()),random16(SEGMENT.virtualHeight()), col);
+ } else {
+ SEGMENT.setPixelColor(random16(SEGLEN), col);
+ }
+ }
+}
+
+//Glitter with palette background, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
uint16_t mode_glitter()
{
- mode_palette();
-
- if (SEGMENT.intensity > random8())
- {
- SEGMENT.setPixelColor(random16(SEGLEN), ULTRAWHITE);
- }
-
+ if (!SEGMENT.check2) mode_palette(); // use "* Color 1" palette for solid background (replacing "Solid glitter")
+ glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE);
return FRAMETIME;
}
-static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!;;!;;m12=0"; //pixels
+static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels
+
+
+//Solid colour background with glitter
+uint16_t mode_solid_glitter()
+{
+ SEGMENT.fill(SEGCOLOR(0));
+ glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE);
+ return FRAMETIME;
+}
+static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;Bg,,Glitter color;;;m12=0";
//each needs 19 bytes
@@ -2953,7 +2967,7 @@ uint16_t mode_popcorn(void) {
Spark* popcorn = reinterpret_cast(SEGENV.data);
bool hasCol2 = SEGCOLOR(2);
- SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1));
+ if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1));
struct virtualStrip {
static void runStrip(uint16_t stripNr, Spark* popcorn) {
@@ -3000,7 +3014,7 @@ uint16_t mode_popcorn(void) {
return FRAMETIME;
}
-static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!;!,!,!;!;;m12=1"; //bar
+static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!,,,,,Overlay;!,!,!;!;;m12=1"; //bar
//values close to 100 produce 5Hz flicker, which looks very candle-y
@@ -3061,7 +3075,7 @@ uint16_t candle(bool multi)
s_target += offset;
uint8_t dif = (s_target > s) ? s_target - s : s - s_target;
-
+
fadeStep = dif >> speedFactor;
if (fadeStep == 0) fadeStep = 1;
}
@@ -3129,26 +3143,26 @@ uint16_t mode_starburst(void) {
uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
-
+
uint32_t it = millis();
-
+
star* stars = reinterpret_cast(SEGENV.data);
-
+
float maxSpeed = 375.0f; // Max velocity
float particleIgnition = 250.0f; // How long to "flash"
float particleFadeTime = 1500.0f; // Fade out time
-
+
for (int j = 0; j < numStars; j++)
{
// speed to adjust chance of a burst, max is nearly always.
if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0)
{
- // Pick a random color and location.
+ // Pick a random color and location.
uint16_t startPos = random16(SEGLEN-1);
float multiplier = (float)(random8())/255.0 * 1.0;
stars[j].color = CRGB(SEGMENT.color_wheel(random8()));
- stars[j].pos = startPos;
+ stars[j].pos = startPos;
stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier;
stars[j].birth = it;
stars[j].last = it;
@@ -3161,9 +3175,9 @@ uint16_t mode_starburst(void) {
}
}
}
-
- SEGMENT.fill(SEGCOLOR(1));
-
+
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
+
for (int j=0; j> 1;
-
+
if (stars[j].fragment[i] > 0) {
//all fragments travel right, will be mirrored on other side
stars[j].fragment[i] += stars[j].vel * dt * (float)var/3.0;
@@ -3180,10 +3194,10 @@ uint16_t mode_starburst(void) {
stars[j].last = it;
stars[j].vel -= 3*stars[j].vel*dt;
}
-
+
CRGB c = stars[j].color;
- // If the star is brand new, it flashes white briefly.
+ // If the star is brand new, it flashes white briefly.
// Otherwise it just fades over time.
float fade = 0.0f;
float age = it-stars[j].birth;
@@ -3191,7 +3205,7 @@ uint16_t mode_starburst(void) {
if (age < particleIgnition) {
c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), 254.5f*((age / particleIgnition))));
} else {
- // Figure out how much to fade and shrink the star based on
+ // Figure out how much to fade and shrink the star based on
// its age relative to its lifetime
if (age > particleIgnition + particleFadeTime) {
fade = 1.0f; // Black hole, all faded out
@@ -3204,7 +3218,7 @@ uint16_t mode_starburst(void) {
c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), f));
}
}
-
+
float particleSize = (1.0f - fade) * 2.0f;
for (size_t index=0; index < STARBURST_MAX_FRAG*2; index++) {
@@ -3217,7 +3231,7 @@ uint16_t mode_starburst(void) {
int end = loc + particleSize;
if (start < 0) start = 0;
if (start == end) end++;
- if (end > SEGLEN) end = SEGLEN;
+ if (end > SEGLEN) end = SEGLEN;
for (int p = start; p < end; p++) {
SEGMENT.setPixelColor(p, c.r, c.g, c.b);
}
@@ -3227,7 +3241,7 @@ uint16_t mode_starburst(void) {
return FRAMETIME;
}
#undef STARBURST_MAX_FRAG
-static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments;,!;!;;pal=11,m12=0";
+static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments,,,,,Overlay;,!;!;;pal=11,m12=0";
/*
@@ -3258,15 +3272,14 @@ uint16_t mode_exploding_fireworks(void)
SEGENV.aux1 = dataSize;
}
- //SEGMENT.fill(BLACK);
SEGMENT.fade_out(252);
-
+
Spark* sparks = reinterpret_cast(SEGENV.data);
Spark* flare = sparks; //first spark is flare data
float gravity = -0.0004f - (SEGMENT.speed/800000.0f); // m/s/s
gravity *= rows;
-
+
if (SEGENV.aux0 < 2) { //FLARE
if (SEGENV.aux0 == 0) { //init flare
flare->pos = 0;
@@ -3276,10 +3289,10 @@ uint16_t mode_exploding_fireworks(void)
flare->vel = sqrt(-2.0f * gravity * peakHeight);
flare->velX = strip.isMatrix ? (random8(8)-4)/32.f : 0; // no X velocity on 1D
flare->col = 255; //brightness
- SEGENV.aux0 = 1;
+ SEGENV.aux0 = 1;
}
-
- // launch
+
+ // launch
if (flare->vel > 12 * gravity) {
// flare
if (strip.isMatrix) SEGMENT.setPixelColorXY(int(flare->posX), rows - uint16_t(flare->pos) - 1, flare->col, flare->col, flare->col);
@@ -3296,40 +3309,40 @@ uint16_t mode_exploding_fireworks(void)
} else if (SEGENV.aux0 < 4) {
/*
* Explode!
- *
+ *
* Explosion happens where the flare ended.
* Size is proportional to the height.
*/
int nSparks = flare->pos + random8(4);
nSparks = constrain(nSparks, 1, numSparks);
-
+
// initialize sparks
if (SEGENV.aux0 == 2) {
- for (int i = 1; i < nSparks; i++) {
+ for (int i = 1; i < nSparks; i++) {
sparks[i].pos = flare->pos;
sparks[i].posX = flare->posX;
sparks[i].vel = (float(random16(0, 20000)) / 10000.0f) - 0.9f; // from -0.9 to 1.1
sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips
sparks[i].velX = strip.isMatrix ? (float(random16(0, 4000)) / 10000.0f) - 0.2f : 0; // from -0.2 to 0.2
- sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
- //sparks[i].col = constrain(sparks[i].col, 0, 345);
+ sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
+ //sparks[i].col = constrain(sparks[i].col, 0, 345);
sparks[i].colIndex = random8();
- sparks[i].vel *= flare->pos/rows; // proportional to height
+ sparks[i].vel *= flare->pos/rows; // proportional to height
sparks[i].velX *= strip.isMatrix ? flare->posX/cols : 0; // proportional to width
sparks[i].vel *= -gravity *50;
- }
- //sparks[1].col = 345; // this will be our known spark
- *dying_gravity = gravity/2;
+ }
+ //sparks[1].col = 345; // this will be our known spark
+ *dying_gravity = gravity/2;
SEGENV.aux0 = 3;
}
-
+
if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks
for (int i = 1; i < nSparks; i++) {
sparks[i].pos += sparks[i].vel;
sparks[i].posX += sparks[i].velX;
sparks[i].vel += *dying_gravity;
sparks[i].velX += strip.isMatrix ? *dying_gravity : 0;
- if (sparks[i].col > 3) sparks[i].col -= 4;
+ if (sparks[i].col > 3) sparks[i].col -= 4;
if (sparks[i].pos > 0 && sparks[i].pos < rows) {
if (strip.isMatrix && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue;
@@ -3360,7 +3373,7 @@ uint16_t mode_exploding_fireworks(void)
}
}
- return FRAMETIME;
+ return FRAMETIME;
}
#undef MAX_SPARKS
static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!;12;pal=11,ix=128";
@@ -3374,13 +3387,13 @@ uint16_t mode_drip(void)
{
//allocate segment data
uint16_t strips = SEGMENT.nrOfVStrips();
- const int maxNumDrops = 4;
+ const int maxNumDrops = 4;
uint16_t dataSize = sizeof(spark) * maxNumDrops;
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
Spark* drops = reinterpret_cast(SEGENV.data);
- SEGMENT.fill(SEGCOLOR(1));
-
+ if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
+
struct virtualStrip {
static void runStrip(uint16_t stripNr, Spark* drops) {
@@ -3449,7 +3462,7 @@ uint16_t mode_drip(void)
return FRAMETIME;
}
-static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips;!,!;!;;m12=1"; //bar
+static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,,,,,Overlay;!,!;!;;m12=1"; //bar
/*
@@ -3472,7 +3485,7 @@ uint16_t mode_tetrix(void) {
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
Tetris* drops = reinterpret_cast(SEGENV.data);
- if (SEGENV.call == 0) SEGMENT.fill(SEGCOLOR(1)); // will fill entire segment (1D or 2D)
+ //if (SEGENV.call == 0) SEGMENT.fill(SEGCOLOR(1)); // will fill entire segment (1D or 2D), then use drop->step = 0 below
// virtualStrip idea by @ewowi (Ewoud Wijma)
// requires virtual strip # to be embedded into upper 16 bits of index in setPixelcolor()
@@ -3482,11 +3495,10 @@ uint16_t mode_tetrix(void) {
// initialize dropping on first call or segment full
if (SEGENV.call == 0) {
drop->stack = 0; // reset brick stack size
- drop->step = 0;
+ drop->step = millis() + 2000; // start by fading out strip
if (SEGMENT.check1) drop->col = 0;// use only one color from palette
- //for (int i=0; istep == 0) { // init brick
// speed calcualtion: a single brick should reach bottom of strip in X seconds
// if the speed is set to 1 this should take 5s and at 255 it should take 0.25s
@@ -3499,7 +3511,7 @@ uint16_t mode_tetrix(void) {
drop->step = 1; // drop state (0 init, 1 forming, 2 falling)
drop->brick = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
}
-
+
if (drop->step == 1) { // forming
if (random8()>>6) { // random drop
drop->step = 2; // fall
@@ -3538,7 +3550,7 @@ uint16_t mode_tetrix(void) {
for (int stripNr=0; stripNr> 11));
if (SEGMENT.speed == 255) size = 255;
-
+
if (percent <= 100) {
for (int i = 0; i < SEGLEN; i++) {
if (i < SEGENV.aux1) {
@@ -3661,16 +3673,16 @@ static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;;m12=
// For Dan.
//
//
-// In this animation, there are four "layers" of waves of light.
+// In this animation, there are four "layers" of waves of light.
//
// Each layer moves independently, and each is scaled separately.
//
-// All four wave layers are added together on top of each other, and then
-// another filter is applied that adds "whitecaps" of brightness where the
+// All four wave layers are added together on top of each other, and then
+// another filter is applied that adds "whitecaps" of brightness where the
// waves line up with each other more. Finally, another pass is taken
// over the led array to 'deepen' (dim) the blues and greens.
//
-// The speed and scale and motion each layer varies slowly within independent
+// The speed and scale and motion each layer varies slowly within independent
// hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions
// with a lot of oddly specific numeric ranges.
//
@@ -3685,7 +3697,7 @@ CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t
uint16_t ci = cistart;
uint16_t waveangle = ioff;
uint16_t wavescale_half = (wavescale >> 1) + 20;
-
+
waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i
uint16_t s16 = sin16(waveangle) + 32768;
uint16_t cs = scale16(s16, wavescale_half) + wavescale_half;
@@ -3699,14 +3711,14 @@ uint16_t mode_pacifica()
{
uint32_t nowOld = strip.now;
- CRGBPalette16 pacifica_palette_1 =
- { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
+ CRGBPalette16 pacifica_palette_1 =
+ { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 };
- CRGBPalette16 pacifica_palette_2 =
- { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
+ CRGBPalette16 pacifica_palette_2 =
+ { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F };
- CRGBPalette16 pacifica_palette_3 =
- { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33,
+ CRGBPalette16 pacifica_palette_3 =
+ { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33,
0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF };
if (SEGMENT.palette) {
@@ -3739,7 +3751,7 @@ uint16_t mode_pacifica()
uint8_t basethreshold = beatsin8( 9, 55, 65);
uint8_t wave = beat8( 7 );
-
+
for (int i = 0; i < SEGLEN; i++) {
CRGB c = CRGB(2, 6, 10);
// Render each of four layers, with different scales and speeds, that vary over time
@@ -3747,7 +3759,7 @@ uint16_t mode_pacifica()
c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401));
c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8(9, 10,38) , 0-beat16(503));
c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8(8, 10,28) , beat16(601));
-
+
// Add extra 'white' to areas where the four layers of light have lined up brightly
uint8_t threshold = scale8( sin8( wave), 20) + basethreshold;
wave += 7;
@@ -3759,8 +3771,8 @@ uint16_t mode_pacifica()
}
//deepen the blues and greens
- c.blue = scale8(c.blue, 145);
- c.green = scale8(c.green, 200);
+ c.blue = scale8(c.blue, 145);
+ c.green = scale8(c.green, 200);
c |= CRGB( 2, 5, 7);
SEGMENT.setPixelColor(i, c.red, c.green, c.blue);
@@ -3772,21 +3784,6 @@ uint16_t mode_pacifica()
static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=51";
-//Solid colour background with glitter
-uint16_t mode_solid_glitter()
-{
- SEGMENT.fill(SEGCOLOR(0));
-
- if (SEGMENT.intensity > random8())
- {
- SEGMENT.setPixelColor(random16(SEGLEN), ULTRAWHITE);
- }
-
- return FRAMETIME;
-}
-static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;!;;;m12=0";
-
-
/*
* Mode simulates a gradual sunrise
*/
@@ -3799,12 +3796,12 @@ uint16_t mode_sunrise() {
SEGENV.step = millis(); //save starting time, millis() because now can change from sync
SEGENV.aux0 = SEGMENT.speed;
}
-
- SEGMENT.fill(0);
+
+ SEGMENT.fill(BLACK);
uint16_t stage = 0xFFFF;
-
+
uint32_t s10SinceStart = (millis() - SEGENV.step) /100; //tenths of seconds
-
+
if (SEGMENT.speed > 120) { //quick sunrise and sunset
uint16_t counter = (strip.now >> 1) * (((SEGMENT.speed -120) >> 1) +1);
stage = triwave16(counter);
@@ -3816,7 +3813,7 @@ uint16_t mode_sunrise() {
stage = map(s10SinceStart, 0, s10Target, 0, 0xFFFF);
if (SEGMENT.speed > 60) stage = 0xFFFF - stage; //sunset
}
-
+
for (int i = 0; i <= SEGLEN/2; i++)
{
//default palette is Fire
@@ -3963,12 +3960,12 @@ static const char _data_FX_MODE_SINEWAVE[] PROGMEM = "Sine";
uint16_t mode_flow(void)
{
uint16_t counter = 0;
- if (SEGMENT.speed != 0)
+ if (SEGMENT.speed != 0)
{
counter = strip.now * ((SEGMENT.speed >> 2) +1);
counter = counter >> 8;
}
-
+
uint16_t maxZones = SEGLEN / 6; //only looks good if each zone has at least 6 LEDs
uint16_t zones = (SEGMENT.intensity * maxZones) >> 8;
if (zones & 0x01) zones++; //zones must be even
@@ -4001,7 +3998,6 @@ static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //ver
*/
uint16_t mode_chunchun(void)
{
- //SEGMENT.fill(SEGCOLOR(1));
SEGMENT.fade_out(254); // add a bit of trail
uint16_t counter = strip.now * (6 + (SEGMENT.speed >> 4));
uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment
@@ -4178,7 +4174,7 @@ uint16_t mode_washing_machine(void) {
speed /= quot;
SEGENV.step += (speed * 128.0f);
-
+
for (int i=0; i> 7));
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3));
@@ -4267,19 +4263,19 @@ uint16_t mode_tv_simulator(void) {
tvSimulator->sceeneColorBri = random8 ( 200, 240); // random start color-brightness for the sceene
SEGENV.aux1 = 1;
SEGENV.aux0 = 0;
- }
-
+ }
+
// slightly change the color-tone in this sceene
if ( SEGENV.aux0 == 0) {
// hue change in both directions
j = random8(4 * colorIntensity);
hue = (random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative
((j + tvSimulator->sceeneColorHue) < 767 ? tvSimulator->sceeneColorHue + j : tvSimulator->sceeneColorHue + j - 767) ; // positive
-
+
// saturation
j = random8(2 * colorIntensity);
sat = (tvSimulator->sceeneColorSat - j) < 0 ? 0 : tvSimulator->sceeneColorSat - j;
-
+
// brightness
j = random8(100);
bri = (tvSimulator->sceeneColorBri - j) < 0 ? 0 : tvSimulator->sceeneColorBri - j;
@@ -4303,7 +4299,7 @@ uint16_t mode_tv_simulator(void) {
ng = (uint8_t)gamma8(tvSimulator->actualColorG) * 257;
nb = (uint8_t)gamma8(tvSimulator->actualColorB) * 257;
- if (SEGENV.aux0 == 0) { // initialize next iteration
+ if (SEGENV.aux0 == 0) { // initialize next iteration
SEGENV.aux0 = 1;
// randomize total duration and fade duration for the actual color
@@ -4319,7 +4315,7 @@ uint16_t mode_tv_simulator(void) {
// fade from prev volor to next color
if (tvSimulator->elapsed < tvSimulator->fadeTime) {
- r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr);
+ r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr);
g = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pg, ng);
b = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pb, nb);
} else { // Avoid divide-by-zero in map()
@@ -4340,7 +4336,7 @@ uint16_t mode_tv_simulator(void) {
tvSimulator->pb = nb;
SEGENV.aux0 = 0;
}
-
+
return FRAMETIME;
}
static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;";
@@ -4386,7 +4382,7 @@ class AuroraWave {
alive = true;
}
- CRGB getColorForLED(int ledIndex) {
+ CRGB getColorForLED(int ledIndex) {
if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave
CRGB rgb;
@@ -4399,7 +4395,7 @@ class AuroraWave {
//The age of the wave determines it brightness.
//At half its maximum age it will be the brightest.
- float ageFactor = 0.1;
+ float ageFactor = 0.1;
if((float)age / ttl < 0.5) {
ageFactor = (float)age / (ttl / 2);
} else {
@@ -4411,7 +4407,7 @@ class AuroraWave {
rgb.r = basecolor.r * factor;
rgb.g = basecolor.g * factor;
rgb.b = basecolor.b * factor;
-
+
return rgb;
};
@@ -4488,22 +4484,22 @@ uint16_t mode_aurora(void) {
if (SEGCOLOR(1)) backlight++;
if (SEGCOLOR(2)) backlight++;
//Loop through LEDs to determine color
- for (int i = 0; i < SEGLEN; i++) {
+ for (int i = 0; i < SEGLEN; i++) {
CRGB mixedRgb = CRGB(backlight, backlight, backlight);
//For each LED we must check each wave if it is "active" at this position.
//If there are multiple waves active on a LED we multiply their values.
for (int j = 0; j < SEGENV.aux1; j++) {
CRGB rgb = waves[j].getColorForLED(i);
-
- if(rgb != CRGB(0)) {
+
+ if(rgb != CRGB(0)) {
mixedRgb += rgb;
}
}
SEGMENT.setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]);
}
-
+
return FRAMETIME;
}
static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50";
@@ -4629,8 +4625,8 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
SEGENV.aux0 = 0; // start with red hue
}
- bool dot = false;
- bool grad = true;
+ bool dot = SEGMENT.check3;
+ bool grad = SEGMENT.check1;
byte numLines = SEGMENT.intensity/16 + 1;
@@ -4646,24 +4642,26 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
byte xsteps = abs8(x1 - y1) + 1;
byte ysteps = abs8(x2 - y2) + 1;
byte steps = xsteps >= ysteps ? xsteps : ysteps;
-
+ //Draw gradient line
for (size_t i = 1; i <= steps; i++) {
- byte dx = lerp8by8(x1, y1, i * 255 / steps);
- byte dy = lerp8by8(x2, y2, i * 255 / steps);
+ uint8_t rate = i * 255 / steps;
+ byte dx = lerp8by8(x1, y1, rate);
+ byte dy = lerp8by8(x2, y2, rate);
+ //SEGMENT.setPixelColorXY(dx, dy, grad ? color.nscale8_video(255-rate) : color); // use addPixelColorXY for different look
SEGMENT.addPixelColorXY(dx, dy, color); // use setPixelColorXY for different look
- if (grad) SEGMENT.fadePixelColorXY(dx, dy, (i * 255 / steps)); //Draw gradient line
+ if (grad) SEGMENT.fadePixelColorXY(dx, dy, rate);
}
if (dot) { //add white point at the ends of line
- SEGMENT.addPixelColorXY(x1, x2, WHITE);
- SEGMENT.addPixelColorXY(y1, y2, WHITE);
+ SEGMENT.setPixelColorXY(x1, x2, WHITE);
+ SEGMENT.setPixelColorXY(y1, y2, DARKSLATEGRAY);
}
}
- SEGMENT.blur(4);
+ if (SEGMENT.custom3) SEGMENT.blur(SEGMENT.custom3/2);
return FRAMETIME;
} // mode_2DColoredBursts()
-static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines;;!;2";
+static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines,,,Blur,Gradient,,Dots;;!;2;c3=16";
/////////////////////
@@ -4705,10 +4703,9 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
- SEGENV.aux0 = 0; // hue
}
- uint8_t speeds = SEGMENT.speed/2;
+ uint8_t speeds = SEGMENT.speed/2 + 1;
uint8_t freq = SEGMENT.intensity/8;
uint32_t ms = millis() / 20;
@@ -4717,17 +4714,21 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
for (int i = 0; i < rows; i++) {
uint16_t x = beatsin8(speeds, 0, cols - 1, 0, i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, i * freq + 128);
uint16_t x1 = beatsin8(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq);
- SEGENV.aux0 = i * 128 / cols + ms; //ewowi20210629: not width - 1 to avoid crash if width = 1
+ uint8_t hue = (i * 128 / rows) + ms;
+ // skip every 4th row every now and then (fade it more)
if ((i + ms / 8) & 3) {
+ // draw a gradient line between x and x1
x = x / 2; x1 = x1 / 2;
- byte steps = abs8(x - x1) + 1;
+ uint8_t steps = abs8(x - x1) + 1;
for (size_t k = 1; k <= steps; k++) {
- byte dx = lerp8by8(x, x1, k * 255 / steps);
- SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, SEGENV.aux0, 255, LINEARBLEND));
- SEGMENT.fadePixelColorXY(dx, i, (k * 255 / steps));
+ uint8_t rate = k * 255 / steps;
+ uint8_t dx = lerp8by8(x, x1, rate);
+ //SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate));
+ SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look
+ SEGMENT.fadePixelColorXY(dx, i, rate);
}
- SEGMENT.addPixelColorXY(x, i, DARKSLATEGRAY);
- SEGMENT.addPixelColorXY(x1, i, WHITE);
+ SEGMENT.setPixelColorXY(x, i, DARKSLATEGRAY);
+ SEGMENT.setPixelColorXY(x1, i, WHITE);
}
}
@@ -4834,7 +4835,7 @@ static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y f
///////////////////////////////////////////
typedef struct ColorCount {
CRGB color;
- int8_t count;
+ int8_t count;
} colorCount;
uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color
@@ -4843,17 +4844,18 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled
+ const uint16_t crcBufferLen = (SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi)
- if (!SEGENV.allocateData(dataSize + sizeof(unsigned long))) return mode_static(); //allocation failed
+ if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed
CRGB *prevLeds = reinterpret_cast(SEGENV.data);
- unsigned long *resetMillis = reinterpret_cast(SEGENV.data + dataSize); // triggers reset
+ uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize);
CRGB backgroundColor = SEGCOLOR(1);
- if (SEGENV.call == 0 || strip.now - *resetMillis > 5000) {
- *resetMillis = strip.now;
-
- random16_set_seed(strip.now); //seed the random generator
+ if (SEGENV.call == 0 || strip.now - SEGMENT.step > 5000) {
+ SEGENV.step = strip.now;
+ SEGENV.aux0 = 0;
+ random16_set_seed(millis()>>2); //seed the random generator
//give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen)
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
@@ -4861,17 +4863,18 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
if (state == 0)
SEGMENT.setPixelColorXY(x,y, backgroundColor);
else
- SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0));
+ SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255));
}
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black;
-
-
- SEGENV.aux1 = 0;
- SEGENV.aux0 = 0xFFFF;
+ memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen);
+ } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * map(SEGMENT.speed,0,255,64,4)) {
+ // update only when appropriate time passes (in 42 FPS slots)
+ return FRAMETIME;
}
//copy previous leds (save previous generation)
+ //NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y);
//calculate new leds
@@ -4908,8 +4911,8 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0);
if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness
else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
- else if ((col == bgc) && (neighbors == 3)) { // Reproduction
- //find dominantcolor and assign to cell
+ else if ((col == bgc) && (neighbors == 3)) { // Reproduction
+ //find dominant color and assign to cell
colorCount dominantColorCount = {backgroundColor, 0};
for (int i=0; i<9 && colorsCount[i].count != 0; i++)
if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i];
@@ -4919,16 +4922,17 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
} //x,y
// calculate CRC16 of leds
- uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize-1); //ewowi: prevLeds instead of leds work as well, tbd: compare more patterns, see SR!
-
+ uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize);
// check if we had same CRC and reset if needed
+ bool repetition = false;
+ for (int i=0; i>1)); // update only when appropriate time passes (in 42 FPS slots)
+ return FRAMETIME;
} // mode_2Dgameoflife()
static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2";
@@ -5086,11 +5090,11 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
for (int i=0; i < 256; i ++) {
//float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f;
//float ylocn = float(cos8(now/4+i*2)) / 255.0f;
- uint8_t xlocn = sin8(strip.now/2+i*(SEGMENT.speed>>5));
- uint8_t ylocn = cos8(strip.now/2+i*2);
+ uint8_t xlocn = sin8(millis()/4+i*(SEGMENT.speed>>5));
+ uint8_t ylocn = cos8(millis()/4+i*2);
xlocn = map(xlocn,0,255,0,cols-1);
ylocn = map(ylocn,0,255,0,rows-1);
- SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0));
+ SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0));
}
return FRAMETIME;
@@ -5173,15 +5177,15 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have
float speed = 0.25f * (1+(SEGMENT.speed>>6));
// get some 2 random moving points
- uint8_t x2 = inoise8(strip.now * speed, 25355, 685 ) / 16;
- uint8_t y2 = inoise8(strip.now * speed, 355, 11685 ) / 16;
+ uint8_t x2 = map(inoise8(strip.now * speed, 25355, 685), 0, 255, 0, cols-1);
+ uint8_t y2 = map(inoise8(strip.now * speed, 355, 11685), 0, 255, 0, rows-1);
- uint8_t x3 = inoise8(strip.now * speed, 55355, 6685 ) / 16;
- uint8_t y3 = inoise8(strip.now * speed, 25355, 22685 ) / 16;
+ uint8_t x3 = map(inoise8(strip.now * speed, 55355, 6685), 0, 255, 0, cols-1);
+ uint8_t y3 = map(inoise8(strip.now * speed, 25355, 22685), 0, 255, 0, rows-1);
// and one Lissajou function
- uint8_t x1 = beatsin8(23 * speed, 0, 15);
- uint8_t y1 = beatsin8(28 * speed, 0, 15);
+ uint8_t x1 = beatsin8(23 * speed, 0, cols-1);
+ uint8_t y1 = beatsin8(28 * speed, 0, rows-1);
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
@@ -5200,7 +5204,7 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have
dist += sqrt16((dx * dx) + (dy * dy));
// inverse result
- byte color = 1000 / dist;
+ byte color = dist ? 1000 / dist : 255;
// map color between thresholds
if (color > 0 and color < 60) {
@@ -5616,7 +5620,7 @@ uint16_t mode_2Dcrazybees(void) {
SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1));
SEGMENT.fadeToBlackBy(32);
-
+
for (size_t i = 0; i < n; i++) {
SEGMENT.addPixelColorXY(bee[i].aimX + 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255));
SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY + 1, CHSV(bee[i].hue, 255, 255));
@@ -5792,7 +5796,7 @@ uint16_t mode_2Dfloatingblobs(void) {
// reduce radius until it is < 1
blob->r[i] -= (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05f;
if (blob->r[i] < 1.f) {
- blob->grow[i] = true;
+ blob->grow[i] = true;
}
}
uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0);
@@ -5858,7 +5862,7 @@ uint16_t mode_2Dscrollingtext(void) {
char text[33] = {'\0'};
if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
- if (!strlen(text) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#TIME"),5)) { // fallback if empty segment name: display date and time
+ if (!strlen(text) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time
char sec[5];
byte AmPmHour = hour(localTime);
boolean isitAM = true;
@@ -5869,7 +5873,10 @@ uint16_t mode_2Dscrollingtext(void) {
if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
else sprintf_P(sec, PSTR(":%02d"), second(localTime));
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
+ else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, PSTR("%d.%d"), day(localTime), month(localTime));
+ else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
+ else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, PSTR("%2d:%02d"), AmPmHour, minute(localTime));
else sprintf_P(text, PSTR("%s %d, %d %2d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
}
const int numberOfLetters = strlen(text);
@@ -5879,20 +5886,21 @@ uint16_t mode_2Dscrollingtext(void) {
else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2;
++SEGENV.aux1 &= 0xFF; // color shift
SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED);
-
- // we need it 3 times
- SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
- SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
- SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
- for (int i = 0; i < numberOfLetters; i++) {
- if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen
- SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0));
+ if (!SEGMENT.check2) {
+ // we need it 3 times
+ SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
+ SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
+ SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
}
+}
+ for (int i = 0; i < numberOfLetters; i++) {
+ if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen
+ SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0));
}
return FRAMETIME;
}
-static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size;!,!;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
+static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,,Overlay;!,!;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
////////////////////////////
@@ -6181,7 +6189,7 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment
uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6232,7 +6240,7 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6280,7 +6288,7 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivty" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6525,7 +6533,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
// updated, similar to "plasma" effect - softhack007
uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2;
thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases.
-
+
uint8_t colorIndex=thisbright;
if (volumeSmth * SEGMENT.intensity / 64 < thisbright) {thisbright = 0;}
@@ -6663,7 +6671,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
SEGENV.aux0 = 0;
}
- int fadeoutDelay = (256 - SEGMENT.speed) / 32;
+ int fadeoutDelay = (256 - SEGMENT.speed) / 32;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed);
SEGENV.step += FRAMETIME;
@@ -6729,11 +6737,11 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float my_magnitude = *(float*) um_data->u_data[5] / 4.0f;
+ float my_magnitude = *(float*) um_data->u_data[5] / 4.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
- int fadeoutDelay = (256 - SEGMENT.speed) / 32;
+ int fadeoutDelay = (256 - SEGMENT.speed) / 32;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed);
int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
@@ -6821,13 +6829,13 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
+ float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
- int fadeoutDelay = (256 - SEGMENT.speed) / 64;
+ int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate);
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
@@ -6934,7 +6942,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6976,7 +6984,7 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
//SEGMENT.fade_out(224); // Just in case something doesn't get faded.
- int fadeoutDelay = (256 - SEGMENT.speed) / 96;
+ int fadeoutDelay = (256 - SEGMENT.speed) / 96;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
@@ -7001,7 +7009,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
+ float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
@@ -7047,7 +7055,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
float FFT_MajorPeak = *(float*) um_data->u_data[4];
uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
uint8_t *binNum = (uint8_t*)um_data->u_data[7];
- float my_magnitude = *(float*) um_data->u_data[5] / 8.0f;
+ float my_magnitude = *(float*) um_data->u_data[5] / 8.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
@@ -7113,7 +7121,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
}
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
- int fadeoutDelay = (256 - SEGMENT.speed) / 64;
+ int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed);
for (int x=0; x < cols; x++) {
@@ -7126,7 +7134,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
uint32_t ledColor = BLACK;
for (int y=0; y < barHeight; y++) {
- if (SEGMENT.check1) //color_vertical / color bars toggle
+ if (SEGMENT.check1) //color_vertical / color bars toggle
colorIndex = map(y, 0, rows-1, 0, 255);
ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0);
@@ -7302,6 +7310,61 @@ uint16_t mode_2DAkemi(void) {
return FRAMETIME;
} // mode_2DAkemi
static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette;2f;si=0"; //beatsin
+
+
+// Distortion waves - ldirko
+// https://editor.soulmatelights.com/gallery/1089-distorsion-waves
+// apated for WLD by @blazoncek
+uint16_t mode_2Ddistortionwaves() {
+ if (!strip.isMatrix) return mode_static(); // not a 2D set-up
+
+ const uint16_t cols = SEGMENT.virtualWidth();
+ const uint16_t rows = SEGMENT.virtualHeight();
+
+ uint8_t speed = SEGMENT.speed/32;
+ uint8_t scale = SEGMENT.intensity/32;
+
+ uint8_t w = 2;
+
+ uint16_t a = millis()/32;
+ uint16_t a2 = a/2;
+ uint16_t a3 = a/3;
+
+ uint16_t cx = beatsin8(10-speed,0,cols-1)*scale;
+ uint16_t cy = beatsin8(12-speed,0,rows-1)*scale;
+ uint16_t cx1 = beatsin8(13-speed,0,cols-1)*scale;
+ uint16_t cy1 = beatsin8(15-speed,0,rows-1)*scale;
+ uint16_t cx2 = beatsin8(17-speed,0,cols-1)*scale;
+ uint16_t cy2 = beatsin8(14-speed,0,rows-1)*scale;
+
+ uint16_t xoffs = 0;
+ for (int x = 0; x < cols; x++) {
+ xoffs += scale;
+ uint16_t yoffs = 0;
+
+ for (int y = 0; y < rows; y++) {
+ yoffs += scale;
+
+ byte rdistort = cos8((cos8(((x<<3)+a )&255)+cos8(((y<<3)-a2)&255)+a3 )&255)>>1;
+ byte gdistort = cos8((cos8(((x<<3)-a2)&255)+cos8(((y<<3)+a3)&255)+a+32 )&255)>>1;
+ byte bdistort = cos8((cos8(((x<<3)+a3)&255)+cos8(((y<<3)-a) &255)+a2+64)&255)>>1;
+
+ byte valueR = rdistort+ w* (a- ( ((xoffs - cx) * (xoffs - cx) + (yoffs - cy) * (yoffs - cy))>>7 ));
+ byte valueG = gdistort+ w* (a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 ));
+ byte valueB = bdistort+ w* (a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 ));
+
+ valueR = gamma8(cos8(valueR));
+ valueG = gamma8(cos8(valueG));
+ valueB = gamma8(cos8(valueB));
+
+ SEGMENT.setPixelColorXY(x, y, RGBW32(valueR, valueG, valueB, 0));
+ }
+ }
+
+ return FRAMETIME;
+}
+static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2;";
+
#endif // WLED_DISABLE_2D
@@ -7385,7 +7448,7 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_FIRE_FLICKER, &mode_fire_flicker, _data_FX_MODE_FIRE_FLICKER);
addEffect(FX_MODE_GRADIENT, &mode_gradient, _data_FX_MODE_GRADIENT);
addEffect(FX_MODE_LOADING, &mode_loading, _data_FX_MODE_LOADING);
-
+
addEffect(FX_MODE_FAIRY, &mode_fairy, _data_FX_MODE_FAIRY);
addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
addEffect(FX_MODE_FAIRYTWINKLE, &mode_fairytwinkle, _data_FX_MODE_FAIRYTWINKLE);
@@ -7500,6 +7563,7 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_2DBLOBS, &mode_2Dfloatingblobs, _data_FX_MODE_2DBLOBS);
addEffect(FX_MODE_2DSCROLLTEXT, &mode_2Dscrollingtext, _data_FX_MODE_2DSCROLLTEXT);
addEffect(FX_MODE_2DDRIFTROSE, &mode_2Ddriftrose, _data_FX_MODE_2DDRIFTROSE);
+ addEffect(FX_MODE_2DDISTORTIONWAVES, &mode_2Ddistortionwaves, _data_FX_MODE_2DDISTORTIONWAVES);
addEffect(FX_MODE_2DGEQ, &mode_2DGEQ, _data_FX_MODE_2DGEQ); // audio
@@ -7537,5 +7601,5 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio
#endif // WLED_DISABLE_2D
-
+
}
diff --git a/wled00/FX.h b/wled00/FX.h
index 60b1473c..5de4aff5 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -250,6 +250,7 @@
#define FX_MODE_2DBLOBS 121 //gap fill
#define FX_MODE_2DSCROLLTEXT 122 //gap fill
#define FX_MODE_2DDRIFTROSE 123 //gap fill
+#define FX_MODE_2DDISTORTIONWAVES 124
// WLED-SR effects (SR compatible IDs !!!)
#define FX_MODE_PIXELS 128
@@ -504,6 +505,7 @@ typedef struct Segment {
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
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
void setCCT(uint16_t k);
void setOpacity(uint8_t o);
@@ -518,9 +520,9 @@ typedef struct Segment {
bool allocateData(size_t len);
void deallocateData(void);
void resetIfRequired(void);
- /**
+ /**
* 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.
* Safe to call from interrupts and network requests.
*/
@@ -587,6 +589,7 @@ typedef struct Segment {
void moveX(int8_t delta);
void moveY(int8_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 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
@@ -640,7 +643,7 @@ class WS2812FX { // 96 bytes
} mode_data_t;
static WS2812FX* instance;
-
+
public:
WS2812FX() :
@@ -654,12 +657,8 @@ class WS2812FX { // 96 bytes
timebase(0),
isMatrix(false),
#ifndef WLED_DISABLE_2D
- hPanels(1),
- vPanels(1),
- panelH(8),
- panelW(8),
+ panels(1),
matrix{0,0,0,0},
- panel{{0,0,0,0}},
#endif
// semi-private (just obscured) used in effect functions through macros
_currentPalette(CRGBPalette16(CRGB::Black)),
@@ -696,6 +695,9 @@ class WS2812FX { // 96 bytes
_mode.clear();
_modeData.clear();
_segments.clear();
+#ifndef WLED_DISABLE_2D
+ panel.clear();
+#endif
customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds);
}
@@ -808,22 +810,31 @@ class WS2812FX { // 96 bytes
#ifndef WLED_DISABLE_2D
#define WLED_MAX_PANELS 64
uint8_t
- hPanels,
- vPanels;
+ panels;
- uint16_t
- panelH,
- panelW;
+ struct {
+ bool bottomStart : 1;
+ bool rightStart : 1;
+ bool vertical : 1;
+ bool serpentine : 1;
+ } matrix;
- typedef struct panel_bitfield_t {
- bool bottomStart : 1; // starts at bottom?
- bool rightStart : 1; // starts on right?
- bool vertical : 1; // is vertical?
- bool serpentine : 1; // is serpentine?
+ typedef struct panel_t {
+ uint16_t xOffset; // x offset relative to the top left of matrix in LEDs
+ uint16_t yOffset; // y offset relative to the top left of matrix in LEDs
+ uint8_t width; // width of the panel
+ 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
- matrix,
- panel[WLED_MAX_PANELS];
+ std::vector panel;
#endif
void
@@ -876,9 +887,9 @@ class WS2812FX { // 96 bytes
uint16_t* customMappingTable;
uint16_t customMappingSize;
-
+
uint32_t _lastShow;
-
+
uint8_t _segment_index;
uint8_t _mainSegment;
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 42d2dde0..17882fe8 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -1,6 +1,6 @@
/*
FX_2Dfcn.cpp contains all 2D utility functions
-
+
LICENSE
The MIT License (MIT)
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
if (isMatrix) {
- Segment::maxWidth = hPanels * panelW;
- Segment::maxHeight = vPanels * panelH;
+ // calculate width dynamically because it will have gaps
+ 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
- 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::maxHeight = 1;
- isMatrix = false;
+ panels = 0;
+ panel.clear(); // release memory allocated by panels
return;
}
- customMappingSize = Segment::maxWidth * Segment::maxHeight;
- customMappingTable = new uint16_t[customMappingSize];
+ customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight];
if (customMappingTable != nullptr) {
- uint16_t startL; // index in custom mapping array (logical strip)
- 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;
+ customMappingSize = Segment::maxWidth * Segment::maxHeight;
- for (uint8_t j=0, p=0; j= customMappingSize) return; // customMappingSize is always W * H of matrix in 2D setup
+ if (index >= customMappingSize) return;
#else
uint16_t index = x;
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;
uint8_t _bri_t = currentBri(on ? opacity : 0);
+ if (!_bri_t && !transitional) return;
if (_bri_t < 255) {
byte r = scale8(R(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) {
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
@@ -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) {
+ // Bresenham’s 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
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
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 &&
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 &&
int16_t(cx)+x= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int16_t dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2;
for (;;) {
- addPixelColorXY(x0,y0,c);
+ setPixelColorXY(x0,y0,c);
if (x0==x1 && y0==y1) break;
e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 99284439..147f625b 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -174,18 +174,18 @@ void Segment::deallocateData() {
_dataLen = 0;
}
-/**
+/**
* If reset of this segment was requested, clears runtime
* settings of this segment.
* 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.
*/
void Segment::resetIfRequired() {
if (reset) {
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
//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);
}
}
@@ -211,6 +211,7 @@ void Segment::setUpLeds() {
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment
static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR);
+ static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK));
byte tcp[72];
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;
@@ -230,16 +231,29 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) {
case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break;
- case 1: //periodically replace palette with a random one. Doesn't work with multiple FastLED segments
- if (millis() - _lastPaletteChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) {
+ case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
+ uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
+ if (timeSinceLastChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) {
+ prevRandomPalette = randomPalette;
randomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)));
_lastPaletteChange = millis();
+ timeSinceLastChange = 0;
}
- 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; i1) 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
if (slot >= NUM_COLORS || c == colors[slot]) return false;
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
if (loadDefaults) {
int16_t sOpt;
- sOpt = extractModeDefaults(fx, "sx"); if (sOpt >= 0) speed = sOpt;
- sOpt = extractModeDefaults(fx, "ix"); if (sOpt >= 0) intensity = sOpt;
- sOpt = extractModeDefaults(fx, "c1"); if (sOpt >= 0) custom1 = sOpt;
- sOpt = extractModeDefaults(fx, "c2"); if (sOpt >= 0) custom2 = sOpt;
- sOpt = extractModeDefaults(fx, "c3"); if (sOpt >= 0) custom3 = sOpt;
+ sOpt = extractModeDefaults(fx, "sx"); speed = (sOpt >= 0) ? sOpt : DEFAULT_SPEED;
+ sOpt = extractModeDefaults(fx, "ix"); intensity = (sOpt >= 0) ? sOpt : DEFAULT_INTENSITY;
+ sOpt = extractModeDefaults(fx, "c1"); custom1 = (sOpt >= 0) ? sOpt : DEFAULT_C1;
+ sOpt = extractModeDefaults(fx, "c2"); custom2 = (sOpt >= 0) ? sOpt : DEFAULT_C2;
+ 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, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 7);
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, "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, "pal"); if (sOpt >= 0) setPalette(sOpt);
+ sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
}
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);
setPixelColorXY(x, y, col);
}
+ // Bresenham’s 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;
case M12_pCorner:
@@ -546,6 +613,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0);
+ if (!_bri_t && !transitional) return;
if (_bri_t < 255) {
byte r = scale8(R(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);
if (indexSet >= start && indexSet < stop) {
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
if (indexMir >= stop) indexMir -= len; // wrap
strip.setPixelColor(indexMir, col);
@@ -689,16 +757,16 @@ void Segment::refreshLightCapabilities() {
if (bus->getStart() + bus->getLength() <= start) continue;
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 (!cctFromRgb) {
switch (type) {
case TYPE_ANALOG_5CH:
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();
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)
@@ -858,7 +926,7 @@ uint8_t Segment::get_random_wheel_index(uint8_t pos) {
* 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 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 pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
* @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 defNumCounts = ((sizeof defCounts) / (sizeof defCounts[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]};
uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
prevLen += count;
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
}
+ 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
if (Segment::_globalLeds) {
purgeSegments(true);
@@ -1001,7 +1074,7 @@ void WS2812FX::service() {
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
delay = (*_mode[seg.currentMode(seg.mode)])();
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();
}
@@ -1042,7 +1115,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
//Stay safe with high amperage and have a reasonable safety margin!
//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)
//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;
powerSum *= _brightness;
-
+
if (powerSum > powerBudget) //scale brightness down to stay in current limit
{
float scale = (float)powerBudget / (float)powerSum;
@@ -1125,7 +1198,7 @@ void WS2812FX::show(void) {
if (callback) callback();
estimateCurrentAndLimitBri();
-
+
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
@@ -1162,7 +1235,7 @@ void WS2812FX::setTargetFps(uint8_t fps) {
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (segid >= _segments.size()) return;
-
+
if (m >= getModeCount()) m = getModeCount() - 1;
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) {
if (n >= _segments.size()) return;
- Segment& seg = _segments[n];
-
- //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();
+ _segments[n].set(i1, i2, grouping, spacing, offset, startY, stopY);
}
void WS2812FX::restartRuntime() {
@@ -1511,15 +1537,28 @@ void WS2812FX::loadCustomPalettes()
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
JsonArray pal = pDoc[F("palette")];
- if (!pal.isNull() && pal.size()>7) { // not an empty palette (at least 2 entries)
- size_t palSize = MIN(pal.size(), 72);
- palSize -= palSize % 4; // make sure size is multiple of 4
- for (size_t i=0; i()<256; i+=4) {
- tcp[ i ] = (uint8_t) pal[ i ].as(); // index
- tcp[i+1] = (uint8_t) pal[i+1].as(); // R
- tcp[i+2] = (uint8_t) pal[i+2].as(); // G
- tcp[i+3] = (uint8_t) pal[i+3].as(); // 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]));
+ if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries)
+ if (pal[0].is() && pal[1].is()) {
+ // we have an array of index & hex strings
+ size_t palSize = MIN(pal.size(), 36);
+ palSize -= palSize % 2; // make sure size is multiple of 2
+ for (size_t i=0, j=0; i()<256; i+=2, j+=4) {
+ uint8_t rgbw[] = {0,0,0,0};
+ tcp[ j ] = (uint8_t) pal[ i ].as(); // index
+ colorFromHexString(rgbw, pal[i+1].as()); // 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()<256; i+=4) {
+ tcp[ i ] = (uint8_t) pal[ i ].as(); // index
+ tcp[i+1] = (uint8_t) pal[i+1].as(); // R
+ tcp[i+2] = (uint8_t) pal[i+2].as(); // G
+ tcp[i+3] = (uint8_t) pal[i+3].as(); // 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));
}
diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp
index 3b7e8052..c122402a 100644
--- a/wled00/alexa.cpp
+++ b/wled00/alexa.cpp
@@ -2,7 +2,7 @@
/*
* Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa.
- *
+ *
* This was put together from these two excellent projects:
* https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch
* https://github.com/probonopd/ESP8266HueEmulator
@@ -21,11 +21,11 @@ void alexaInit()
espalexaDevice = new EspalexaDevice(alexaInvocationName, onAlexaChange, EspalexaDeviceType::extendedcolor);
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
if (alexaNumPresets) {
String name = "";
- for (byte presetIndex = 1; presetIndex <= alexaNumPresets; presetIndex++)
+ for (byte presetIndex = 1; presetIndex <= alexaNumPresets; presetIndex++)
{
if (!getPresetName(presetIndex, name)) break; // no more presets
EspalexaDevice* dev = new EspalexaDevice(name.c_str(), onAlexaChange, EspalexaDeviceType::extendedcolor);
@@ -44,7 +44,7 @@ void handleAlexa()
void onAlexaChange(EspalexaDevice* dev)
{
EspalexaDeviceProperty m = dev->getLastChangedProperty();
-
+
if (m == EspalexaDeviceProperty::on)
{
if (dev->getId() == 0) // Device 0 is for on/off or macros
@@ -56,7 +56,7 @@ void onAlexaChange(EspalexaDevice* dev)
bri = briLast;
stateUpdated(CALL_MODE_ALEXA);
}
- } else
+ } else
{
applyPreset(macroAlexaOn, CALL_MODE_ALEXA);
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;
stateUpdated(CALL_MODE_ALEXA);
}
- } else
+ } else
{
applyPreset(macroAlexaOff, CALL_MODE_ALEXA);
// below for loop stops Alexa from complaining if macroAlexaOff does not actually turn off
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index 5f604321..8250695f 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -217,7 +217,7 @@ class Bus {
static uint8_t _gAWM; // definition in FX_fcn.cpp
static int16_t _cct; // definition in FX_fcn.cpp
static uint8_t _cctBlend; // definition in FX_fcn.cpp
-
+
uint32_t autoWhiteCalc(uint32_t c) {
uint8_t aWM = _autoWhiteMode;
if (_gAWM < 255) aWM = _gAWM;
@@ -271,7 +271,7 @@ class BusDigital : public Bus {
//Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN
if (_bri == 0 && b > 0) {
- if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
+ if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
}
#endif
Bus::setBrightness(b);
@@ -343,7 +343,7 @@ class BusDigital : public Bus {
cleanup();
}
- private:
+ private:
uint8_t _colorOrder = COL_ORDER_GRB;
uint8_t _pins[2] = {255, 255};
uint8_t _iType = I_NONE;
@@ -477,7 +477,7 @@ class BusPwm : public Bus {
cleanup();
}
- private:
+ private:
uint8_t _pins[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {0};
#ifdef ARDUINO_ARCH_ESP32
@@ -553,7 +553,7 @@ class BusOnOff : public Bus {
cleanup();
}
- private:
+ private:
uint8_t _pin = 255;
uint8_t _data = 0;
};
@@ -684,9 +684,24 @@ class BusManager {
if (type == 44 || type == 45) return len*4; //RGBW
return len*3; //RGB
}
-
+
+/*
+ int add(BusConfig &bc);
+ void removeAll(); //do not call this method from system context (network callback)
+ void setStatusPixel(uint32_t c);
+ void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
+ void setBrightness(uint8_t b);
+ void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
+ uint32_t getPixelColor(uint16_t pix);
+ bool canAllShow();
+ Bus* getBus(uint8_t busNr);
+ void show();
+ uint16_t getTotalLength(); //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
+*/
+ // the following functions are inlined by compiler since they are defined within class definition
+ // they should be placed in cpp file instead
int add(BusConfig &bc) {
- if (numBusses >= WLED_MAX_BUSSES) return -1;
+ if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
busses[numBusses] = new BusNetwork(bc);
} else if (IS_DIGITAL(bc.type)) {
@@ -702,7 +717,7 @@ class BusManager {
//do not call this method from system context (network callback)
void removeAll() {
DEBUG_PRINTLN(F("Removing all."));
- //prevents crashes due to deleting busses while in use.
+ //prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield();
for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
numBusses = 0;
@@ -714,11 +729,11 @@ class BusManager {
}
}
- void setStatusPixel(uint32_t c) {
+ void setStatusPixel(uint32_t c) {
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) {
for (uint8_t i = 0; i < numBusses; i++) {
@@ -766,10 +781,6 @@ class BusManager {
return busses[busNr];
}
- inline uint8_t getNumBusses() {
- return numBusses;
- }
-
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t getTotalLength() {
uint16_t len = 0;
@@ -777,17 +788,27 @@ class BusManager {
return len;
}
- void updateColorOrderMap(const ColorOrderMap &com) {
+ inline void updateColorOrderMap(const ColorOrderMap &com) {
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
}
- const ColorOrderMap& getColorOrderMap() const {
+ inline const ColorOrderMap& getColorOrderMap() const {
return colorOrderMap;
}
+ inline uint8_t getNumBusses() {
+ return numBusses;
+ }
+
private:
uint8_t numBusses = 0;
- Bus* busses[WLED_MAX_BUSSES];
+ Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap colorOrderMap;
+
+ inline uint8_t getNumVirtualBusses() {
+ int j = 0;
+ for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++;
+ return j;
+ }
};
#endif
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index 1c2468d6..323f75af 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -699,7 +699,7 @@ class PolyBus {
}
};
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) {
case I_NONE: break;
#ifdef ESP8266
@@ -771,7 +771,7 @@ class PolyBus {
case I_HS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
case I_SS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
}
-
+
// upper nibble contains W swap information
uint8_t w = col.W;
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) {
if (!IS_DIGITAL(busType)) return I_NONE;
if (IS_2PIN(busType)) { //SPI LED chips
diff --git a/wled00/button.cpp b/wled00/button.cpp
index b34e3c38..fce21424 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -24,12 +24,14 @@ void shortPressAction(uint8_t b)
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
}
+#ifndef WLED_DISABLE_MQTT
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "short");
}
+#endif
}
void longPressAction(uint8_t b)
@@ -43,12 +45,14 @@ void longPressAction(uint8_t b)
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
}
+#ifndef WLED_DISABLE_MQTT
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "long");
}
+#endif
}
void doublePressAction(uint8_t b)
@@ -62,12 +66,14 @@ void doublePressAction(uint8_t b)
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
}
+#ifndef WLED_DISABLE_MQTT
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "double");
}
+#endif
}
bool isButtonPressed(uint8_t i)
@@ -105,20 +111,21 @@ void handleSwitch(uint8_t b)
}
if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
-
+
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (!buttonPressedBefore[b]) { // on -> off
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
else { //turn on
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
- }
+ }
} else { // off -> on
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
else { //turn off
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
- }
+ }
}
+#ifndef WLED_DISABLE_MQTT
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
@@ -126,13 +133,14 @@ void handleSwitch(uint8_t b)
else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, !buttonPressedBefore[b] ? "off" : "on");
}
+#endif
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
}
}
#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_SENSITIVITY 4 // changes below this amount are noise (POT scratching, or ADC noise)
@@ -165,7 +173,7 @@ void handleAnalog(uint8_t b)
//while(strip.isUpdating() && (millis() - wait_started < STRIP_WAIT_TIME)) {
// delay(1);
//}
- //if (strip.isUpdating()) return; // give up
+ //if (strip.isUpdating()) return; // give up
oldRead[b] = aRead;
@@ -326,7 +334,7 @@ void esp32RMTInvertIdle()
void handleIO()
{
handleButton();
-
+
//set relay when LEDs turn on
if (strip.getBrightness())
{
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index d429f716..9a8e7fbf 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -64,7 +64,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (apHide > 1) apHide = 1;
CJSON(apBehavior, ap[F("behav")]);
-
+
/*
JsonArray ap_ip = ap["ip"];
for (byte i = 0; i < 4; i++) {
@@ -97,32 +97,38 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject matrix = hw_led[F("matrix")];
if (!matrix.isNull()) {
strip.isMatrix = true;
- CJSON(strip.panelH, matrix[F("ph")]);
- CJSON(strip.panelW, matrix[F("pw")]);
- CJSON(strip.hPanels, matrix[F("mph")]);
- CJSON(strip.vPanels, matrix[F("mpv")]);
+ CJSON(strip.panels, matrix[F("mpc")]);
CJSON(strip.matrix.bottomStart, matrix[F("pb")]);
CJSON(strip.matrix.rightStart, matrix[F("pr")]);
CJSON(strip.matrix.vertical, matrix[F("pv")]);
CJSON(strip.matrix.serpentine, matrix["ps"]);
+ strip.panel.clear();
JsonArray panels = matrix[F("panels")];
uint8_t s = 0;
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) {
- CJSON(strip.panel[s].bottomStart, pnl["b"]);
- CJSON(strip.panel[s].rightStart, pnl["r"]);
- CJSON(strip.panel[s].vertical, pnl["v"]);
- CJSON(strip.panel[s].serpentine, pnl["s"]);
- if (++s >= WLED_MAX_PANELS) break; // max panels reached
+ WS2812FX::Panel p;
+ CJSON(p.bottomStart, pnl["b"]);
+ CJSON(p.rightStart, pnl["r"]);
+ CJSON(p.vertical, pnl["v"]);
+ 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
}
- }
- // clear remaining panels
- for (; s= WLED_MAX_BUSSES) break;
+ if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm["pin"];
if (pinArr.size() == 0) continue;
@@ -161,7 +167,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
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 {
if (busConfigs[s] != nullptr) delete busConfigs[s];
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
JsonObject btn_obj = hw["btn"];
- int pull = -1; // trick for inverted setting
- CJSON(pull, btn_obj[F("pull")]);
- if (pull>=0) disablePullUp = pull;
+ bool pull = btn_obj[F("pull")] | (!disablePullUp); // if true, pullup is enabled
+ disablePullUp = !pull;
JsonArray hw_btn_ins = btn_obj[F("ins")];
if (!hw_btn_ins.isNull()) {
uint8_t s = 0;
@@ -205,14 +210,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
btnPin[s] = pin;
#ifdef ARDUINO_ARCH_ESP32
// 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
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s);
btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button);
- }
- else
+ }
+ else
#endif
{
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.
uint8_t s = 0;
if (pinManager.allocatePin(btnPin[0], false, PinOwner::Button)) { // initialized to #define value BTNPIN, or zero if not defined(!)
- ++s; // do not clear default button if allocated successfully
+ ++s; // do not clear default button if allocated successfully
}
for (; s 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(timerMinutes[it], timer["min"]);
CJSON(timerMacro[it], timer["macro"]);
@@ -704,22 +709,23 @@ void serializeConfig() {
// 2D Matrix Settings
if (strip.isMatrix) {
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
- matrix[F("ph")] = strip.panelH;
- matrix[F("pw")] = strip.panelW;
- matrix[F("mph")] = strip.hPanels;
- matrix[F("mpv")] = strip.vPanels;
+ matrix[F("mpc")] = strip.panels;
matrix[F("pb")] = strip.matrix.bottomStart;
matrix[F("pr")] = strip.matrix.rightStart;
matrix[F("pv")] = strip.matrix.vertical;
matrix["ps"] = strip.matrix.serpentine;
JsonArray panels = matrix.createNestedArray(F("panels"));
- for (uint8_t i=0; i 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
#ifndef WLED_MAX_BUTTONS
@@ -79,7 +98,7 @@
#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h"
#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h"
#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h"
-#define USERMOD_ID_BATTERY_STATUS_BASIC 18 //Usermod "usermod_v2_battery_status_basic.h"
+#define USERMOD_ID_BATTERY 18 //Usermod "usermod_v2_battery.h"
#define USERMOD_ID_PWM_FAN 19 //Usermod "usermod_PWM_fan.h"
#define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h"
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
@@ -150,13 +169,13 @@
#define DMX_MODE_DISABLED 0 //not used
#define DMX_MODE_SINGLE_RGB 1 //all LEDs same RGB color (3 channels)
#define DMX_MODE_SINGLE_DRGB 2 //all LEDs same RGB color and master dimmer (4 channels)
-#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (11 channels)
-#define DMX_MODE_EFFECT_W 7 //trigger standalone effects of WLED (13 channels)
+#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (15 channels)
+#define DMX_MODE_EFFECT_W 7 //trigger standalone effects of WLED (18 channels)
#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels)
#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels)
#define DMX_MODE_MULTIPLE_RGBW 6 //every LED is addressed with its own RGBW (ledCount * 4 channels)
-#define DMX_MODE_EFFECT_SEGMENT 8 //trigger standalone effects of WLED (11 channels per segement)
-#define DMX_MODE_EFFECT_SEGMENT_W 9 //trigger standalone effects of WLED (13 channels per segement)
+#define DMX_MODE_EFFECT_SEGMENT 8 //trigger standalone effects of WLED (15 channels per segement)
+#define DMX_MODE_EFFECT_SEGMENT_W 9 //trigger standalone effects of WLED (18 channels per segement)
#define DMX_MODE_PRESET 10 //apply presets (1 channel)
//Light capability byte (unused) 0bRCCCTTTT
@@ -294,7 +313,7 @@
#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
#ifdef ESP8266
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
@@ -323,7 +342,7 @@
#ifdef ESP8266
#define SETTINGS_STACK_BUF_SIZE 2048
#else
-#define SETTINGS_STACK_BUF_SIZE 3096
+#define SETTINGS_STACK_BUF_SIZE 3096
#endif
#ifdef WLED_USE_ETHERNET
diff --git a/wled00/data/index.css b/wled00/data/index.css
index 843d478f..9a2e2642 100644
--- a/wled00/data/index.css
+++ b/wled00/data/index.css
@@ -436,6 +436,9 @@ button {
left: 50%;
margin-left: -92px;
+ /* Ensure tooltip goes away when mouse leaves control */
+ pointer-events: none;
+
/* Fade in tooltip */
opacity: 0;
transition: opacity 0.75s;
@@ -865,8 +868,8 @@ select {
transition-duration: 0.5s;
-webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0);
- -webkit-appearance: none;
- -moz-appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
backface-visibility: hidden;
transform:translate3d(0,0,0);
text-overflow: ellipsis;
@@ -890,8 +893,8 @@ div.sel-p:after {
position: absolute;
right: 10px;
top: 22px;
- width: 0;
- height: 0;
+ width: 0;
+ height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid var(--c-f);
@@ -1221,7 +1224,7 @@ TD .checkmark, TD .radiomark {
line-height: 24px;
vertical-align: middle;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
- filter: grayscale(100%);
+ filter: grayscale(100%);
}
.lbl-l {
@@ -1331,7 +1334,7 @@ TD .checkmark, TD .radiomark {
white-space: nowrap;
text-overflow: ellipsis;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
- filter: grayscale(100%);
+ filter: grayscale(100%);
}
/* list item palette preview */
@@ -1362,7 +1365,7 @@ TD .checkmark, TD .radiomark {
background: var(--c-2);
border: 1px solid var(--c-3);
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
- filter: grayscale(100%);
+ filter: grayscale(100%);
}
.fnd input[type="text"]:focus {
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 1ba88f95..131fd122 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -273,9 +273,9 @@ function onLoad()
function updateTablinks(tabI)
{
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;
- tablinks[tabI].classList.add("active");
+ tablinks[tabI].classList.add('active');
}
function openTab(tabI, force = false)
@@ -291,13 +291,13 @@ var timeout;
function showToast(text, error = false)
{
if (error) gId('connind').style.backgroundColor = "var(--c-r)";
- var x = gId("toast");
+ var x = gId('toast');
//if (error) text += '';
x.innerHTML = text;
- x.classList.add(error ? "error":"show");
+ x.classList.add(error ? 'error':'show');
clearTimeout(timeout);
x.style.animation = 'none';
- timeout = setTimeout(()=>{ x.classList.remove("show"); }, 2900);
+ timeout = setTimeout(()=>{ x.classList.remove('show'); }, 2900);
if (error) console.log(text);
}
@@ -308,12 +308,12 @@ function showErrorToast()
function clearErrorToast(n=5000)
{
- var x = gId("toast");
- if (x.classList.contains("error")) {
+ var x = gId('toast');
+ if (x.classList.contains('error')) {
clearTimeout(timeout);
timeout = setTimeout(()=>{
- x.classList.remove("show");
- x.classList.remove("error");
+ x.classList.remove('show');
+ x.classList.remove('error');
}, n);
}
}
@@ -544,8 +544,8 @@ function populateQL()
for (var key of (pQL||[])) {
cn += ``;
}
- gId('pql').classList.add("expanded");
- } else gId('pql').classList.remove("expanded");
+ gId('pql').classList.add('expanded');
+ } else gId('pql').classList.remove('expanded');
gId('pql').innerHTML = cn;
}
@@ -610,8 +610,8 @@ function parseInfo(i) {
mh = i.leds.matrix ? i.leds.matrix.h : 0;
isM = mw>0 && mh>0;
if (!isM) {
- gId("filter1D").classList.add("hide");
- //gId("filter2D").classList.add("hide");
+ gId("filter1D").classList.add('hide');
+ //gId("filter2D").classList.add('hide');
hideModes("2D");
}
// if (i.noaudio) {
@@ -697,7 +697,7 @@ function populateSegments(s)
if (i > lSeg) lSeg = 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 = `
@@ -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) 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:
";
gId("ledmap").innerHTML = cont;
- gId("ledmap").classList.remove("hide");
+ gId("ledmap").classList.remove('hide');
} else {
- gId("ledmap").classList.add("hide");
+ gId("ledmap").classList.add('hide');
}
}
@@ -837,7 +837,7 @@ function populateEffects()
});
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 nm = ef.name+" ";
let fd = "";
@@ -1057,20 +1057,20 @@ function updateLen(s)
let tPL = gId(`seg${s}lbtm`);
if (stop-start>1 && stopY-startY>1) {
// 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}"]`);
if (sE) {
let sN = sE.querySelector(".lstIname").innerText;
let seg = gId(`seg${s}map2D`);
if (seg) {
- if(sN.indexOf("\u25A6")<0) seg.classList.remove("hide"); // unhide mapping for 1D effects (| in name)
- else seg.classList.add("hide"); // hide mapping otherwise
+ 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 {
// 1D segment in 2D set-up
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
}
}
@@ -1102,7 +1102,7 @@ function updatePA()
ps = gEBCN("psts"); for (let p of ps) p.classList.remove('selected');
if (currentPreset > 0) {
var acv = gId(`p${currentPreset}o`);
- if (acv /*&& !acv.classList.contains("expanded")*/) {
+ if (acv /*&& !acv.classList.contains('expanded')*/) {
acv.classList.add('selected');
/*
// scroll selected preset into view (on WS refresh)
@@ -1119,13 +1119,13 @@ function updatePA()
function updateUI()
{
- gId('buttonPower').className = (isOn) ? "active":"";
- gId('buttonNl').className = (nlA) ? "active":"";
- gId('buttonSync').className = (syncSend) ? "active":"";
+ gId('buttonPower').className = (isOn) ? 'active':'';
+ gId('buttonNl').className = (nlA) ? 'active':'';
+ gId('buttonSync').className = (syncSend) ? 'active':'';
showNodes();
- updateSelectedPalette();
updateSelectedFx();
+ updateSelectedPalette(selectedPal); // must be after updateSelectedFx() to un-hide color slots for * palettes
updateTrail(gId('sliderBri'));
updateTrail(gId('sliderSpeed'));
@@ -1159,17 +1159,27 @@ function updateUI()
updatePSliders();
}
-function updateSelectedPalette()
+function updateSelectedPalette(s)
{
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;
var selElement = parent.querySelector('.selected');
if (selElement) selElement.classList.remove('selected');
- var selectedPalette = parent.querySelector(`.lstI[data-id="${selectedPal}"]`);
- if (selectedPalette) parent.querySelector(`.lstI[data-id="${selectedPal}"]`).classList.add('selected');
+ var selectedPalette = parent.querySelector(`.lstI[data-id="${s}"]`);
+ 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()
@@ -1191,9 +1201,9 @@ function updateSelectedFx()
var selectedName = selectedEffect.querySelector(".lstIname").innerText;
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"]`);
- 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;
}
-// WLEDSR: control HTML elements for Slider and Color Control
+// control HTML elements for Slider and Color Control (original ported form WLED-SR)
// Technical notes
// ===============
// 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==1) label.innerHTML = "Effect intensity";
else label.innerHTML = "Custom" + (i-1);
- slider.classList.remove("hide");
+ slider.classList.remove('hide');
} else {
- slider.classList.add("hide");
+ slider.classList.add('hide');
}
}
if (slOnOff.length>5) { // up to 3 checkboxes
@@ -1422,14 +1432,14 @@ function setEffectParameters(idx)
// set html color items on/off
var cslLabel = '';
var sep = '';
- var hide = true;
var cslCnt = 0, oCsel = csel;
for (let i=0; ii && coOnOff[i] != "") {
- btn.style.display = "inline";
- if (coOnOff.length>i && coOnOff[i] != "!") {
+ btn.classList.remove('hide');
+ btn.dataset.hide = 0;
+ if (coOnOff[i] != "!") {
var abbreviation = coOnOff[i].substr(0,2);
btn.innerHTML = abbreviation;
if (abbreviation != coOnOff[i]) {
@@ -1440,17 +1450,18 @@ function setEffectParameters(idx)
else if (i==0) btn.innerHTML = "Fx";
else if (i==1) btn.innerHTML = "Bg";
else btn.innerHTML = "Cs";
- hide = false;
if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one
cslCnt++;
} 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}`;
- hide = false;
if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one
cslCnt++;
} 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;
@@ -1475,12 +1486,13 @@ function setEffectParameters(idx)
palw.style.display = "none";
}
// not all color selectors shown, hide palettes created from color selectors
- for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) {
- let fltr = "* C";
- if (cslCnt==1 && csel==0) fltr = "* Colors";
- 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');
- }
+ // NOTE: this will disallow user to select "* Color ..." palettes which may be undesirable in some cases or for some users
+ //for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) {
+ // let fltr = "* C";
+ // if (cslCnt==1 && csel==0) fltr = "* Colors";
+ // 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;
@@ -1836,7 +1848,7 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
`;
- if (Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
+ if (!isM && Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
content += `
Ledmap:
";
@@ -1949,7 +1961,7 @@ function tglSegn(s)
{
let t = gId(s<100?`seg${s}t`:`p${s-100}txt`);
if (t) {
- t.classList.toggle("show");
+ t.classList.toggle('show');
t.focus();
t.select();
}
@@ -1966,7 +1978,6 @@ function selSegAll(o)
function selSegEx(s)
{
- if (gId('selall')) gId('selall').checked = false;
var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":(i==s)});
obj.mainseg = s;
@@ -1975,7 +1986,6 @@ function selSegEx(s)
function selSeg(s)
{
- if (gId('selall')) gId('selall').checked = false;
var sel = gId(`seg${s}sel`).checked;
var obj = {"seg": {"id": s, "sel": sel}};
requestJson(obj);
@@ -1991,7 +2001,7 @@ function rptSeg(s)
var rev = gId(`seg${s}rev`).checked;
var mi = gId(`seg${s}mi`).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}};
if (gId(`seg${s}grp`)) {
var grp = parseInt(gId(`seg${s}grp`).value);
@@ -2102,7 +2112,7 @@ function setTp(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}};
requestJson(obj);
}
@@ -2296,7 +2306,7 @@ function delP(i) {
requestJson(obj);
delete pJson[i];
populatePresets();
- gId('putil').classList.add("staybot");
+ gId('putil').classList.add('staybot');
} else {
bt.style.color = "var(--c-r)";
bt.innerHTML = "Delete!";
@@ -2571,7 +2581,7 @@ function hideModes(txt)
let f = false;
if (txt==="2D") f = iT.indexOf("\u25A6") >= 0 && iT.indexOf("\u22EE") < 0; // 2D && !1D
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`);
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
if (i >= 100) {
@@ -2666,11 +2676,11 @@ function expand(i)
gId(`p${p}api`).value = papi;
if (papi.indexOf("Please") == 0) gId(`p${p}cstgl`).checked = false;
tglCs(p);
- gId('putil').classList.remove("staybot");
+ gId('putil').classList.remove('staybot');
} else {
updatePA();
gId('seg' +i).innerHTML = "";
- gId('putil').classList.add("staybot");
+ gId('putil').classList.add('staybot');
}
}
diff --git a/wled00/data/settings.htm b/wled00/data/settings.htm
index 3032959d..dc8e3838 100644
--- a/wled00/data/settings.htm
+++ b/wled00/data/settings.htm
@@ -68,9 +68,9 @@
-
+
-
+
diff --git a/wled00/data/settings_2D.htm b/wled00/data/settings_2D.htm
index 94246a85..ffac029d 100644
--- a/wled00/data/settings_2D.htm
+++ b/wled00/data/settings_2D.htm
@@ -8,6 +8,7 @@
@@ -118,10 +150,11 @@ Serpentine: `;
`;
- Serpentine:
-
- A matrix is made of 1 or more physical LED panels of the same dimensions.
- Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).
- Each panel can have different LED orientation and/or starting point and/or layout.
+ Serpentine:
+ Can populate LED panel layout with pre-arranged matrix. These values do not affect final layout.
+
+
Panel set-up
+ Number of panels:
+ A matrix is made of 1 or more physical LED panels.
+
+ Each panel can be of different size and/or have different LED orientation and/or starting point and/or layout.
LED panel layout
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index befcc9a6..20b8de8d 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -6,7 +6,7 @@
LED Settings