Merge branch 'MoonModules:mdev' into Strip_Level_Color_Adjust
This commit is contained in:
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -48,7 +48,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: What version/release of MM WLED?
|
label: What version/release of MM WLED?
|
||||||
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
||||||
placeholder: "e.g. build 2401290, WLEDMM_0.14.1-b30.36_esp32_4MB_M.bin"
|
placeholder: "e.g. build 2401290, WLEDMM_0.14.1-b30.37_esp32_4MB_M.bin"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|||||||
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,5 +1,37 @@
|
|||||||
## [WLED upstream](https://github.com/Aircoookie/WLED/tree/0_14_1) changelog
|
## [WLED upstream](https://github.com/Aircoookie/WLED/tree/0_14_1) changelog
|
||||||
|
|
||||||
|
#### Build 2403290
|
||||||
|
- WLED 0.14.3 release
|
||||||
|
- Fix for transition 0 (#3854, #3832, #3720)
|
||||||
|
|
||||||
|
#### Build 2403170
|
||||||
|
- WLED 0.14.2 release
|
||||||
|
|
||||||
|
#### Build 2403110
|
||||||
|
- Beta WLED 0.14.2-b2
|
||||||
|
- Fixing a potential array bounds violation in ESPDMX
|
||||||
|
- LockedJsonResponse: Release early if possible (by @willmmiles)
|
||||||
|
|
||||||
|
#### Build 2402120
|
||||||
|
- Beta WLED 0.14.2-b1
|
||||||
|
- Possible fix for #3589 & partial fix for #3605
|
||||||
|
- Prevent JSON buffer clear after failed lock attempt
|
||||||
|
- Multiple analog button fix for #3549
|
||||||
|
- UM Audioreactive: add two compiler options (#3732 by @wled-install)
|
||||||
|
- Fix for #3693
|
||||||
|
|
||||||
|
#### Build 2401141
|
||||||
|
- Official release of WLED 0.14.1
|
||||||
|
- Fix for #3566, #3665, #3672
|
||||||
|
- Sorting of palettes in custom palette editor (#3674 by @WoodyLetsCode)
|
||||||
|
|
||||||
|
#### Build 2401060
|
||||||
|
- Version bump: 0.14.1-b3
|
||||||
|
- Global JSON buffer guarding (#3648 by @willmmiles, resolves #3641, #3312, #3367, #3637, #3646, #3447)
|
||||||
|
- Fix for #3632
|
||||||
|
- Custom palette editor mobile UI enhancement (#3617 by @imeszaros)
|
||||||
|
- changelog update
|
||||||
|
|
||||||
#### Build 2312290
|
#### Build 2312290
|
||||||
- Fix for #3622
|
- Fix for #3622
|
||||||
- NB: fix for #3613 #3609 are not needed in MoonModules fork
|
- NB: fix for #3613 #3609 are not needed in MoonModules fork
|
||||||
@@ -61,6 +93,12 @@
|
|||||||
- Improved ESP8266 stability by reducing WebSocket response resends
|
- Improved ESP8266 stability by reducing WebSocket response resends
|
||||||
- Updated ESP8266 core to 3.1.2
|
- Updated ESP8266 core to 3.1.2
|
||||||
|
|
||||||
|
#### Build 2306180
|
||||||
|
|
||||||
|
- Added client-side option for applying effect defaults from metadata
|
||||||
|
- Improved ESP8266 stability by reducing WebSocket response resends
|
||||||
|
- Updated ESP8266 core to 3.1.2
|
||||||
|
|
||||||
#### Build 2306141
|
#### Build 2306141
|
||||||
- Lissajous improvements
|
- Lissajous improvements
|
||||||
- Scrolling Text improvements (leading 0)
|
- Scrolling Text improvements (leading 0)
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
Here are a few suggestions to make it easier for you to contribute!
|
Here are a few suggestions to make it easier for you to contribute!
|
||||||
|
|
||||||
|
### Describe your PR
|
||||||
|
|
||||||
|
Please add a description of your proposed code changes. It does not need to be an exhaustive essay, however a PR with no description or just a few words might not get accepted, simply because very basic information is missing.
|
||||||
|
|
||||||
|
A good description helps us to review and understand your proposed changes. For example, you could say a few words about
|
||||||
|
* what you try to achieve (new feature, fixing a bug, refactoring, security enhancements, etc.)
|
||||||
|
* how your code works (short technical summary - focus on important aspects that might not be obvious when reading the code)
|
||||||
|
* testing you performed, known limitations, open ends you possibly could not solve.
|
||||||
|
* any areas where you like to get help from an experienced maintainer (yes WLEDMM has become big 😉)
|
||||||
|
|
||||||
### Code style
|
### Code style
|
||||||
|
|
||||||
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
|
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
|
||||||
@@ -79,4 +89,5 @@ Good:
|
|||||||
|
|
||||||
There is no set character limit for a comment within a line,
|
There is no set character limit for a comment within a line,
|
||||||
though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window.
|
though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window.
|
||||||
|
|
||||||
Inline comments are OK if they describe that line only and are not exceedingly wide.
|
Inline comments are OK if they describe that line only and are not exceedingly wide.
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "wled",
|
"name": "wled",
|
||||||
"version": "0.14.1-b30.36",
|
"version": "0.14.1-b30.37",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "wled",
|
"name": "wled",
|
||||||
"version": "0.14.1-b30.36",
|
"version": "0.14.1-b30.37",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clean-css": "^4.2.3",
|
"clean-css": "^4.2.3",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "wled",
|
"name": "wled",
|
||||||
"version": "0.14.1-b30.36",
|
"version": "0.14.1-b30.37",
|
||||||
"description": "Tools for WLED project",
|
"description": "Tools for WLED project",
|
||||||
"main": "tools/cdata.js",
|
"main": "tools/cdata.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ arduino_core_2_7_4 = espressif8266@2.6.2
|
|||||||
arduino_core_3_0_0 = espressif8266@3.0.0
|
arduino_core_3_0_0 = espressif8266@3.0.0
|
||||||
arduino_core_3_2_0 = espressif8266@3.2.0
|
arduino_core_3_2_0 = espressif8266@3.2.0
|
||||||
arduino_core_4_1_0 = espressif8266@4.1.0
|
arduino_core_4_1_0 = espressif8266@4.1.0
|
||||||
arduino_core_3_1_2 = espressif8266@4.2.0
|
arduino_core_3_1_2 = espressif8266@4.2.1
|
||||||
|
|
||||||
# Development platforms
|
# Development platforms
|
||||||
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
||||||
@@ -123,8 +123,7 @@ arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/
|
|||||||
platform_wled_default = ${common.arduino_core_3_1_2}
|
platform_wled_default = ${common.arduino_core_3_1_2}
|
||||||
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
|
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
|
||||||
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
|
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
|
||||||
platform_packages = platformio/framework-arduinoespressif8266
|
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
|
||||||
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
|
|
||||||
platformio/tool-esptool #@ ~1.413.0
|
platformio/tool-esptool #@ ~1.413.0
|
||||||
platformio/tool-esptoolpy #@ ~1.30000.0
|
platformio/tool-esptoolpy #@ ~1.30000.0
|
||||||
|
|
||||||
@@ -241,6 +240,7 @@ lib_deps =
|
|||||||
;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections
|
;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections
|
||||||
;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
|
;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
|
||||||
https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit
|
https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit
|
||||||
|
;; https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.2.1
|
||||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||||
#TFT_eSPI
|
#TFT_eSPI
|
||||||
#For compatible OLED display uncomment following
|
#For compatible OLED display uncomment following
|
||||||
@@ -452,6 +452,11 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED
|
|||||||
lib_deps = ${esp8266.lib_deps}
|
lib_deps = ${esp8266.lib_deps}
|
||||||
monitor_filters = esp8266_exception_decoder
|
monitor_filters = esp8266_exception_decoder
|
||||||
|
|
||||||
|
[env:nodemcuv2_160]
|
||||||
|
extends = env:nodemcuv2
|
||||||
|
board_build.f_cpu = 160000000L
|
||||||
|
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
|
||||||
|
|
||||||
[env:esp8266_2m]
|
[env:esp8266_2m]
|
||||||
board = esp_wroom_02
|
board = esp_wroom_02
|
||||||
platform = ${common.platform_wled_default}
|
platform = ${common.platform_wled_default}
|
||||||
@@ -461,6 +466,11 @@ 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
|
||||||
lib_deps = ${esp8266.lib_deps}
|
lib_deps = ${esp8266.lib_deps}
|
||||||
|
|
||||||
|
[env:esp8266_2m_160]
|
||||||
|
extends = env:esp8266_2m
|
||||||
|
board_build.f_cpu = 160000000L
|
||||||
|
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02_160
|
||||||
|
|
||||||
;WLEDMM: see below
|
;WLEDMM: see below
|
||||||
; [env:esp01_1m_full]
|
; [env:esp01_1m_full]
|
||||||
; board = esp01_1m
|
; board = esp01_1m
|
||||||
@@ -471,6 +481,11 @@ lib_deps = ${esp8266.lib_deps}
|
|||||||
; 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_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
|
; ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
|
||||||
; lib_deps = ${esp8266.lib_deps}
|
; lib_deps = ${esp8266.lib_deps}
|
||||||
|
; [env:esp01_1m_full_160]
|
||||||
|
; extends = env:esp01_1m_full
|
||||||
|
; board_build.f_cpu = 160000000L
|
||||||
|
; build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
|
||||||
|
; ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
|
||||||
|
|
||||||
[env:esp07]
|
[env:esp07]
|
||||||
board = esp07
|
board = esp07
|
||||||
@@ -963,6 +978,7 @@ build_flags_S =
|
|||||||
; -D WLED_DISABLE_2D ;; un-comment to build a firmware without 2D matrix support
|
; -D WLED_DISABLE_2D ;; un-comment to build a firmware without 2D matrix support
|
||||||
; -D WLED_USE_CIE_BRIGHTNESS_TABLE ;; experimental: use different color / brightness lookup table
|
; -D WLED_USE_CIE_BRIGHTNESS_TABLE ;; experimental: use different color / brightness lookup table
|
||||||
-D USERMOD_AUDIOREACTIVE
|
-D USERMOD_AUDIOREACTIVE
|
||||||
|
-D USERMOD_AUTO_PLAYLIST
|
||||||
-D UM_AUDIOREACTIVE_USE_NEW_FFT ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra
|
-D UM_AUDIOREACTIVE_USE_NEW_FFT ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra
|
||||||
; -D USERMOD_ARTIFX ;; WLEDMM usermod - temporarily moved into "_M", due to problems in "_S" when compiling with -O2
|
; -D USERMOD_ARTIFX ;; WLEDMM usermod - temporarily moved into "_M", due to problems in "_S" when compiling with -O2
|
||||||
-D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions.
|
-D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <driver/i2s.h>
|
#include <driver/i2s.h>
|
||||||
#include <driver/adc.h>
|
#include <driver/adc.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG))
|
#if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG))
|
||||||
@@ -141,6 +142,8 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann
|
|||||||
static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter)
|
static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter)
|
||||||
static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON)
|
static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON)
|
||||||
|
|
||||||
|
static uint16_t zeroCrossingCount = 0; // number of zero crossings in the current batch of 512 samples
|
||||||
|
|
||||||
// TODO: probably best not used by receive nodes
|
// TODO: probably best not used by receive nodes
|
||||||
static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255
|
static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255
|
||||||
|
|
||||||
@@ -543,15 +546,27 @@ void FFTcode(void * parameter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find highest sample in the batch
|
// set imaginary parts to 0
|
||||||
|
memset(vImag, 0, sizeof(vImag));
|
||||||
|
|
||||||
|
// find highest sample in the batch, and count zero crossings
|
||||||
float maxSample = 0.0f; // max sample from FFT batch
|
float maxSample = 0.0f; // max sample from FFT batch
|
||||||
|
uint_fast16_t newZeroCrossingCount = 0;
|
||||||
for (int i=0; i < samplesFFT; i++) {
|
for (int i=0; i < samplesFFT; i++) {
|
||||||
// set imaginary parts to 0
|
|
||||||
vImag[i] = 0;
|
|
||||||
// pick our our current mic sample - we take the max value from all samples that go into FFT
|
// pick our our current mic sample - we take the max value from all samples that go into FFT
|
||||||
if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts
|
if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts
|
||||||
if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]);
|
if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]);
|
||||||
|
|
||||||
|
// WLED-MM/TroyHacks: Calculate zero crossings
|
||||||
|
//
|
||||||
|
if (i < (samplesFFT-1)) {
|
||||||
|
if (__builtin_signbit(vReal[i]) != __builtin_signbit(vReal[i+1])) // test sign bit: sign changed -> zero crossing
|
||||||
|
newZeroCrossingCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
newZeroCrossingCount = (newZeroCrossingCount*2)/3; // reduce value so it typicially stays below 256
|
||||||
|
zeroCrossingCount = newZeroCrossingCount; // update only once, to avoid that effects pick up an intermediate value
|
||||||
|
|
||||||
// release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function
|
// release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function
|
||||||
// early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results.
|
// early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results.
|
||||||
micDataReal = maxSample;
|
micDataReal = maxSample;
|
||||||
@@ -770,8 +785,7 @@ void FFTcode(void * parameter)
|
|||||||
// run peak detection
|
// run peak detection
|
||||||
autoResetPeak();
|
autoResetPeak();
|
||||||
detectSamplePeak();
|
detectSamplePeak();
|
||||||
|
|
||||||
// we have new results - notify UDP sound send
|
|
||||||
haveNewFFTResult = true;
|
haveNewFFTResult = true;
|
||||||
|
|
||||||
#if !defined(I2S_GRAB_ADC1_COMPLETELY)
|
#if !defined(I2S_GRAB_ADC1_COMPLETELY)
|
||||||
@@ -1007,7 +1021,7 @@ class AudioReactive : public Usermod {
|
|||||||
uint8_t samplePeak; // 01 Bytes offset 16 - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude
|
uint8_t samplePeak; // 01 Bytes offset 16 - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude
|
||||||
uint8_t frameCounter; // 01 Bytes offset 17 - track duplicate/out of order packets
|
uint8_t frameCounter; // 01 Bytes offset 17 - track duplicate/out of order packets
|
||||||
uint8_t fftResult[16]; // 16 Bytes offset 18
|
uint8_t fftResult[16]; // 16 Bytes offset 18
|
||||||
uint8_t gap2[2]; // gap added by compiler: 02 Bytes, offset 34
|
uint16_t zeroCrossingCount; // 02 Bytes, offset 34
|
||||||
float FFT_Magnitude; // 04 Bytes offset 36
|
float FFT_Magnitude; // 04 Bytes offset 36
|
||||||
float FFT_MajorPeak; // 04 Bytes offset 40
|
float FFT_MajorPeak; // 04 Bytes offset 40
|
||||||
};
|
};
|
||||||
@@ -1547,6 +1561,7 @@ class AudioReactive : public Usermod {
|
|||||||
transmitData.samplePeak = udpSamplePeak ? 1:0;
|
transmitData.samplePeak = udpSamplePeak ? 1:0;
|
||||||
udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it
|
udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it
|
||||||
transmitData.frameCounter = frameCounter;
|
transmitData.frameCounter = frameCounter;
|
||||||
|
transmitData.zeroCrossingCount = zeroCrossingCount;
|
||||||
|
|
||||||
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
||||||
transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254);
|
transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254);
|
||||||
@@ -1619,6 +1634,7 @@ class AudioReactive : public Usermod {
|
|||||||
FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
||||||
soundPressure = volumeSmth; // substitute - V2 format does not (yet) include this value
|
soundPressure = volumeSmth; // substitute - V2 format does not (yet) include this value
|
||||||
agcSensitivity = 128.0f; // substitute - V2 format does not (yet) include this value
|
agcSensitivity = 128.0f; // substitute - V2 format does not (yet) include this value
|
||||||
|
zeroCrossingCount = receivedPacket->zeroCrossingCount;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1720,7 +1736,7 @@ class AudioReactive : public Usermod {
|
|||||||
// usermod exchangeable data
|
// usermod exchangeable data
|
||||||
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
|
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
|
||||||
um_data = new um_data_t;
|
um_data = new um_data_t;
|
||||||
um_data->u_size = 11;
|
um_data->u_size = 12;
|
||||||
um_data->u_type = new um_types_t[um_data->u_size];
|
um_data->u_type = new um_types_t[um_data->u_size];
|
||||||
um_data->u_data = new void*[um_data->u_size];
|
um_data->u_data = new void*[um_data->u_size];
|
||||||
um_data->u_data[0] = &volumeSmth; //*used (New)
|
um_data->u_data[0] = &volumeSmth; //*used (New)
|
||||||
@@ -1746,6 +1762,8 @@ class AudioReactive : public Usermod {
|
|||||||
um_data->u_type[9] = UMT_FLOAT;
|
um_data->u_type[9] = UMT_FLOAT;
|
||||||
um_data->u_data[10] = &agcSensitivity; // used (New)
|
um_data->u_data[10] = &agcSensitivity; // used (New)
|
||||||
um_data->u_type[10] = UMT_FLOAT;
|
um_data->u_type[10] = UMT_FLOAT;
|
||||||
|
um_data->u_data[11] = &zeroCrossingCount;
|
||||||
|
um_data->u_type[11] = UMT_UINT16;
|
||||||
#else
|
#else
|
||||||
// ESP8266
|
// ESP8266
|
||||||
// See https://github.com/MoonModules/WLED/pull/60#issuecomment-1666972133 for explanation of these alternative sources of data
|
// See https://github.com/MoonModules/WLED/pull/60#issuecomment-1666972133 for explanation of these alternative sources of data
|
||||||
@@ -1760,6 +1778,8 @@ class AudioReactive : public Usermod {
|
|||||||
um_data->u_type[9] = UMT_FLOAT;
|
um_data->u_type[9] = UMT_FLOAT;
|
||||||
um_data->u_data[10] = &agcSensitivity; // used (New) - dummy value (128 => 50%)
|
um_data->u_data[10] = &agcSensitivity; // used (New) - dummy value (128 => 50%)
|
||||||
um_data->u_type[10] = UMT_FLOAT;
|
um_data->u_type[10] = UMT_FLOAT;
|
||||||
|
um_data->u_data[11] = &zeroCrossingCount;
|
||||||
|
um_data->u_type[11] = UMT_UINT16;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1921,7 +1941,7 @@ class AudioReactive : public Usermod {
|
|||||||
USER_PRINTF("| uint8_t samplePeak offset = %2d size = %2d\n", offsetof(audioSyncPacket, samplePeak), sizeof(data.samplePeak)); // offset 16 size 1
|
USER_PRINTF("| uint8_t samplePeak offset = %2d size = %2d\n", offsetof(audioSyncPacket, samplePeak), sizeof(data.samplePeak)); // offset 16 size 1
|
||||||
USER_PRINTF("| uint8_t frameCounter offset = %2d size = %2d\n", offsetof(audioSyncPacket, frameCounter), sizeof(data.frameCounter)); // offset 17 size 1
|
USER_PRINTF("| uint8_t frameCounter offset = %2d size = %2d\n", offsetof(audioSyncPacket, frameCounter), sizeof(data.frameCounter)); // offset 17 size 1
|
||||||
USER_PRINTF("| uint8_t fftResult[16] offset = %2d size = %2d\n", offsetof(audioSyncPacket, fftResult[0]), sizeof(data.fftResult)); // offset 18 size 16
|
USER_PRINTF("| uint8_t fftResult[16] offset = %2d size = %2d\n", offsetof(audioSyncPacket, fftResult[0]), sizeof(data.fftResult)); // offset 18 size 16
|
||||||
USER_PRINTF("| uint8_t gap2[2] offset = %2d size = %2d\n", offsetof(audioSyncPacket, gap2[0]), sizeof(data.gap2)); // offset 34 size 2
|
USER_PRINTF("| uint16_t zeroCrossingCount offset = %2d size = %2d\n", offsetof(audioSyncPacket, zeroCrossingCount), sizeof(data.zeroCrossingCount)); // offset 34 size 2
|
||||||
USER_PRINTF("| float FFT_Magnitude offset = %2d size = %2d\n", offsetof(audioSyncPacket, FFT_Magnitude), sizeof(data.FFT_Magnitude));// offset 36 size 4
|
USER_PRINTF("| float FFT_Magnitude offset = %2d size = %2d\n", offsetof(audioSyncPacket, FFT_Magnitude), sizeof(data.FFT_Magnitude));// offset 36 size 4
|
||||||
USER_PRINTF("| float FFT_MajorPeak offset = %2d size = %2d\n", offsetof(audioSyncPacket, FFT_MajorPeak), sizeof(data.FFT_MajorPeak));// offset 40 size 4
|
USER_PRINTF("| float FFT_MajorPeak offset = %2d size = %2d\n", offsetof(audioSyncPacket, FFT_MajorPeak), sizeof(data.FFT_MajorPeak));// offset 40 size 4
|
||||||
USER_PRINTLN(); USER_FLUSH();
|
USER_PRINTLN(); USER_FLUSH();
|
||||||
|
|||||||
@@ -667,7 +667,13 @@ class WM8978Source : public I2SSource {
|
|||||||
_wm8978I2cWrite( 1,0b000111110); // Power Management 1 - power off most things, but enable mic bias and I/O tie-off to help mitigate mic leakage.
|
_wm8978I2cWrite( 1,0b000111110); // Power Management 1 - power off most things, but enable mic bias and I/O tie-off to help mitigate mic leakage.
|
||||||
_wm8978I2cWrite( 2,0b110111111); // Power Management 2 - enable output and amp stages (amps may lift signal but it works better on the ADCs)
|
_wm8978I2cWrite( 2,0b110111111); // Power Management 2 - enable output and amp stages (amps may lift signal but it works better on the ADCs)
|
||||||
_wm8978I2cWrite( 3,0b000001100); // Power Management 3 - enable L&R output mixers
|
_wm8978I2cWrite( 3,0b000001100); // Power Management 3 - enable L&R output mixers
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||||
_wm8978I2cWrite( 4,0b001010000); // Audio Interface - standard I2S, 24-bit
|
_wm8978I2cWrite( 4,0b001010000); // Audio Interface - standard I2S, 24-bit
|
||||||
|
#else
|
||||||
|
_wm8978I2cWrite( 4,0b001001000); // Audio Interface - left-justified I2S, 24-bit
|
||||||
|
#endif
|
||||||
|
|
||||||
_wm8978I2cWrite( 6,0b000000000); // Clock generation control - use external mclk
|
_wm8978I2cWrite( 6,0b000000000); // Clock generation control - use external mclk
|
||||||
_wm8978I2cWrite( 7,0b000000100); // Sets sample rate to ~24kHz (only used for internal calculations, not I2S)
|
_wm8978I2cWrite( 7,0b000000100); // Sets sample rate to ~24kHz (only used for internal calculations, not I2S)
|
||||||
_wm8978I2cWrite(14,0b010001000); // 128x ADC oversampling - high pass filter disabled as it kills the bass response
|
_wm8978I2cWrite(14,0b010001000); // 128x ADC oversampling - high pass filter disabled as it kills the bass response
|
||||||
|
|||||||
510
usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
Normal file
510
usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
Normal file
@@ -0,0 +1,510 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef WLED_DEBUG
|
||||||
|
#ifndef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
#define USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "wled.h"
|
||||||
|
|
||||||
|
class AutoPlaylistUsermod : public Usermod {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// experimental parameters by softhack007 - more balanced but need testing
|
||||||
|
const uint_fast32_t MAX_DISTANCE_TRACKER = 184; // maximum accepted distance_tracker
|
||||||
|
const uint_fast32_t ENERGY_SCALE = 1500;
|
||||||
|
const float FILTER_SLOW1 = 0.0075f; // for "slow" energy - was 0.01f
|
||||||
|
const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr - was 0.01f
|
||||||
|
const float FILTER_FAST1 = 0.2f; // for "fast" energy - was 0.10f
|
||||||
|
const float FILTER_FAST2 = 0.25f; // for "fast" lfc / zcr - was 0.10f
|
||||||
|
const float FILTER_VOLUME = 0.03f; // for volumeSmth averaging - takes 8-10sec until "silence"
|
||||||
|
|
||||||
|
bool initDone = false;
|
||||||
|
bool functionality_enabled = false;
|
||||||
|
bool silenceDetected = true;
|
||||||
|
byte ambientPlaylist = 1;
|
||||||
|
byte musicPlaylist = 2;
|
||||||
|
int timeout = 60;
|
||||||
|
bool autoChange = false;
|
||||||
|
byte lastAutoPlaylist = 0;
|
||||||
|
unsigned long lastSoundTime = millis()-(timeout*1000)-100;
|
||||||
|
unsigned long change_timer = millis();
|
||||||
|
unsigned long autochange_timer = millis();
|
||||||
|
float avg_volumeSmth = 0;
|
||||||
|
|
||||||
|
// fftesult de-scaling factors: 2.8f / fftResultPink[]
|
||||||
|
const float fftDeScaler[NUM_GEQ_CHANNELS] = {2.8/2.35, 2.8/1.32, 2.8/1.32, 2.8/1.40, 2.8/1.48, 2.8/1.57, 2.8/1.68, 2.8/1.80, 2.8/1.89, 2.8/1.95, 2.8/2.14, 2.8/2.26, 2.8/2.50, 2.8/2.90, 2.8/4.20, 2.8/6.50};
|
||||||
|
|
||||||
|
uint_fast32_t energy = 0;
|
||||||
|
|
||||||
|
float avg_long_energy = 250;
|
||||||
|
float avg_long_lfc = 1000;
|
||||||
|
float avg_long_zcr = 500;
|
||||||
|
|
||||||
|
float avg_short_energy = 250;
|
||||||
|
float avg_short_lfc = 1000;
|
||||||
|
float avg_short_zcr = 500;
|
||||||
|
|
||||||
|
bool resetFilters = true; // to (re)initialize filters on first run
|
||||||
|
uint_fast32_t vector_energy = 0;
|
||||||
|
uint_fast32_t vector_lfc = 0;
|
||||||
|
uint_fast32_t vector_zcr = 0;
|
||||||
|
|
||||||
|
uint_fast32_t distance = 0;
|
||||||
|
uint_fast32_t distance_tracker = UINT_FAST32_MAX;
|
||||||
|
|
||||||
|
unsigned long lastchange = millis();
|
||||||
|
|
||||||
|
int_fast16_t change_threshold = 50; // arbitrary starting point.
|
||||||
|
uint_fast16_t change_threshold_change = 0;
|
||||||
|
|
||||||
|
int change_lockout = 1000; // never change below this number of millis. Ideally 60000/your_average_bpm*beats_to_skip = change_lockout (1000 = skip 2 beats at 120bpm)
|
||||||
|
int ideal_change_min = 10000; // ideally change patterns no less than this number of millis
|
||||||
|
int ideal_change_max = 20000; // ideally change patterns no more than this number of millis
|
||||||
|
|
||||||
|
std::vector<int> autoChangeIds;
|
||||||
|
|
||||||
|
static const char _name[];
|
||||||
|
static const char _autoPlaylistEnabled[];
|
||||||
|
static const char _ambientPlaylist[];
|
||||||
|
static const char _musicPlaylist[];
|
||||||
|
static const char _timeout[];
|
||||||
|
static const char _autoChange[];
|
||||||
|
static const char _change_lockout[];
|
||||||
|
static const char _ideal_change_min[];
|
||||||
|
static const char _ideal_change_max[];
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AutoPlaylistUsermod(bool enabled):Usermod("AutoPlaylist", enabled) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets called once at boot. Do all initialization that doesn't depend on
|
||||||
|
// network here
|
||||||
|
void setup() {
|
||||||
|
USER_PRINTLN("AutoPlaylistUsermod");
|
||||||
|
initDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||||
|
// interfaces here
|
||||||
|
void connected() {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void change(um_data_t *um_data) {
|
||||||
|
|
||||||
|
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
|
||||||
|
|
||||||
|
energy = 0;
|
||||||
|
|
||||||
|
for (int i=0; i < NUM_GEQ_CHANNELS; i++) {
|
||||||
|
|
||||||
|
// make an attempt to undo some "trying to look better" FFT manglings in AudioReactive postProcessFFTResults()
|
||||||
|
|
||||||
|
float amplitude = float(fftResult[i]) * fftDeScaler[i]; // undo "pink noise" scaling
|
||||||
|
amplitude /= 0.85f + (float(i)/4.5f); // undo extra up-scaling for high frequencies
|
||||||
|
energy += roundf(amplitude * amplitude); // calc energy from amplitude
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
energy /= ENERGY_SCALE; // scale down so we get 0 sometimes
|
||||||
|
|
||||||
|
uint16_t lfc = float(fftResult[0]) * fftDeScaler[0] / 0.85f; // might as well undo pink noise here too.
|
||||||
|
uint16_t zcr = *(uint16_t*)um_data->u_data[11];
|
||||||
|
|
||||||
|
// WLED-MM/TroyHacks: Calculate the long- and short-running averages
|
||||||
|
// and the individual vector distances.
|
||||||
|
|
||||||
|
if (volumeSmth > 1.0f) {
|
||||||
|
|
||||||
|
// initialize filters on first run
|
||||||
|
if (resetFilters) {
|
||||||
|
avg_short_energy = avg_long_energy = energy;
|
||||||
|
avg_short_lfc = avg_long_lfc = lfc;
|
||||||
|
avg_short_zcr = avg_long_zcr = zcr;
|
||||||
|
resetFilters = false;
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTLN("AutoPlaylist: Filters reset.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
avg_long_energy = avg_long_energy + FILTER_SLOW1 * (float(energy) - avg_long_energy);
|
||||||
|
avg_long_lfc = avg_long_lfc + FILTER_SLOW2 * (float(lfc) - avg_long_lfc);
|
||||||
|
avg_long_zcr = avg_long_zcr + FILTER_SLOW2 * (float(zcr) - avg_long_zcr);
|
||||||
|
|
||||||
|
avg_short_energy = avg_short_energy + FILTER_FAST1 * (float(energy) - avg_short_energy);
|
||||||
|
avg_short_lfc = avg_short_lfc + FILTER_FAST2 * (float(lfc) - avg_short_lfc);
|
||||||
|
avg_short_zcr = avg_short_zcr + FILTER_FAST2 * (float(zcr) - avg_short_zcr);
|
||||||
|
|
||||||
|
// allegedly this is faster than pow(whatever,2)
|
||||||
|
vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc);
|
||||||
|
vector_energy = (avg_short_energy-avg_long_energy)*(avg_short_energy-avg_long_energy);
|
||||||
|
vector_zcr = (avg_short_zcr-avg_long_zcr)*(avg_short_zcr-avg_long_zcr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
distance = vector_lfc + vector_energy + vector_zcr;
|
||||||
|
|
||||||
|
long change_interval = millis()-lastchange;
|
||||||
|
|
||||||
|
if (distance < distance_tracker && change_interval > change_lockout && volumeSmth > 1.0f) {
|
||||||
|
distance_tracker = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug for adjusting formulas, etc:
|
||||||
|
// USER_PRINTF("Distance: %5lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr);
|
||||||
|
|
||||||
|
if ((millis() - change_timer) > ideal_change_min) { // softhack007 same result as "millis() > change_timer + ideal_change_min", but more robust against unsigned overflow
|
||||||
|
|
||||||
|
// Make the analysis less sensitive if we miss the window.
|
||||||
|
// Sometimes the analysis lowers the change_threshold too much for
|
||||||
|
// the current music, especially after track changes or during
|
||||||
|
// sparse intros and breakdowns.
|
||||||
|
|
||||||
|
if (change_interval > ideal_change_min && distance_tracker <= MAX_DISTANCE_TRACKER) {
|
||||||
|
|
||||||
|
if (distance_tracker >= change_threshold) {
|
||||||
|
change_threshold_change = distance_tracker-change_threshold;
|
||||||
|
} else {
|
||||||
|
change_threshold_change = change_threshold-distance_tracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
change_threshold = distance_tracker;
|
||||||
|
|
||||||
|
if (change_threshold_change > 9999) change_threshold_change = 0; // cosmetic for debug
|
||||||
|
|
||||||
|
if (functionality_enabled) {
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff approx)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
distance_tracker = UINT_FAST32_MAX;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
change_timer = millis();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) {
|
||||||
|
|
||||||
|
change_threshold_change = max(1.0f, roundf(change_threshold-(distance*0.9f))); // exclude negatives, ensure change_threshold_change is always >= 1
|
||||||
|
|
||||||
|
if (change_interval > ideal_change_max) {
|
||||||
|
change_threshold += change_threshold_change; // make changes more sensitive
|
||||||
|
} else if (change_interval < ideal_change_min) {
|
||||||
|
change_threshold -= change_threshold_change; // make changes less sensitive
|
||||||
|
} else {
|
||||||
|
change_threshold_change = 0; // change was within our window, no sensitivity change
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change_threshold < 1) change_threshold = 0; // we need change_threshold to be signed because otherwise this wraps to UINT_FAST16_MAX
|
||||||
|
|
||||||
|
distance_tracker = UINT_FAST32_MAX;
|
||||||
|
|
||||||
|
if (functionality_enabled) {
|
||||||
|
|
||||||
|
if (autoChangeIds.size() == 0) {
|
||||||
|
if(currentPlaylist < 1) return;
|
||||||
|
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("Loading presets from playlist: %3d\n", currentPlaylist);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JsonObject playtlistOjb = doc.to<JsonObject>();
|
||||||
|
serializePlaylist(playtlistOjb);
|
||||||
|
JsonArray playlistArray = playtlistOjb["playlist"]["ps"];
|
||||||
|
|
||||||
|
for(JsonVariant v : playlistArray) {
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("Adding %3u to autoChangeIds\n", v.as<int>());
|
||||||
|
#endif
|
||||||
|
autoChangeIds.push_back(v.as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t newpreset = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size.
|
||||||
|
} while ((currentPreset == newpreset) && (autoChangeIds.size() > 1)); // make sure we get a different random preset. Unless there is only one.
|
||||||
|
|
||||||
|
if (change_interval > change_lockout+3) {
|
||||||
|
|
||||||
|
// Make sure we have a statistically significant change and we aren't
|
||||||
|
// just bouncing off change_lockout. That's valid for changing the
|
||||||
|
// thresholds, but might be a bit crazy for lighting changes.
|
||||||
|
// When the music changes quite a bit, the distance calculation can
|
||||||
|
// go into freefall - this logic stops that from triggering right
|
||||||
|
// after change_lockout. Better for smaller change_lockout values.
|
||||||
|
|
||||||
|
suspendPlaylist(); // suspend the playlist engine before changing to another preset
|
||||||
|
applyPreset(newpreset);
|
||||||
|
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lastchange = millis();
|
||||||
|
change_timer = millis();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Da loop.
|
||||||
|
*/
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
if (!enabled) return;
|
||||||
|
|
||||||
|
if (millis() < 10000) return; // Wait for device to settle
|
||||||
|
|
||||||
|
if (lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) {
|
||||||
|
if (functionality_enabled) {
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset);
|
||||||
|
#endif
|
||||||
|
functionality_enabled = false;
|
||||||
|
} else if (currentPlaylist == musicPlaylist) {
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("AutoPlaylist: enabled due to manual change of playlist back to %u\n", currentPlaylist);
|
||||||
|
#endif
|
||||||
|
functionality_enabled = true;
|
||||||
|
lastAutoPlaylist = currentPlaylist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!functionality_enabled && currentPlaylist == musicPlaylist) {
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("AutoPlaylist: enabled due selecting musicPlaylist(%u)\n", musicPlaylist);
|
||||||
|
#endif
|
||||||
|
functionality_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bri == 0) return;
|
||||||
|
|
||||||
|
um_data_t *um_data;
|
||||||
|
|
||||||
|
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||||
|
// No Audio Reactive
|
||||||
|
silenceDetected = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float volumeSmth = *(float*)um_data->u_data[0];
|
||||||
|
|
||||||
|
avg_volumeSmth = avg_volumeSmth + FILTER_VOLUME * (volumeSmth - avg_volumeSmth);
|
||||||
|
|
||||||
|
if (avg_volumeSmth >= 1.0f) {
|
||||||
|
lastSoundTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - lastSoundTime > (long(timeout) * 1000)) {
|
||||||
|
if (!silenceDetected) {
|
||||||
|
silenceDetected = true;
|
||||||
|
USER_PRINTLN("AutoPlaylist: Silence detected");
|
||||||
|
changePlaylist(ambientPlaylist);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (silenceDetected) {
|
||||||
|
silenceDetected = false;
|
||||||
|
USER_PRINTLN("AutoPlaylist: Sound detected");
|
||||||
|
changePlaylist(musicPlaylist);
|
||||||
|
}
|
||||||
|
if (autoChange && millis() >= autochange_timer+22) {
|
||||||
|
change(um_data);
|
||||||
|
autochange_timer = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 addToJsonInfo(JsonObject& root) {
|
||||||
|
JsonObject user = root["u"];
|
||||||
|
if (user.isNull()) {
|
||||||
|
user = root.createNestedObject("u");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled) return; // usermod disabled -> don't add to info page
|
||||||
|
|
||||||
|
String uiNameString = FPSTR(_name);
|
||||||
|
if (enabled && functionality_enabled) {
|
||||||
|
uiNameString += F(" Running");
|
||||||
|
} else if (!enabled) {
|
||||||
|
uiNameString += F(" Disabled");
|
||||||
|
} else {
|
||||||
|
uiNameString += F(" Suspended");
|
||||||
|
}
|
||||||
|
JsonArray infoArr = user.createNestedArray(uiNameString); // name + status
|
||||||
|
|
||||||
|
String uiDomString = (currentPlaylist > 0) ? String("#") + String(currentPlaylist) + String(" ") : String("");
|
||||||
|
|
||||||
|
if (currentPlaylist == musicPlaylist && currentPlaylist > 0) {
|
||||||
|
uiDomString += F("Music Playlist");
|
||||||
|
} else if (currentPlaylist == ambientPlaylist && currentPlaylist > 0) {
|
||||||
|
uiDomString += F("Ambient Playlist");
|
||||||
|
} else {
|
||||||
|
uiDomString += F("Playlist Overridden");
|
||||||
|
}
|
||||||
|
|
||||||
|
uiDomString += F("<br />");
|
||||||
|
|
||||||
|
if (enabled && autoChange && currentPlaylist == musicPlaylist && functionality_enabled) {
|
||||||
|
uiDomString += F("AutoChange is Active");
|
||||||
|
} else if (autoChange && (currentPlaylist != musicPlaylist || !functionality_enabled || !enabled)) {
|
||||||
|
uiDomString += F("AutoChange on Stand-by");
|
||||||
|
} else if (!autoChange) {
|
||||||
|
uiDomString += F("AutoChange is Disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
// uiDomString += F("<br />");
|
||||||
|
// uiDomString += F("Change Threshold: ");
|
||||||
|
// uiDomString += String(change_threshold);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
infoArr.add(uiDomString);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendConfigData() {
|
||||||
|
oappend(SET_F("addHB('AutoPlaylist');"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||||
|
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||||
|
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||||
|
*
|
||||||
|
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||||
|
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||||
|
* Use it sparingly and always in the loop, never in network callbacks!
|
||||||
|
*
|
||||||
|
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
||||||
|
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||||
|
*
|
||||||
|
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||||
|
*/
|
||||||
|
void addToConfig(JsonObject& root) {
|
||||||
|
|
||||||
|
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||||
|
|
||||||
|
top[FPSTR(_autoPlaylistEnabled)] = enabled;
|
||||||
|
top[FPSTR(_timeout)] = timeout;
|
||||||
|
top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam
|
||||||
|
top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam
|
||||||
|
top[FPSTR(_autoChange)] = autoChange;
|
||||||
|
top[FPSTR(_change_lockout)] = change_lockout;
|
||||||
|
top[FPSTR(_ideal_change_min)] = ideal_change_min;
|
||||||
|
top[FPSTR(_ideal_change_max)] = ideal_change_max;
|
||||||
|
|
||||||
|
lastAutoPlaylist = 0;
|
||||||
|
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTLN(F("AutoPlaylist config saved."));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||||
|
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
||||||
|
*
|
||||||
|
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||||
|
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||||
|
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||||
|
*
|
||||||
|
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||||
|
*/
|
||||||
|
bool readFromConfig(JsonObject& root) {
|
||||||
|
|
||||||
|
JsonObject top = root[FPSTR(_name)];
|
||||||
|
|
||||||
|
if (top.isNull()) {
|
||||||
|
USER_PRINT(FPSTR(_name));
|
||||||
|
USER_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = top[FPSTR(_autoPlaylistEnabled)] | enabled;
|
||||||
|
timeout = top[FPSTR(_timeout)] | timeout;
|
||||||
|
ambientPlaylist = top[FPSTR(_ambientPlaylist)] | ambientPlaylist;
|
||||||
|
musicPlaylist = top[FPSTR(_musicPlaylist)] | musicPlaylist;
|
||||||
|
autoChange = top[FPSTR(_autoChange)] | autoChange;
|
||||||
|
change_lockout = top[FPSTR(_change_lockout)] | change_lockout;
|
||||||
|
ideal_change_min = top[FPSTR(_ideal_change_min)] | ideal_change_min;
|
||||||
|
ideal_change_max = top[FPSTR(_ideal_change_max)] | ideal_change_max;
|
||||||
|
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINT(FPSTR(_name));
|
||||||
|
USER_PRINTLN(F(" config (re)loaded."));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||||
|
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||||
|
*/
|
||||||
|
uint16_t getId() {
|
||||||
|
return USERMOD_ID_AUTOPLAYLIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void changePlaylist(byte id) {
|
||||||
|
String name = "";
|
||||||
|
getPresetName(id, name);
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG
|
||||||
|
USER_PRINTF("AutoPlaylist: Applying \"%s\"\n", name.c_str());
|
||||||
|
#endif
|
||||||
|
// if (currentPlaylist != id) { // un-comment to only change on "real" changes
|
||||||
|
unloadPlaylist(); // applying a preset requires to unload previous playlist
|
||||||
|
applyPreset(id, CALL_MODE_NOTIFICATION);
|
||||||
|
// }
|
||||||
|
lastAutoPlaylist = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const char AutoPlaylistUsermod::_name[] PROGMEM = "AutoPlaylist";
|
||||||
|
const char AutoPlaylistUsermod::_autoPlaylistEnabled[] PROGMEM = "enabled";
|
||||||
|
const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist";
|
||||||
|
const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist";
|
||||||
|
const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout";
|
||||||
|
const char AutoPlaylistUsermod::_autoChange[] PROGMEM = "autoChange";
|
||||||
|
const char AutoPlaylistUsermod::_change_lockout[] PROGMEM = "change_lockout";
|
||||||
|
const char AutoPlaylistUsermod::_ideal_change_min[] PROGMEM = "ideal_change_min";
|
||||||
|
const char AutoPlaylistUsermod::_ideal_change_max[] PROGMEM = "ideal_change_max";
|
||||||
@@ -3194,7 +3194,13 @@ static uint16_t mode_popcorn_core(bool useaudio) {
|
|||||||
if (SEGLEN == 1) return mode_static();
|
if (SEGLEN == 1) return mode_static();
|
||||||
//allocate segment data
|
//allocate segment data
|
||||||
uint16_t strips = SEGMENT.nrOfVStrips();
|
uint16_t strips = SEGMENT.nrOfVStrips();
|
||||||
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
|
size_t dataSize = sizeof(spark) * maxNumPopcorn;
|
||||||
|
uint8_t neededPopcorn = maxNumPopcorn; // WLEDMM
|
||||||
|
if (strips > 8) { // WLEDMM more than 8 virtual strips --> reduce memory requirements to minimum necessary
|
||||||
|
neededPopcorn = (SEGMENT.intensity*maxNumPopcorn)/255;
|
||||||
|
neededPopcorn = min(max(neededPopcorn, uint8_t(2)), uint8_t(maxNumPopcorn));
|
||||||
|
dataSize = sizeof(spark) * neededPopcorn;
|
||||||
|
}
|
||||||
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
|
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
|
||||||
|
|
||||||
Spark* popcorn = reinterpret_cast<Spark*>(SEGENV.data);
|
Spark* popcorn = reinterpret_cast<Spark*>(SEGENV.data);
|
||||||
@@ -3212,7 +3218,7 @@ static uint16_t mode_popcorn_core(bool useaudio) {
|
|||||||
|
|
||||||
struct virtualStrip {
|
struct virtualStrip {
|
||||||
static void runStrip(uint16_t stripNr, Spark* popcorn, bool useaudio, um_data_t *um_data) { // WLEDMM added useaudio and um_data
|
static void runStrip(uint16_t stripNr, Spark* popcorn, bool useaudio, um_data_t *um_data) { // WLEDMM added useaudio and um_data
|
||||||
float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s
|
float gravity = -0.0001f - (SEGMENT.speed/200000.0f); // m/s/s
|
||||||
gravity *= SEGLEN;
|
gravity *= SEGLEN;
|
||||||
|
|
||||||
uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255;
|
uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255;
|
||||||
@@ -3267,7 +3273,7 @@ static uint16_t mode_popcorn_core(bool useaudio) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (int stripNr=0; stripNr<strips; stripNr++)
|
for (int stripNr=0; stripNr<strips; stripNr++)
|
||||||
virtualStrip::runStrip(stripNr, &popcorn[stripNr * maxNumPopcorn], useaudio, um_data); // WLEDMM added useaudio and um_data
|
virtualStrip::runStrip(stripNr, &popcorn[stripNr * neededPopcorn], useaudio, um_data); // WLEDMM added useaudio and um_data
|
||||||
|
|
||||||
return FRAMETIME;
|
return FRAMETIME;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ void WS2812FX::setUpMatrix() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight);
|
USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight);
|
||||||
|
|
||||||
//WLEDMM recreate customMappingTable if more space needed
|
//WLEDMM recreate customMappingTable if more space needed
|
||||||
if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) {
|
if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) {
|
||||||
size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight));//TroyHack
|
size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks
|
||||||
USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", size, customMappingTableSize);
|
USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", size, customMappingTableSize);
|
||||||
//if (customMappingTable != nullptr) delete[] customMappingTable;
|
//if (customMappingTable != nullptr) delete[] customMappingTable;
|
||||||
//customMappingTable = new uint16_t[size];
|
//customMappingTable = new uint16_t[size];
|
||||||
@@ -80,7 +80,10 @@ void WS2812FX::setUpMatrix() {
|
|||||||
if ((size > 0) && (customMappingTable == nullptr)) { // second try
|
if ((size > 0) && (customMappingTable == nullptr)) { // second try
|
||||||
DEBUG_PRINTLN("setUpMatrix: trying to get fresh memory block.");
|
DEBUG_PRINTLN("setUpMatrix: trying to get fresh memory block.");
|
||||||
customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t));
|
customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t));
|
||||||
if (customMappingTable == nullptr) USER_PRINTLN("setUpMatrix: alloc failed");
|
if (customMappingTable == nullptr) {
|
||||||
|
USER_PRINTLN("setUpMatrix: alloc failed");
|
||||||
|
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (customMappingTable != nullptr) customMappingTableSize = size;
|
if (customMappingTable != nullptr) customMappingTableSize = size;
|
||||||
}
|
}
|
||||||
@@ -159,6 +162,7 @@ void WS2812FX::setUpMatrix() {
|
|||||||
} else { // memory allocation error
|
} else { // memory allocation error
|
||||||
customMappingTableSize = 0;
|
customMappingTableSize = 0;
|
||||||
USER_PRINTLN(F("Ledmap alloc error."));
|
USER_PRINTLN(F("Ledmap alloc error."));
|
||||||
|
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
isMatrix = false; //WLEDMM does not like this done in teh background while end users are confused whats happened...
|
isMatrix = false; //WLEDMM does not like this done in teh background while end users are confused whats happened...
|
||||||
panels = 0;
|
panels = 0;
|
||||||
panel.clear();
|
panel.clear();
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ Segment::Segment(const Segment &orig) {
|
|||||||
|
|
||||||
//WLEDMM: recreate ledsrgb if more space needed (will not free ledsrgb!)
|
//WLEDMM: recreate ledsrgb if more space needed (will not free ledsrgb!)
|
||||||
void Segment::allocLeds() {
|
void Segment::allocLeds() {
|
||||||
size_t size = sizeof(CRGB)*max((size_t) length(), ledmapMaxSize); //TroyHack
|
size_t size = sizeof(CRGB)*max((size_t) length(), ledmapMaxSize); // TroyHacks
|
||||||
if ((size < sizeof(CRGB)) || (size > 164000)) { //softhack too small (<3) or too large (>160Kb)
|
if ((size < sizeof(CRGB)) || (size > 164000)) { //softhack too small (<3) or too large (>160Kb)
|
||||||
DEBUG_PRINTF("allocLeds warning: size == %u !!\n", size);
|
DEBUG_PRINTF("allocLeds warning: size == %u !!\n", size);
|
||||||
if (ledsrgb && (ledsrgbSize == 0)) {
|
if (ledsrgb && (ledsrgbSize == 0)) {
|
||||||
@@ -128,10 +128,13 @@ void Segment::allocLeds() {
|
|||||||
if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first
|
if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first
|
||||||
ledsrgb = (CRGB*)calloc(size, 1);
|
ledsrgb = (CRGB*)calloc(size, 1);
|
||||||
ledsrgbSize = ledsrgb?size:0;
|
ledsrgbSize = ledsrgb?size:0;
|
||||||
if (ledsrgb == nullptr) USER_PRINTLN("allocLeds failed!!");
|
if (ledsrgb == nullptr) {
|
||||||
|
USER_PRINTLN("allocLeds failed!!");
|
||||||
|
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
USER_PRINTF("reuse Leds %u from %u\n", size, ledsrgb?ledsrgbSize:0);
|
//USER_PRINTF("reuse Leds %u from %u\n", size, ledsrgb?ledsrgbSize:0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +215,11 @@ bool Segment::allocateData(size_t len) {
|
|||||||
//DEBUG_PRINTF("allocateData(%u) start %d, stop %d, vlen %d\n", len, start, stop, virtualLength());
|
//DEBUG_PRINTF("allocateData(%u) start %d, stop %d, vlen %d\n", len, start, stop, virtualLength());
|
||||||
deallocateData();
|
deallocateData();
|
||||||
if (len == 0) return false; // nothing to do
|
if (len == 0) return false; // nothing to do
|
||||||
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
|
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
|
||||||
|
//USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA);
|
||||||
|
if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag
|
||||||
|
return false; //not enough memory
|
||||||
|
}
|
||||||
// do not use SPI RAM on ESP32 since it is slow
|
// do not use SPI RAM on ESP32 since it is slow
|
||||||
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
|
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
|
||||||
//if (psramFound())
|
//if (psramFound())
|
||||||
@@ -220,10 +227,17 @@ bool Segment::allocateData(size_t len) {
|
|||||||
//else
|
//else
|
||||||
//#endif
|
//#endif
|
||||||
data = (byte*) malloc(len);
|
data = (byte*) malloc(len);
|
||||||
if (!data) { _dataLen = 0; return false;} //allocation failed // WLEDMM reset dataLen
|
if (!data) {
|
||||||
|
_dataLen = 0; // WLEDMM reset dataLen
|
||||||
|
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
|
USER_PRINT(F("Segment::allocateData: FAILED to allocate "));
|
||||||
|
USER_PRINT(len); USER_PRINTLN(F(" bytes."));
|
||||||
|
return false;
|
||||||
|
} //allocation failed
|
||||||
Segment::addUsedSegmentData(len);
|
Segment::addUsedSegmentData(len);
|
||||||
_dataLen = len;
|
_dataLen = len;
|
||||||
memset(data, 0, len);
|
memset(data, 0, len);
|
||||||
|
if (errorFlag == ERR_LOW_SEG_MEM) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +245,7 @@ void Segment::deallocateData() {
|
|||||||
if (!data) {_dataLen = 0; return;} // WLEDMM reset dataLen
|
if (!data) {_dataLen = 0; return;} // WLEDMM reset dataLen
|
||||||
free(data);
|
free(data);
|
||||||
data = nullptr;
|
data = nullptr;
|
||||||
//DEBUG_PRINTLN("deallocateData() called free().");
|
//USER_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen);
|
||||||
Segment::addUsedSegmentData(-_dataLen);
|
Segment::addUsedSegmentData(-_dataLen);
|
||||||
_dataLen = 0;
|
_dataLen = 0;
|
||||||
}
|
}
|
||||||
@@ -482,6 +496,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
|
|||||||
&& (!grp || (grouping == grp && spacing == spc))
|
&& (!grp || (grouping == grp && spacing == spc))
|
||||||
&& (ofs == UINT16_MAX || ofs == offset)) return;
|
&& (ofs == UINT16_MAX || ofs == offset)) return;
|
||||||
|
|
||||||
|
stateChanged = true; // send UDP/WS broadcast
|
||||||
|
|
||||||
if (stop>start) fill(BLACK); //turn old segment range off // WLEDMM stop > start
|
if (stop>start) fill(BLACK); //turn old segment range off // WLEDMM stop > start
|
||||||
if (i2 <= i1) { //disable segment
|
if (i2 <= i1) { //disable segment
|
||||||
stop = 0;
|
stop = 0;
|
||||||
@@ -1536,7 +1552,7 @@ void WS2812FX::enumerateLedmaps() {
|
|||||||
|
|
||||||
USER_PRINTF("enumerateLedmaps %s \"%s\"", fileName, name);
|
USER_PRINTF("enumerateLedmaps %s \"%s\"", fileName, name);
|
||||||
if (isMatrix) {
|
if (isMatrix) {
|
||||||
//WLEDMM calc ledmapMaxSize (TroyHack)
|
//WLEDMM calc ledmapMaxSize (TroyHacks)
|
||||||
char dim[34] = { '\0' };
|
char dim[34] = { '\0' };
|
||||||
f.find("\"width\":");
|
f.find("\"width\":");
|
||||||
f.readBytesUntil('\n', dim, sizeof(dim)-1); //hack: use fileName as we have this allocated already
|
f.readBytesUntil('\n', dim, sizeof(dim)-1); //hack: use fileName as we have this allocated already
|
||||||
@@ -1654,6 +1670,7 @@ void WS2812FX::finalizeInit(void)
|
|||||||
//#endif
|
//#endif
|
||||||
if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0)
|
if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0)
|
||||||
if ((Segment::_globalLeds != nullptr) && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); // WLEDMM avoid dereferencing nullptr
|
if ((Segment::_globalLeds != nullptr) && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); // WLEDMM avoid dereferencing nullptr
|
||||||
|
if ((Segment::_globalLeds == nullptr) && (arrSize > 0)) errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
}
|
}
|
||||||
|
|
||||||
//segments are created in makeAutoSegments();
|
//segments are created in makeAutoSegments();
|
||||||
@@ -2273,16 +2290,16 @@ void WS2812FX::loadCustomPalettes() {
|
|||||||
|
|
||||||
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
|
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
|
||||||
JsonArray pal = pDoc[F("palette")];
|
JsonArray pal = pDoc[F("palette")];
|
||||||
if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries)
|
if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries)
|
||||||
if (pal[0].is<int>() && pal[1].is<const char *>()) {
|
if (pal[0].is<int>() && pal[1].is<const char *>()) {
|
||||||
// we have an array of index & hex strings
|
// we have an array of index & hex strings
|
||||||
size_t palSize = min(pal.size(), (size_t)36); // WLEDMM use native min/max
|
size_t palSize = min(pal.size(), (size_t)36); // WLEDMM use native min/max
|
||||||
palSize -= palSize % 2; // make sure size is multiple of 2
|
palSize -= palSize % 2; // make sure size is multiple of 2
|
||||||
for (size_t i=0, j=0; i<palSize && pal[i].as<int>()<256; i+=2, j+=4) {
|
for (unsigned i=0, j=0; i<palSize && pal[i].as<int>()<256; i+=2, j+=4) {
|
||||||
uint8_t rgbw[] = {0,0,0,0};
|
uint8_t rgbw[] = {0,0,0,0};
|
||||||
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
|
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
|
||||||
colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires
|
colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires
|
||||||
for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component
|
for (unsigned c=0; c<3; c++) tcp[j+1+c] = gamma8(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]));
|
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 {
|
} else {
|
||||||
@@ -2290,13 +2307,15 @@ void WS2812FX::loadCustomPalettes() {
|
|||||||
palSize -= palSize % 4; // make sure size is multiple of 4
|
palSize -= palSize % 4; // make sure size is multiple of 4
|
||||||
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
|
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
|
||||||
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
|
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
|
||||||
tcp[i+1] = (uint8_t) pal[i+1].as<int>(); // R
|
tcp[i+1] = gamma8((uint8_t) pal[i+1].as<int>()); // R
|
||||||
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G
|
tcp[i+2] = gamma8((uint8_t) pal[i+2].as<int>()); // G
|
||||||
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B
|
tcp[i+3] = gamma8((uint8_t) pal[i+3].as<int>()); // B
|
||||||
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
|
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
|
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTLN(F("Wrong palette format."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -2387,7 +2406,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
|
|||||||
|
|
||||||
//WLEDMM recreate customMappingTable if more space needed
|
//WLEDMM recreate customMappingTable if more space needed
|
||||||
if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) {
|
if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) {
|
||||||
size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight));//TroyHack
|
size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks
|
||||||
USER_PRINTF("deserializemap customMappingTable alloc %u from %u\n", size, customMappingTableSize);
|
USER_PRINTF("deserializemap customMappingTable alloc %u from %u\n", size, customMappingTableSize);
|
||||||
//if (customMappingTable != nullptr) delete[] customMappingTable;
|
//if (customMappingTable != nullptr) delete[] customMappingTable;
|
||||||
//customMappingTable = new uint16_t[size];
|
//customMappingTable = new uint16_t[size];
|
||||||
@@ -2399,7 +2418,10 @@ bool WS2812FX::deserializeMap(uint8_t n) {
|
|||||||
if ((size > 0) && (customMappingTable == nullptr)) { // second try
|
if ((size > 0) && (customMappingTable == nullptr)) { // second try
|
||||||
DEBUG_PRINTLN("deserializeMap: trying to get fresh memory block.");
|
DEBUG_PRINTLN("deserializeMap: trying to get fresh memory block.");
|
||||||
customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t));
|
customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t));
|
||||||
if (customMappingTable == nullptr) DEBUG_PRINTLN("deserializeMap: alloc failed!");
|
if (customMappingTable == nullptr) {
|
||||||
|
DEBUG_PRINTLN("deserializeMap: alloc failed!");
|
||||||
|
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (customMappingTable != nullptr) customMappingTableSize = size;
|
if (customMappingTable != nullptr) customMappingTableSize = size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,6 +142,7 @@
|
|||||||
#define USERMOD_ID_WEATHER 91 //Usermod "usermod_v2_weather.h"
|
#define USERMOD_ID_WEATHER 91 //Usermod "usermod_v2_weather.h"
|
||||||
#define USERMOD_ID_GAMES 92 //Usermod "usermod_v2_games.h"
|
#define USERMOD_ID_GAMES 92 //Usermod "usermod_v2_games.h"
|
||||||
#define USERMOD_ID_ANIMARTRIX 93 //Usermod "usermod_v2_animartrix.h"
|
#define USERMOD_ID_ANIMARTRIX 93 //Usermod "usermod_v2_animartrix.h"
|
||||||
|
#define USERMOD_ID_AUTOPLAYLIST 94 // Usermod usermod_v2_auto_playlist.h
|
||||||
|
|
||||||
//Access point behavior
|
//Access point behavior
|
||||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||||
@@ -347,7 +348,9 @@
|
|||||||
#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented)
|
#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented)
|
||||||
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)
|
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)
|
||||||
#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented)
|
#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented)
|
||||||
#define ERR_LOW_MEM 33 // low memory (RAM)
|
#define ERR_LOW_MEM 33 // WLEDMM: low memory (RAM)
|
||||||
|
#define ERR_LOW_SEG_MEM 34 // WLEDMM: low memory (segment data RAM)
|
||||||
|
#define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws)
|
||||||
|
|
||||||
// Timer mode types
|
// Timer mode types
|
||||||
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness
|
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness
|
||||||
@@ -494,7 +497,10 @@
|
|||||||
#define DEFAULT_LED_COUNT 30
|
#define DEFAULT_LED_COUNT 30
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates
|
#define INTERFACE_UPDATE_COOLDOWN 1200 // time in ms to wait between websockets, alexa, and MQTT updates
|
||||||
|
|
||||||
|
#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct
|
||||||
|
#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes
|
||||||
|
|
||||||
// HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves
|
// HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves
|
||||||
// which GPIO pins are actually used in a hardware layout (controller board)
|
// which GPIO pins are actually used in a hardware layout (controller board)
|
||||||
|
|||||||
@@ -1968,8 +1968,14 @@ function readState(s,command=false)
|
|||||||
errstr = "A filesystem error has occured.";
|
errstr = "A filesystem error has occured.";
|
||||||
break;
|
break;
|
||||||
case 33:
|
case 33:
|
||||||
errstr = "Warning: Low Memory (RAM).";
|
errstr = "Low Memory (generic RAM).";
|
||||||
break;
|
break;
|
||||||
|
case 34:
|
||||||
|
errstr = "Low Memory (effect data).";
|
||||||
|
break;
|
||||||
|
case 35:
|
||||||
|
errstr = "Low Memory (WS data).";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
showToast('Error ' + s.error + ": " + errstr, true);
|
showToast('Error ' + s.error + ": " + errstr, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,11 +231,16 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
|||||||
if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3];
|
if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3];
|
||||||
if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]);
|
if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]);
|
||||||
|
|
||||||
uint8_t segOption = (uint8_t)floor(e131_data[dataOffset+5]/64.0);
|
if ((e131_data[dataOffset+5] & 0b00000010) != seg.reverse_y) { seg.setOption(SEG_OPTION_REVERSED_Y, e131_data[dataOffset+5] & 0b00000010); }
|
||||||
if (segOption == 0 && (seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, false);}
|
if ((e131_data[dataOffset+5] & 0b00000100) != seg.mirror_y) { seg.setOption(SEG_OPTION_MIRROR_Y, e131_data[dataOffset+5] & 0b00000100); }
|
||||||
if (segOption == 1 && (seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, true);}
|
if ((e131_data[dataOffset+5] & 0b00001000) != seg.transpose) { seg.setOption(SEG_OPTION_TRANSPOSED, e131_data[dataOffset+5] & 0b00001000); }
|
||||||
if (segOption == 2 && (!seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, false);}
|
if ((e131_data[dataOffset+5] & 0b00110000) / 8 != seg.map1D2D) {
|
||||||
if (segOption == 3 && (!seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, true);}
|
seg.map1D2D = (e131_data[dataOffset+5] & 0b00110000) / 8;
|
||||||
|
}
|
||||||
|
// To maintain backwards compatibility with prior e1.31 values, reverse is fixed to mask 0x01000000
|
||||||
|
if ((e131_data[dataOffset+5] & 0b01000000) != seg.reverse) { seg.setOption(SEG_OPTION_REVERSED, e131_data[dataOffset+5] & 0b01000000); }
|
||||||
|
// To maintain backwards compatibility with prior e1.31 values, mirror is fixed to mask 0x10000000
|
||||||
|
if ((e131_data[dataOffset+5] & 0b10000000) != seg.mirror) { seg.setOption(SEG_OPTION_MIRROR, e131_data[dataOffset+5] & 0b10000000); }
|
||||||
|
|
||||||
uint32_t colors[3];
|
uint32_t colors[3];
|
||||||
byte whites[3] = {0,0,0};
|
byte whites[3] = {0,0,0};
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ void _overlayAnalogCountdown();
|
|||||||
void _overlayAnalogClock();
|
void _overlayAnalogClock();
|
||||||
|
|
||||||
//playlist.cpp
|
//playlist.cpp
|
||||||
|
void suspendPlaylist(); // WLEDMM support function for auto playlist usermod
|
||||||
void shufflePlaylist();
|
void shufflePlaylist();
|
||||||
void unloadPlaylist();
|
void unloadPlaylist();
|
||||||
int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
||||||
@@ -370,9 +371,11 @@ void releaseJSONBufferLock();
|
|||||||
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
|
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
|
||||||
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
|
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
|
||||||
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
|
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
|
||||||
|
void checkSettingsPIN(const char *pin);
|
||||||
uint16_t __attribute__((pure)) crc16(const unsigned char* data_p, size_t length); // WLEDMM: added attribute pure
|
uint16_t __attribute__((pure)) crc16(const unsigned char* data_p, size_t length); // WLEDMM: added attribute pure
|
||||||
um_data_t* simulateSound(uint8_t simulationId);
|
um_data_t* simulateSound(uint8_t simulationId);
|
||||||
// WLEDMM enumerateLedmaps(); moved to FX.h
|
// WLEDMM enumerateLedmaps(); moved to FX.h
|
||||||
|
uint8_t get_random_wheel_index(uint8_t pos);
|
||||||
CRGB getCRGBForBand(int x, uint8_t *fftResult, int pal); //WLEDMM netmindz ar palette
|
CRGB getCRGBForBand(int x, uint8_t *fftResult, int pal); //WLEDMM netmindz ar palette
|
||||||
char *cleanUpName(char *in); // to clean up a name that was read from file
|
char *cleanUpName(char *in); // to clean up a name that was read from file
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
|
|||||||
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
|
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
|
||||||
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
|
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
|
||||||
char vString[32];
|
char vString[32];
|
||||||
snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b30.36/%i"),VERSION);
|
snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b30.37/%i"),VERSION);
|
||||||
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
|
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
|
||||||
|
|
||||||
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
|
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ void stateUpdated(byte callMode) {
|
|||||||
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
|
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
|
||||||
|
|
||||||
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
|
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
|
||||||
|
if (bri != briOld && nodeBroadcastEnabled) sendSysInfoUDP(); // update on state
|
||||||
|
|
||||||
//set flag to update ws and mqtt
|
//set flag to update ws and mqtt
|
||||||
interfaceUpdateCallMode = callMode;
|
interfaceUpdateCallMode = callMode;
|
||||||
@@ -139,6 +140,8 @@ void stateUpdated(byte callMode) {
|
|||||||
jsonTransitionOnce = false;
|
jsonTransitionOnce = false;
|
||||||
strip.setTransition(transitionDelayTemp);
|
strip.setTransition(transitionDelayTemp);
|
||||||
if (transitionDelayTemp == 0) {
|
if (transitionDelayTemp == 0) {
|
||||||
|
jsonTransitionOnce = false;
|
||||||
|
transitionActive = false;
|
||||||
applyFinalBri();
|
applyFinalBri();
|
||||||
strip.trigger();
|
strip.trigger();
|
||||||
return;
|
return;
|
||||||
@@ -161,8 +164,12 @@ void stateUpdated(byte callMode) {
|
|||||||
|
|
||||||
void updateInterfaces(uint8_t callMode)
|
void updateInterfaces(uint8_t callMode)
|
||||||
{
|
{
|
||||||
|
if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return;
|
||||||
|
|
||||||
sendDataWs();
|
sendDataWs();
|
||||||
lastInterfaceUpdate = millis();
|
lastInterfaceUpdate = millis();
|
||||||
|
interfaceUpdateCallMode = 0; //disable
|
||||||
|
|
||||||
if (callMode == CALL_MODE_WS_SEND) return;
|
if (callMode == CALL_MODE_WS_SEND) return;
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_ALEXA
|
#ifndef WLED_DISABLE_ALEXA
|
||||||
@@ -172,14 +179,13 @@ void updateInterfaces(uint8_t callMode)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
doPublishMqtt = true;
|
doPublishMqtt = true;
|
||||||
interfaceUpdateCallMode = 0; //disable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void handleTransitions()
|
void handleTransitions()
|
||||||
{
|
{
|
||||||
//handle still pending interface update
|
//handle still pending interface update
|
||||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode);
|
updateInterfaces(interfaceUpdateCallMode);
|
||||||
#ifndef WLED_DISABLE_MQTT
|
#ifndef WLED_DISABLE_MQTT
|
||||||
if (doPublishMqtt) publishMqtt();
|
if (doPublishMqtt) publishMqtt();
|
||||||
#endif
|
#endif
|
||||||
@@ -190,12 +196,15 @@ void handleTransitions()
|
|||||||
if (tper >= 1.0f)
|
if (tper >= 1.0f)
|
||||||
{
|
{
|
||||||
strip.setTransitionMode(false);
|
strip.setTransitionMode(false);
|
||||||
|
// restore (global) transition time if not called from UDP notifier or single/temporary transition from JSON (also playlist)
|
||||||
|
if (jsonTransitionOnce) strip.setTransition(transitionDelay);
|
||||||
transitionActive = false;
|
transitionActive = false;
|
||||||
|
jsonTransitionOnce = false;
|
||||||
tperLast = 0;
|
tperLast = 0;
|
||||||
applyFinalBri();
|
applyFinalBri();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tper - tperLast < 0.004) return;
|
if (tper - tperLast < 0.004f) return;
|
||||||
tperLast = tper;
|
tperLast = tper;
|
||||||
briT = briOld + ((bri - briOld) * tper);
|
briT = briOld + ((bri - briOld) * tper);
|
||||||
|
|
||||||
@@ -205,7 +214,7 @@ void handleTransitions()
|
|||||||
|
|
||||||
|
|
||||||
// legacy method, applies values from col, effectCurrent, ... to selected segments
|
// legacy method, applies values from col, effectCurrent, ... to selected segments
|
||||||
void colorUpdated(byte callMode){
|
void colorUpdated(byte callMode) {
|
||||||
applyValuesToSelectedSegs();
|
applyValuesToSelectedSegs();
|
||||||
stateUpdated(callMode);
|
stateUpdated(callMode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ void shufflePlaylist() {
|
|||||||
DEBUG_PRINTLN(F("Playlist shuffle."));
|
DEBUG_PRINTLN(F("Playlist shuffle."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WLEDMM supporting function for auto_playlist usermod
|
||||||
|
// prevents the active playlist from progressing (until it gets unloaded)
|
||||||
|
static bool playlistSuspended = false;
|
||||||
|
void suspendPlaylist() {
|
||||||
|
playlistSuspended = true;
|
||||||
|
}
|
||||||
|
|
||||||
void unloadPlaylist() {
|
void unloadPlaylist() {
|
||||||
if (playlistEntries != nullptr) {
|
if (playlistEntries != nullptr) {
|
||||||
@@ -49,6 +55,7 @@ void unloadPlaylist() {
|
|||||||
}
|
}
|
||||||
currentPlaylist = playlistIndex = -1;
|
currentPlaylist = playlistIndex = -1;
|
||||||
playlistLen = playlistEntryDur = playlistOptions = 0;
|
playlistLen = playlistEntryDur = playlistOptions = 0;
|
||||||
|
playlistSuspended = false; // WLEDMM
|
||||||
DEBUG_PRINTLN(F("Playlist unloaded."));
|
DEBUG_PRINTLN(F("Playlist unloaded."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +132,11 @@ void handlePlaylist() {
|
|||||||
// if fileDoc is not null JSON buffer is in use so just quit
|
// if fileDoc is not null JSON buffer is in use so just quit
|
||||||
if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return;
|
if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return;
|
||||||
|
|
||||||
|
if (playlistSuspended) { // WLEDMM
|
||||||
|
if (millis() - presetCycledTime > (100*playlistEntryDur)) presetCycledTime = millis(); // keep updating timer
|
||||||
|
return; // but don't progress to next extry, and don't shuffle
|
||||||
|
}
|
||||||
|
|
||||||
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
|
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
|
||||||
presetCycledTime = millis();
|
presetCycledTime = millis();
|
||||||
if (bri == 0 || nightlightActive) return;
|
if (bri == 0 || nightlightActive) return;
|
||||||
|
|||||||
@@ -712,6 +712,7 @@ void sendSysInfoUDP()
|
|||||||
#else
|
#else
|
||||||
data[38] = NODE_TYPE_ID_UNDEFINED;
|
data[38] = NODE_TYPE_ID_UNDEFINED;
|
||||||
#endif
|
#endif
|
||||||
|
if (bri) data[38] |= 0x80U; // add on/off state
|
||||||
data[39] = ip[3]; // unit ID == last IP number
|
data[39] = ip[3]; // unit ID == last IP number
|
||||||
|
|
||||||
uint32_t build = VERSION;
|
uint32_t build = VERSION;
|
||||||
|
|||||||
@@ -203,6 +203,9 @@
|
|||||||
#ifdef USERMOD_ANIMARTRIX
|
#ifdef USERMOD_ANIMARTRIX
|
||||||
#include "../usermods/usermod_v2_animartrix/usermod_v2_animartrix.h"
|
#include "../usermods/usermod_v2_animartrix/usermod_v2_animartrix.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST
|
||||||
|
#include "../usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
void registerUsermods()
|
void registerUsermods()
|
||||||
{
|
{
|
||||||
@@ -402,4 +405,9 @@ void registerUsermods()
|
|||||||
usermods.add(new AnimartrixUsermod("Animartrix", false));
|
usermods.add(new AnimartrixUsermod("Animartrix", false));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USERMOD_AUTO_PLAYLIST
|
||||||
|
usermods.add(new AutoPlaylistUsermod(false));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ bool requestJSONBufferLock(uint8_t module)
|
|||||||
{
|
{
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
while (jsonBufferLock && millis()-now < 1200) delay(1); // wait for fraction for buffer lock
|
while (jsonBufferLock && millis()-now < 1100) delay(1); // wait for fraction for buffer lock
|
||||||
|
|
||||||
if (jsonBufferLock) {
|
if (jsonBufferLock) {
|
||||||
USER_PRINT(F("ERROR: Locking JSON buffer failed! (still locked by "));
|
USER_PRINT(F("ERROR: Locking JSON buffer failed! (still locked by "));
|
||||||
@@ -239,9 +239,10 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
|
|||||||
{
|
{
|
||||||
if (src == JSON_mode_names || src == nullptr) {
|
if (src == JSON_mode_names || src == nullptr) {
|
||||||
if (mode < strip.getModeCount()) {
|
if (mode < strip.getModeCount()) {
|
||||||
char lineBuffer[256];
|
char lineBuffer[256] = { '\0' };
|
||||||
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
|
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
|
||||||
strcpy_P(lineBuffer, strip.getModeData(mode));
|
strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
|
||||||
|
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
|
||||||
size_t len = strlen(lineBuffer);
|
size_t len = strlen(lineBuffer);
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
for (; j < maxLen && j < len; j++) {
|
for (; j < maxLen && j < len; j++) {
|
||||||
@@ -253,6 +254,12 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
|
|||||||
} else return 0;
|
} else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) {
|
||||||
|
snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode);
|
||||||
|
dest[maxLen-1] = '\0';
|
||||||
|
return strlen(dest);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t qComma = 0;
|
uint8_t qComma = 0;
|
||||||
bool insideQuotes = false;
|
bool insideQuotes = false;
|
||||||
uint8_t printedChars = 0;
|
uint8_t printedChars = 0;
|
||||||
@@ -363,9 +370,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
|
|||||||
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
|
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
|
||||||
{
|
{
|
||||||
if (mode < strip.getModeCount()) {
|
if (mode < strip.getModeCount()) {
|
||||||
char lineBuffer[128] = "";
|
char lineBuffer[256] = { '\0' };
|
||||||
strncpy_P(lineBuffer, strip.getModeData(mode), 127);
|
strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
|
||||||
lineBuffer[127] = '\0'; // terminate string
|
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
|
||||||
if (lineBuffer[0] != 0) {
|
if (lineBuffer[0] != 0) {
|
||||||
char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data
|
char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data
|
||||||
if (!startPtr) return -1;
|
if (!startPtr) return -1;
|
||||||
@@ -381,6 +388,16 @@ int16_t extractModeDefaults(uint8_t mode, const char *segVar)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void checkSettingsPIN(const char* pin) {
|
||||||
|
if (!pin) return;
|
||||||
|
if (!correctPIN && millis() - lastEditTime < PIN_RETRY_COOLDOWN) return; // guard against PIN brute force
|
||||||
|
bool correctBefore = correctPIN;
|
||||||
|
correctPIN = (strlen(settingsPIN) == 0 || strncmp(settingsPIN, pin, 4) == 0);
|
||||||
|
if (correctBefore != correctPIN) createEditHandler(correctPIN);
|
||||||
|
lastEditTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint16_t crc16(const unsigned char* data_p, size_t length) {
|
uint16_t crc16(const unsigned char* data_p, size_t length) {
|
||||||
uint8_t x;
|
uint8_t x;
|
||||||
uint16_t crc = 0xFFFF;
|
uint16_t crc = 0xFFFF;
|
||||||
@@ -401,9 +418,9 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
|
|||||||
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
|
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
|
||||||
typedef enum UM_SoundSimulations {
|
typedef enum UM_SoundSimulations {
|
||||||
UMS_BeatSin = 0,
|
UMS_BeatSin = 0,
|
||||||
UMS_WeWillRockYou
|
UMS_WeWillRockYou,
|
||||||
//UMS_10_13,
|
UMS_10_13,
|
||||||
//UMS_14_3
|
UMS_14_3
|
||||||
} um_soundSimulations_t;
|
} um_soundSimulations_t;
|
||||||
|
|
||||||
um_data_t* simulateSound(uint8_t simulationId)
|
um_data_t* simulateSound(uint8_t simulationId)
|
||||||
@@ -491,7 +508,7 @@ um_data_t* simulateSound(uint8_t simulationId)
|
|||||||
fftResult[i] = 0;
|
fftResult[i] = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/*case UMS_10_3:
|
case UMS_10_13:
|
||||||
for (int i = 0; i<16; i++)
|
for (int i = 0; i<16; i++)
|
||||||
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
|
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
|
||||||
volumeSmth = fftResult[8];
|
volumeSmth = fftResult[8];
|
||||||
@@ -500,11 +517,11 @@ um_data_t* simulateSound(uint8_t simulationId)
|
|||||||
for (int i = 0; i<16; i++)
|
for (int i = 0; i<16; i++)
|
||||||
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
|
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
|
||||||
volumeSmth = fftResult[8];
|
volumeSmth = fftResult[8];
|
||||||
break;*/
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
samplePeak = random8() > 250;
|
samplePeak = random8() > 250;
|
||||||
FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // WLEDMM 21hz...8200hz
|
FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // walk thru full range of 21hz...8200hz
|
||||||
maxVol = 31; // this gets feedback fro UI
|
maxVol = 31; // this gets feedback fro UI
|
||||||
binNum = 8; // this gets feedback fro UI
|
binNum = 8; // this gets feedback fro UI
|
||||||
volumeRaw = volumeSmth;
|
volumeRaw = volumeSmth;
|
||||||
@@ -545,6 +562,20 @@ CRGB getCRGBForBand(int x, uint8_t *fftResult, int pal) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a new, random color wheel index with a minimum distance of 42 from pos.
|
||||||
|
*/
|
||||||
|
uint8_t get_random_wheel_index(uint8_t pos) {
|
||||||
|
uint8_t r = 0, x = 0, y = 0, d = 0;
|
||||||
|
while (d < 42) {
|
||||||
|
r = random8();
|
||||||
|
x = abs(pos - r);
|
||||||
|
y = 255 - x;
|
||||||
|
d = MIN(x, y);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
// WLEDMM extended "trim string" function to support enumerateLedmaps
|
// WLEDMM extended "trim string" function to support enumerateLedmaps
|
||||||
// The function takes char* as input, and removes all leading and trailing "decorations" like spaces, tabs, line endings, quotes, colons
|
// The function takes char* as input, and removes all leading and trailing "decorations" like spaces, tabs, line endings, quotes, colons
|
||||||
// The conversion is "in place" (destructive).
|
// The conversion is "in place" (destructive).
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// version code in format yymmddb (b = daily build)
|
// version code in format yymmddb (b = daily build)
|
||||||
#define VERSION 2402252
|
#define VERSION 2404161
|
||||||
|
|
||||||
// WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED.
|
// WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED.
|
||||||
#define _MoonModules_WLED_
|
#define _MoonModules_WLED_
|
||||||
|
|||||||
@@ -220,6 +220,8 @@ void initServer()
|
|||||||
|
|
||||||
if (verboseResponse) {
|
if (verboseResponse) {
|
||||||
if (!isConfig) {
|
if (!isConfig) {
|
||||||
|
lastInterfaceUpdate = millis(); // prevent WS update until cooldown
|
||||||
|
interfaceUpdateCallMode = CALL_MODE_WS_SEND; // schedule WS update
|
||||||
serveJson(request); return; //if JSON contains "v"
|
serveJson(request); return; //if JSON contains "v"
|
||||||
} else {
|
} else {
|
||||||
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
||||||
|
|||||||
@@ -62,14 +62,15 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
|||||||
}
|
}
|
||||||
releaseJSONBufferLock(); // will clean fileDoc
|
releaseJSONBufferLock(); // will clean fileDoc
|
||||||
|
|
||||||
// force broadcast in 500ms after updating client
|
if (!interfaceUpdateCallMode) { // individual client response only needed if no WS broadcast soon
|
||||||
if (verboseResponse) {
|
if (verboseResponse) {
|
||||||
sendDataWs(client);
|
sendDataWs(client);
|
||||||
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
|
} else {
|
||||||
} else {
|
// we have to send something back otherwise WS connection closes
|
||||||
// we have to send something back otherwise WS connection closes
|
client->text(F("{\"success\":true}"));
|
||||||
client->text(F("{\"success\":true}"));
|
}
|
||||||
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
|
// force broadcast in 500ms after updating client
|
||||||
|
//lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); // ESP8266 does not like this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -161,6 +162,7 @@ void sendDataWs(AsyncWebSocketClient * client)
|
|||||||
ws.closeAll(1013); //code 1013 = temporary overload, try again later
|
ws.closeAll(1013); //code 1013 = temporary overload, try again later
|
||||||
ws.cleanupClients(0); //disconnect all clients to release memory
|
ws.cleanupClients(0); //disconnect all clients to release memory
|
||||||
ws._cleanBuffers();
|
ws._cleanBuffers();
|
||||||
|
errorFlag = ERR_LOW_WS_MEM;
|
||||||
return; //out of memory
|
return; //out of memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user