diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py
index 4df40ed4..3a55ced8 100644
--- a/pio-scripts/output_bins.py
+++ b/pio-scripts/output_bins.py
@@ -22,6 +22,88 @@ def _create_dirs(dirs=["firmware", "map"]):
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
+
+# trick for py2/3 compatibility
+if 'basestring' not in globals():
+ basestring = str
+
+# WLEDMM : custom print function
+def print_my_item(items, flag = False):
+ if flag: print(" -D", end='')
+ else: print(" ", end='')
+ if isinstance(items, basestring):
+ # print a single string
+ print(items, end='')
+ else:
+ # print a list
+ first = True
+ for item in items:
+ if not first: print("=", end='')
+ print(item, end='')
+ first = False
+
+# WLEDMM : dump out buildflags : usermods, disable, enable, use_..
+def wledmm_print_build_info(env):
+ all_flags = env["CPPDEFINES"]
+ first = True
+
+ found = False
+ for item in all_flags:
+ if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0]:
+ if first: print("\nUsermods and Features:")
+ print_my_item(item)
+ first = False
+ found = True
+ if found: print("")
+
+ found = False
+ for item in all_flags:
+ if 'USERMOD_' in item or 'UM_' in item:
+ if first: print("\nUsermods and Features:")
+ print_my_item(item)
+ first = False
+ found = True
+ if found: print("")
+
+ found = False
+ for item in all_flags:
+ if 'WLED_DISABLE' in item or 'WIFI_FIX' in item:
+ if first: print("\nUsermods and Features:")
+ print_my_item(item)
+ first = False
+ found = True
+ if found: print("")
+
+ found = False
+ for item in all_flags:
+ if 'WLED_' in item or 'WLED_' in item[0] or 'MAX_LED' in item[0]:
+ if not 'WLED_RELEASE_NAME' in item[0] and not 'WLED_VERSION' in item[0] and not 'WLED_WATCHDOG_TIMEOUT' in item[0] and not 'WLED_DISABLE' in item and not 'WLED_USE_MY_CONFIG' in item and not 'ARDUINO_PARTITION' in item:
+ if first: print("\nUsermods and Features:")
+ print_my_item(item)
+ first = False
+ found = True
+ if found: print("")
+
+ first = True
+ found = False
+ for item in all_flags:
+ if 'WLEDMM_' in item[0] or 'WLEDMM_' in item or 'TROYHACKS' in item:
+ if first: print("\nWLEDMM Features:")
+ print_my_item(item)
+ first = False
+ found = True
+ if found: print("\n")
+
+def wledmm_print_all_defines(env):
+ all_flags = env["CPPDEFINES"]
+ found = False
+ for item in all_flags:
+ if not found: print("\nBuild Flags:")
+ print_my_item(item, True)
+ found = True
+ if found: print("\n")
+
+
def bin_rename_copy(source, target, env):
_create_dirs()
variant = env["PIOENV"]
@@ -56,6 +138,9 @@ def bin_rename_copy(source, target, env):
print(f"Found linker mapfile {source_map}")
shutil.copy(source_map, map_file)
+ # wledmm_print_all_defines(env)
+ # wledmm_print_build_info(env)
+
def bin_gzip(source, target, env):
_create_dirs()
variant = env["PIOENV"]
diff --git a/platformio.ini b/platformio.ini
index 9471b484..5ada7d47 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -305,6 +305,8 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
+ ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> ~15% faster on "V3" builds - may flicker a bit more
+ ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv ;; WLED standard for 4MB flash: 1.4MB firmware, 1MB filesystem
@@ -322,6 +324,7 @@ lib_deps =
; WLEDMM specific: use patched version of lorol LittleFS
https://github.com/softhack007/LITTLEFS-threadsafe.git#master
makuna/NeoPixelBus @ 2.7.5
+ ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
;; Compatibility with upstream --> you should prefer using ${common_mm.build_flags_S} and ${common_mm.lib_deps_S}
@@ -348,6 +351,8 @@ build_flagsV4 = -g
-DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE
+ ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups
+ ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
-D NO_GFX ; Disable the use of Adafruit_GFX by the HUB75 driver
@@ -355,6 +360,7 @@ build_flagsV4 = -g
lib_depsV4 =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ;; WLEDMM this must be first in the list, otherwise Aircoookie/ESPAsyncWebServer pulls in an older version of AsyncTCP !!
makuna/NeoPixelBus @ 2.7.5
+ ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${common_mm.HUB75_lib_deps}
${env.lib_deps}
@@ -376,10 +382,13 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
+ ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups
+ ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
makuna/NeoPixelBus @ 2.7.5
+ ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
[esp32s2]
@@ -406,7 +415,8 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
- makuna/NeoPixelBus @ 2.7.5
+ ;; makuna/NeoPixelBus @ 2.7.5 ;; standard
+ makuna/NeoPixelBus @ 2.7.9 ;; experimental - reduces LED glitches on -S2
${env.lib_deps}
[esp32c3]
@@ -428,6 +438,7 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
makuna/NeoPixelBus @ 2.7.5
+ ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
[esp32s3]
@@ -449,6 +460,7 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
makuna/NeoPixelBus @ 2.7.5
+ ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
@@ -2464,16 +2476,23 @@ lib_ignore =
[env:adafruit_matrixportal_esp32s3]
+; ESP32-S3 processor, 8 MB flash, 2 MB of PSRAM, dedicated driver pins for HUB75
extends = esp32_4MB_V4_M_base
platform = ${esp32.platformV4_xp} ;; 6.5.0 = first platform release supporting matrixportal
platform_packages = ${esp32.platformV4_packages_xp} ;; arduino-esp32 2.0.14 needed - previous versions were missing files for matrixportal
board = adafruit_matrixportal_esp32s3
+board_build.partitions = ${esp32.large_partitions}
+board_build.f_flash = 80000000L
+board_build.flash_mode = qio
+
build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" S3 buildenv
+ -D ARDUINO_USB_CDC_ON_BOOT=1 ;; fix warning: "ARDUINO_USB_CDC_ON_BOOT" redefined; comment out for Serial debug
build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation
${common_mm.build_flags_S}
-D WLED_RELEASE_NAME=matrixportal_esp32s3
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
+ ; Serial debug enabled -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
+ -D ARDUINO_USB_CDC_ON_BOOT=0
-D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor)
${common_mm.animartrix_build_flags}
${common_mm.build_disable_sync_interfaces}
diff --git a/tools/ESP32-Chip_info.hpp b/tools/ESP32-Chip_info.hpp
index 89316598..417ee449 100644
--- a/tools/ESP32-Chip_info.hpp
+++ b/tools/ESP32-Chip_info.hpp
@@ -543,6 +543,8 @@ void show_psram_info_part2(void)
void showRealSpeed() {
//Serial.begin(115200);
+ if (!Serial) return; // Avoid writing to unconnected USB-CDC
+
Serial.flush();
Serial.println(F("\n"));
for(int aa=0; aa<65; aa++) Serial.print("="); Serial.println();
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 1af0d22f..82211e82 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -1,5 +1,24 @@
#pragma once
+/*
+ @title MoonModules WLED - audioreactive usermod
+ @file audio_reactive.h
+ @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED
+ @Authors https://github.com/MoonModules/WLED/commits/mdev/
+ @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details)
+ @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
+
+ This file is part of the MoonModules WLED fork also known as "WLED-MM".
+ WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+ WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with WLED-MM. If not, see .
+*/
+
+
#include "wled.h"
#ifdef ARDUINO_ARCH_ESP32
@@ -353,9 +372,6 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul
// These are the input and output vectors. Input vectors receive computed results from FFT.
static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins
static float vImag[samplesFFT] = {0.0f}; // imaginary parts
-#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
-static float windowWeighingFactors[samplesFFT] = {0.0f};
-#endif
#ifdef FFT_MAJORPEAK_HUMAN_EAR
static float pinkFactors[samplesFFT] = {0.0f}; // "pink noise" correction factors
@@ -381,7 +397,14 @@ constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range o
#include
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
-static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors);
+#if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19
+ // arduinoFFT 2.x has a slightly different API
+ static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, true);
+#else
+ // recommended version optimized by @softhack007 (API version 1.9)
+ static float windowWeighingFactors[samplesFFT] = {0.0f}; // cache for FFT windowing factors
+ static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors);
+#endif
#else
static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE);
#endif
@@ -627,9 +650,14 @@ void FFTcode(void * parameter)
#endif
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
- FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
+ #if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19
+ // arduinoFFT 2.x has a slightly different API
+ FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude);
#else
- FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
+ FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
+ #endif
+ #else
+ FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude);
#endif
if (FFT_MajorPeak < (SAMPLE_RATE / samplesFFT)) {FFT_MajorPeak = 1.0f; FFT_Magnitude = 0;} // too low - use zero
@@ -921,7 +949,7 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
if (post_gain < 1.0f) post_gain = ((post_gain -1.0f) * 0.8f) +1.0f;
currentResult *= post_gain;
}
- fftResult[i] = constrain((int)currentResult, 0, 255);
+ fftResult[i] = max(min((int)(currentResult+0.5f), 255), 0); // +0.5 for proper rounding
}
}
////////////////////
@@ -1571,7 +1599,7 @@ class AudioReactive : public Usermod {
transmitData.zeroCrossingCount = zeroCrossingCount;
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
- transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254);
+ transmitData.fftResult[i] = fftResult[i];
}
transmitData.FFT_Magnitude = my_magnitude;
diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h
index 38a2ef72..54d357e7 100644
--- a/usermods/audioreactive/audio_source.h
+++ b/usermods/audioreactive/audio_source.h
@@ -1,4 +1,25 @@
#pragma once
+
+/*
+ @title MoonModules WLED - audioreactive usermod
+ @file audio_source.h
+ @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED
+ @Authors https://github.com/MoonModules/WLED/commits/mdev/
+ @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details)
+ @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
+
+ This file is part of the MoonModules WLED fork also known as "WLED-MM".
+ WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+ WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with WLED-MM. If not, see .
+
+*/
+
+
#ifdef ARDUINO_ARCH_ESP32
#include
#include "wled.h"
diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
index d08f295f..adb5b37e 100644
--- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
+++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
@@ -1,5 +1,25 @@
#pragma once
+/*
+ @title MoonModules WLED - auto-playlist usermod
+ @file usermod_v2_auto_playlist.h
+ @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED
+ @Authors https://github.com/MoonModules/WLED/commits/mdev/
+ @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details)
+ @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
+
+ This file is part of the MoonModules WLED fork also known as "WLED-MM".
+ WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+ WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with WLED-MM. If not, see .
+
+*/
+
+
#ifdef WLED_DEBUG
#ifndef USERMOD_AUTO_PLAYLIST_DEBUG
#define USERMOD_AUTO_PLAYLIST_DEBUG
@@ -302,6 +322,8 @@ class AutoPlaylistUsermod : public Usermod {
if (bri == 0) return;
+ if(!functionality_enabled) return;
+
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 0565c0b2..77efb26f 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -1333,12 +1333,12 @@ uint16_t mode_fire_flicker(void) {
byte r = (SEGCOLOR(0) >> 16);
byte g = (SEGCOLOR(0) >> 8);
byte b = (SEGCOLOR(0) );
- byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255;
+ byte lum = (SEGMENT.palette == 0) ? max(w, max(r, max(g, b))) : 255;
lum /= (((256-SEGMENT.intensity)/16)+1);
for (int i = 0; i < SEGLEN; i++) {
byte flicker = random8(lum);
if (SEGMENT.palette == 0) {
- SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0));
+ SEGMENT.setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0));
} else {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker));
}
@@ -1369,7 +1369,7 @@ uint16_t gradient_base(bool loading) {
{
val = abs(((i>pp) ? p2:pp) -i);
} else {
- val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i)));
+ val = min(abs(pp-i), min(abs(p1-i), abs(p2-i)));
}
val = (brd > val) ? val/brd * 255 : 255;
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val));
@@ -2129,7 +2129,7 @@ uint16_t mode_fire_2012() {
// Step 4. Map from heat cells to LED colors
for (int j = 0; j < SEGLEN; j++) {
- SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, NOBLEND));
+ SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j],byte(240)), 255, NOBLEND));
}
}
};
@@ -2971,7 +2971,7 @@ uint16_t mode_bouncing_balls(void) {
uint32_t color = SEGCOLOR(0);
if (SEGMENT.palette) {
- color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8)));
+ color = SEGMENT.color_wheel(i*(256/max(numBalls, uint16_t(8))));
} else if (hasCol2) {
color = SEGCOLOR(i % NUM_COLORS);
}
@@ -3696,6 +3696,7 @@ uint16_t mode_drip(void)
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
Spark* drops = reinterpret_cast(SEGENV.data);
+ if (SEGENV.call == 0) SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
struct virtualStrip {
@@ -3703,9 +3704,9 @@ uint16_t mode_drip(void)
uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
- float gravity = -0.0005 - (SEGMENT.speed/25000.0); //increased gravity (50000 to 25000)
- gravity *= max(1, SEGLEN-1);
- int sourcedrop = 12;
+ float gravity = -0.0005f - (float(SEGMENT.speed)/35000.0f); //increased gravity (50000 to 35000)
+ gravity *= min(max(1, SEGLEN-1), 255); //WLEDMM speed limit 255
+ const int sourcedrop = 12;
for (int j=0;j 1% ... 20% probalibity
drops[j].colIndex=2; //fall
drops[j].col=255;
}
}
if (drops[j].colIndex > 1) { // falling
- if (drops[j].pos > 0) { // fall until end of segment
+ if (drops[j].pos > 0.01f) { // fall until end of segment
drops[j].pos += drops[j].vel;
if (drops[j].pos < 0) drops[j].pos = 0;
drops[j].vel += gravity; // gravity is negative
for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
- uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
+ int intPos = roundf(drops[j].pos) +i; // WLEDMM round it first
+ uint16_t pos = constrain(intPos, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally // WLEDMM bad cast to uint16_t removed
SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,dropColor,drops[j].col/i)); //spread pixel with fade while falling
}
if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
- SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK,drops[j].col));
+ SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK, (2 * drops[j].col)/3)); // WLEDMM reduced brightness
}
} else { // we hit bottom
if (drops[j].colIndex > 2) { // already hit once, so back to forming
@@ -3768,7 +3771,7 @@ uint16_t mode_drip(void)
return FRAMETIME;
}
-static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,,,,,Overlay;!,!;!;1.5d;m12=1"; //bar WLEDMM 1.5d
+static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,Fall ratio,,,,Overlay;!,!;!;1.5d;c1=127,m12=1"; //bar WLEDMM 1.5d
/*
@@ -5096,25 +5099,25 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline
SEGMENT.fill(BLACK);
}
- uint16_t xscale = SEGMENT.intensity*4;
- uint32_t yscale = SEGMENT.speed*8;
- uint8_t indexx = 0;
+ unsigned xscale = SEGMENT.intensity*4;
+ unsigned yscale = SEGMENT.speed*8;
+ unsigned indexx = 0;
- SEGPALETTE = CRGBPalette16( CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0),
- CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange,
- CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
- CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
+ CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : CRGBPalette16(CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black,
+ CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange,
+ CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
+ CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
for (int j=0; j < cols; j++) {
for (int i=0; i < rows; i++) {
- indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map.
- SEGMENT.setPixelColorXY(j, i, ColorFromPalette(SEGPALETTE, min(i*(indexx)>>4, 255), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED.
+ indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map.
+ SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*(indexx)>>4, 255U), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED.
} // for i
} // for j
return FRAMETIME;
} // mode_2Dfirenoise()
-static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;!;2;pal=0"; //WLEDMM pal=0
+static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale,,,,Palette;;!;2;pal=0"; //WLEDMM pal=0
//////////////////////////////
@@ -5147,117 +5150,191 @@ static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y f
///////////////////////////////////////////
// 2D Cellular Automata Game of life //
///////////////////////////////////////////
-typedef struct ColorCount {
- CRGB color;
- int8_t count;
-} colorCount;
-
-uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color
+static bool getBitValue(const uint8_t* byteArray, size_t n) {
+ size_t byteIndex = n / 8;
+ size_t bitIndex = n % 8;
+ uint8_t byte = byteArray[byteIndex];
+ return (byte >> bitIndex) & 1;
+}
+static void setBitValue(uint8_t* byteArray, size_t n, bool value) {
+ size_t byteIndex = n / 8;
+ size_t bitIndex = n % 8;
+ if (value)
+ byteArray[byteIndex] |= (1 << bitIndex);
+ else
+ byteArray[byteIndex] &= ~(1 << bitIndex);
+}
+// create game of life struct to hold cells and future cells
+struct gameOfLife {
+ uint8_t* cells;
+ uint8_t* futureCells;
+ uint8_t gliderLength;
+ uint16_t oscillatorCRC;
+ uint16_t spaceshipCRC;
+};
+uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/
+ // and https://github.com/DougHaber/nlife-color , Modified By: Brandon Butler
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled
- const uint16_t crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi)
+ const size_t dataSize = (SEGMENT.length() / 8) + (((SEGMENT.length() % 8) != 0) ? 1 : 0); // add one byte when extra bits needed (length not a multiple of 8)
+ const size_t totalSize = dataSize*2 + sizeof(gameOfLife);
- if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed
- CRGB *prevLeds = reinterpret_cast(SEGENV.data);
- uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize);
+ if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed
+ gameOfLife* gol = reinterpret_cast(SEGENV.data);
- CRGB backgroundColor = SEGCOLOR(1);
-
- if (SEGENV.call == 0) SEGMENT.setUpLeds();
-
- if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) {
- SEGENV.step = strip.now;
- SEGENV.aux0 = 0;
- random16_set_seed(strip.now>>2); //seed the random generator
-
- //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen)
- for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
- uint8_t state = random8()%2;
- if (state == 0)
- SEGMENT.setPixelColorXY(x,y, backgroundColor);
- else
- SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16()); //WLEDMM support all colors
- }
-
- for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black;
- memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen);
- } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) {
- // update only when appropriate time passes (in 42 FPS slots)
- return FRAMETIME;
+ if (gol->cells == nullptr) {
+ gol->cells = new uint8_t[dataSize];
+ gol->futureCells = new uint8_t[dataSize];
}
- //copy previous leds (save previous generation)
- //NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset
- for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y);
+ uint16_t &generation = SEGENV.aux0; //rename aux0 and aux1 for readability (not needed)
+ uint16_t &pauseFrames = SEGENV.aux1;
+ CRGB backgroundColor = SEGCOLOR(1);
+ CRGB color;
- //calculate new leds
+ if (SEGENV.call == 0) {
+ SEGMENT.setUpLeds();
+ SEGMENT.fill(BLACK); // to make sure that segment buffer and physical leds are aligned initially
+ }
+ //start new game of life
+ if ((SEGENV.call == 0 || generation == 0) && pauseFrames == 0) {
+ SEGENV.step = strip.now; // .step = previous call time
+ generation = 1;
+ pauseFrames = 75; // show initial state for longer
+ random16_set_seed(strip.now>>2); //seed the random generator
+ //Setup Grid
+ for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
+ uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive
+ if (state == 0) {
+ setBitValue(gol->cells, y * cols + x, false);
+ setBitValue(gol->futureCells, y * cols + x, false);
+ if (SEGMENT.check2) continue;
+ SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0));
+ }
+ else {
+ setBitValue(gol->cells, y * cols + x, true);
+ setBitValue(gol->futureCells, y * cols + x, true);
+ color = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0);
+ SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0));
+ }
+ }
+
+ //Clear CRCs
+ gol->oscillatorCRC = 0;
+ gol->spaceshipCRC = 0;
+
+ //Calculate glider length LCM(rows,cols)*4
+ uint8_t a = rows;
+ uint8_t b = cols;
+ while (b) {
+ uint8_t t = b;
+ b = a % b;
+ a = t;
+ }
+ gol->gliderLength = cols * rows / a * 4;
+ return FRAMETIME;
+ }
+ //Redraw immediately if overlay to avoid flicker
+ if (SEGMENT.check2) {
+ for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
+ //redraw foreground/alive
+ if (getBitValue(gol->cells, y * cols + x)) {
+ color = SEGMENT.getPixelColorXY(x,y);
+ SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0));
+ }
+ }
+ }
+ if (pauseFrames || strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,2)) {
+ if(pauseFrames) pauseFrames--;
+ return FRAMETIME; //skip if not enough time has passed
+ }
+ //Update Game of Life
+ bool cellChanged = false; // Detect still live and dead grids
+ //cell index and coordinates
+ uint16_t cIndex;
+ uint16_t cX;
+ uint16_t cY;
+ //Loop through all cells. Count neighbors, apply rules, setPixel
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
+ byte neighbors = 0;
+ byte colorCount = 0; //track number of valid colors
+ CRGB nColors[3]; // track 3 colors, dying cells may overwrite but this wont be used
- colorCount colorsCount[9]; // count the different colors in the 3*3 matrix
- for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount
-
- // iterate through neighbors and count them and their different colors
- int neighbors = 0;
for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix
if (i==0 && j==0) continue; // ignore itself
- // wrap around segment
- int16_t xx = x+i, yy = y+j;
- if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0;
- if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0;
-
- uint16_t xy = XY(xx, yy); // previous cell xy to check
- // count different neighbours and colors
- if (prevLeds[xy] != backgroundColor) {
- neighbors++;
- bool colorFound = false;
- int k;
- for (k=0; k<9 && colorsCount[i].count != 0; k++)
- if (colorsCount[k].color == prevLeds[xy]) {
- colorsCount[k].count++;
- colorFound = true;
- }
- if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array
+ if (!SEGMENT.check3 || generation % 1500 == 0) { //no wrap disable wrap every 1500 generations to prevent undetected repeats
+ cX = x+i;
+ cY = y+j;
+ if (cX < 0 || cY < 0 || cX >= cols || cY >= rows) continue; //skip if out of bounds
+ } else { //wrap around
+ cX = (x+i+cols) % cols;
+ cY = (y+j+rows) % rows;
}
- } // i,j
+ cIndex = cY * cols + cX;
+ // count neighbors and store upto 3 neighbor colors
+ if (getBitValue(gol->cells, cIndex)) { //if alive
+ neighbors++;
+ color = SEGMENT.getPixelColorXY(cX, cY);
+ if (color == backgroundColor) continue; //parent just died, color lost
+ nColors[colorCount%3] = color;
+ colorCount++;
+ }
+ }
// Rules of Life
- CRGB preCol = prevLeds[XY(x,y)];
- uint32_t col = RGBW32(preCol.r, preCol.g, preCol.b, 0); // WLEDMM explicit color conversion CRGB -> RGB
- uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0);
- if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness
- else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
- else if ((col == bgc) && (neighbors == 3)) { // Reproduction
+ bool cellValue = getBitValue(gol->cells, y * cols + x);
+ if ((cellValue) && (neighbors < 2 || neighbors > 3)) {
+ // Loneliness or overpopulation
+ cellChanged = true;
+ setBitValue(gol->futureCells, y * cols + x, false);
+ if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0));
+ }
+ else if (!(cellValue) && (neighbors == 3)) {
+ // Reproduction
+ setBitValue(gol->futureCells, y * cols + x, true);
+ cellChanged = true;
// find dominant color and assign it to a cell
- colorCount dominantColorCount = {backgroundColor, 0};
- for (int i=0; i<9 && colorsCount[i].count != 0; i++)
- if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i];
- // assign the dominant color w/ a bit of randomness to avoid "gliders"
- if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color);
- } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation
- SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255));
- }
- // else do nothing!
- } //x,y
+ // no longer storing colors, if parent dies the color is lost
+ CRGB dominantColor;
+ if (colorCount == 3) { //All parents survived
+ if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0];
+ else if (nColors[1] == nColors[2]) dominantColor = nColors[1];
+ else dominantColor = nColors[random8()%3];
+ }
+ else if (colorCount == 2) dominantColor = nColors[random8()%2]; // 1 leading parent died
+ else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents died
+ else dominantColor = color; // all parents died last used color
+ // mutate color chance
+ if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16();
+
+ if (SEGMENT.check1) dominantColor = RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0); //WLEDMM support all colors
+ SEGMENT.setPixelColorXY(x,y, dominantColor);
+ }
+ }
+ //update cell values
+ memcpy(gol->cells, gol->futureCells, dataSize);
+
+ // Get current crc value
+ uint16_t crc = crc16((const unsigned char*)gol->cells, dataSize);
- // calculate CRC16 of leds
- uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize);
- // check if we had same CRC and reset if needed
bool repetition = false;
- for (int i=0; i softhack007: not exacly. Different CRC means different image; same CRC means nothing (could be same or slightly different).
- if (!repetition) SEGENV.step = strip.now; //if no repetition avoid reset
- // remember CRCs across frames
- crcBuffer[SEGENV.aux0] = crc;
- ++SEGENV.aux0 %= crcBufferLen;
+ if (!cellChanged || crc == gol->oscillatorCRC || crc == gol->spaceshipCRC) repetition = true; //check if cell changed this gen and compare previous stored crc values
+ if (repetition) {
+ generation = 0; // reset on next call
+ pauseFrames = 50;
+ return FRAMETIME;
+ }
+ // Update CRC values
+ if (generation % 16 == 0) gol->oscillatorCRC = crc;
+ if (generation % gol->gliderLength == 0) gol->spaceshipCRC = crc;
+ generation++;
+ SEGENV.step = strip.now;
return FRAMETIME;
} // mode_2Dgameoflife()
-static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,,,,,All colors ☾;!,!;!;2;c1=0"; //WLEDMM support all colors
-
+static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,,,,All Colors ☾,Overlay ☾,Wrap ☾,;!,!;!;2;sx=200,ix=12,c1=0,o3=1";
/////////////////////////
// 2D Hiphotic //
@@ -5956,19 +6033,20 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2
// 2D Crazy Bees //
/////////////////////////
//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek)
-#define MAX_BEES 5
+constexpr uint_fast16_t MAX_BEES = 5;
uint16_t mode_2Dcrazybees(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
- const uint16_t cols = SEGMENT.virtualWidth();
- const uint16_t rows = SEGMENT.virtualHeight();
+ const uint_fast16_t cols = SEGMENT.virtualWidth();
+ const uint_fast16_t rows = SEGMENT.virtualHeight();
- byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1);
+ const byte n = min(MAX_BEES, (rows * cols) / 256 + 1);
typedef struct Bee {
uint8_t posX, posY, aimX, aimY, hue;
- int8_t deltaX, deltaY, signX, signY, error;
- void aimed(uint16_t w, uint16_t h) {
+ int8_t signX, signY;
+ int16_t deltaX, deltaY, error;
+ void aimed(uint_fast16_t w, uint_fast16_t h) {
if (!true) //WLEDMM SuperSync
random16_set_seed(strip.now);
aimX = random8(0, w);
@@ -6009,7 +6087,7 @@ uint16_t mode_2Dcrazybees(void) {
SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, CHSV(bee[i].hue, 255, 255));
if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) {
SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255)));
- int8_t error2 = bee[i].error * 2;
+ int_fast16_t error2 = bee[i].error * 2;
if (error2 > -bee[i].deltaY) {
bee[i].error -= bee[i].deltaY;
bee[i].posX += bee[i].signX;
@@ -6170,7 +6248,7 @@ uint16_t mode_2Dfloatingblobs(void) {
if (blob->grow[i]) {
// enlarge radius until it is >= 4
blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
- if (blob->r[i] >= MIN(cols/4.f,2.f)) {
+ if (blob->r[i] >= min(cols/4.f,2.f)) {
blob->grow[i] = false;
}
} else {
diff --git a/wled00/FX.h b/wled00/FX.h
index 037ef181..b8864db4 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -106,7 +106,7 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.
//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x])
//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength()
#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */
-#define SEGPALETTE strip._currentPalette
+#define SEGPALETTE Segment::getCurrentPalette()
#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */
#define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN)
@@ -355,7 +355,7 @@ typedef enum mapping1D2D {
M12_jMap = 4, //WLEDMM jMap
M12_sCircle = 5, //WLEDMM Circle
M12_sBlock = 6, //WLEDMM Block
- M12_sPinWheel = 7 //WLEDMM PinWheel
+ M12_sPinwheel = 7 //WLEDMM Pinwheel
} mapping1D2D_t;
// segment, 72 bytes
@@ -428,6 +428,9 @@ typedef struct Segment {
size_t _dataLen; // WLEDMM uint16_t is too small
static size_t _usedSegmentData; // WLEDMM uint16_t is too small
+ // perhaps this should be per segment, not static
+ static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette())
+
// transition data, valid only if transitional==true, holds values during transition
struct Transition {
uint32_t _colorT[NUM_COLORS];
@@ -561,6 +564,7 @@ typedef struct Segment {
static void addUsedSegmentData(int len) { _usedSegmentData += len; }
void allocLeds(); //WLEDMM
+ inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; }
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed
@@ -605,7 +609,7 @@ typedef struct Segment {
uint8_t currentMode(uint8_t modeNew);
uint32_t currentColor(uint8_t slot, uint32_t colorNew);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
- CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID);
+ void setCurrentPalette(void);
// 1D strip
uint16_t virtualLength(void) const;
@@ -759,7 +763,6 @@ class WS2812FX { // 96 bytes
panels(1),
#endif
// semi-private (just obscured) used in effect functions through macros
- _currentPalette(CRGBPalette16(CRGB::Black)),
_colors_t{0,0,0},
_virtualSegmentLength(0),
// true private variables
@@ -978,7 +981,6 @@ class WS2812FX { // 96 bytes
// end 2D support
void loadCustomPalettes(void); // loads custom palettes from JSON
- CRGBPalette16 _currentPalette; // palette used for current effect (includes transition)
std::vector customPalettes; // TODO: move custom palettes out of WS2812FX class
// using public variables to reduce code size increase due to inline function getSegment() (with bounds checking)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 01af8b52..34e59e40 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -95,6 +95,8 @@ CRGB *Segment::_globalLeds = nullptr;
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
uint16_t Segment::maxHeight = 1;
+CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black);
+
// copy constructor - creates a new segment by copy from orig, but does not copy buffers. Does not modify orig!
Segment::Segment(const Segment &orig) {
DEBUG_PRINTLN(F("-- Copy segment constructor --"));
@@ -295,7 +297,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK));
byte tcp[76] = { 255 }; //WLEDMM: prevent out-of-range access in loadDynamicGradientPalette()
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
- if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0;
+ if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip
//default palette. Differs depending on effect
if (pal == 0) switch (mode) {
case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette
@@ -458,18 +460,17 @@ uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
}
-CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
- loadPalette(targetPalette, pal);
+void Segment::setCurrentPalette() {
+ loadPalette(_currentPalette, palette);
if (transitional && _t && progress() < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
- for (int i=0; i_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
- targetPalette = _t->_palT; // copy transitioning/temporary palette
+ for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, _currentPalette, 48);
+ _currentPalette = _t->_palT; // copy transitioning/temporary palette
}
- return targetPalette;
}
void Segment::handleTransition() {
@@ -793,14 +794,41 @@ void Segment::deletejMap() {
}
-// WLEDMM constants for mapping mode "Pinwheel"
-constexpr int Pinwheel_Steps_Medium = 208; // no holes up to 32x32; 60fps
-constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big"
-constexpr int Pinwheel_Steps_Big = 360; // no holes expected up to 58x58; 40fps
-constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...208 to Radians
-constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...360 to Radians
-// WLEDMM end
+// Constants for mapping mode "Pinwheel"
+#ifndef WLED_DISABLE_2D
+constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16
+constexpr int Pinwheel_Size_Small = 16; // larger than this -> use "Medium"
+constexpr int Pinwheel_Steps_Medium = 192; // no holes up to 32x32
+constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big"
+constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50
+constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL"
+constexpr int Pinwheel_Steps_XL = 368;
+constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians
+constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians
+constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians
+constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians
+constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction)
+
+// Pinwheel helper function: pixel index to radians
+static float getPinwheelAngle(int i, int vW, int vH) {
+ int maxXY = max(vW, vH);
+ if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small;
+ if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med;
+ if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big;
+ // else
+ return float(i) * Int_to_Rad_XL;
+}
+// Pinwheel helper function: matrix dimensions to number of rays
+static int getPinwheelLength(int vW, int vH) {
+ int maxXY = max(vW, vH);
+ if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small;
+ if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium;
+ if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big;
+ // else
+ return Pinwheel_Steps_XL;
+}
+#endif
// 1D strip
uint16_t Segment::virtualLength() const {
@@ -831,12 +859,8 @@ uint16_t Segment::virtualLength() const {
else
vLen = max(vW,vH) * 0.5; // get the longest dimension
break;
- case M12_sPinWheel: //WLEDMM
- //vLen = full circle
- if (max(vW,vH) <= Pinwheel_Size_Medium)
- vLen = Pinwheel_Steps_Medium;
- else
- vLen = Pinwheel_Steps_Big;
+ case M12_sPinwheel:
+ vLen = getPinwheelLength(vW, vH);
break;
}
return vLen;
@@ -978,32 +1002,46 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT
}
}
break;
- case M12_sPinWheel: { // WLEDMM
- // i = angle --> 0 through 359 (Big), OR 0 through 208 (Medium)
+ case M12_sPinwheel: {
+ // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
float centerX = roundf((vW-1) / 2.0f);
float centerY = roundf((vH-1) / 2.0f);
- // int maxDistance = sqrt(centerX * centerX + centerY * centerY) + 1;
- float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians
+ float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians
float cosVal = cosf(angleRad);
float sinVal = sinf(angleRad);
+ // avoid re-painting the same pixel
+ int lastX = INT_MIN; // impossible position
+ int lastY = INT_MIN; // impossible position
// draw line at angle, starting at center and ending at the segment edge
// we use fixed point math for better speed. Starting distance is 0.5 for better rounding
- constexpr int_fast32_t Fixed_Scale = 512; // fixpoint scaling factor
- int_fast32_t posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point
- int_fast32_t posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point
- int_fast16_t inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point)
- int_fast16_t inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point)
+ // int_fast16_t and int_fast32_t types changed to int, minimum bits commented
+ int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
+ int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
+ int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
+ int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
- // draw until we hit any edge
- while ((posx > 0) && (posy > 0) && (posx < maxX) && (posy < maxY)) {
+
+ // Odd rays start further from center if prevRay started at center.
+ static int prevRay = INT_MIN; // previous ray number
+ if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) {
+ int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
+ posx += inc_x * jump;
+ posy += inc_y * jump;
+ }
+ prevRay = i;
+
+ // draw ray until we hit any edge
+ while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) {
// scale down to integer (compiler will replace division with appropriate bitshift)
int x = posx / Fixed_Scale;
int y = posy / Fixed_Scale;
// set pixel
- setPixelColorXY(x, y, col);
+ if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different
+ lastX = x;
+ lastY = y;
// advance to next position
posx += inc_x;
posy += inc_y;
@@ -1154,16 +1192,36 @@ uint32_t Segment::getPixelColor(int i)
else
return getPixelColorXY(vW / 2, vH / 2 - i - 1);
break;
- case M12_sPinWheel: //WLEDMM
- // not 100% accurate, returns outer edge of circle
- float distance = max(1.0f, min(vH-1, vW-1) / 2.0f);
- float centerX = (vW - 1) / 2.0f;
- float centerY = (vH - 1) / 2.0f;
- float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians
- int x = roundf(centerX + distance * cosf(angleRad));
- int y = roundf(centerY + distance * sinf(angleRad));
+ case M12_sPinwheel:
+ // not 100% accurate, returns pixel at outer edge
+ // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
+ float centerX = roundf((vW-1) / 2.0f);
+ float centerY = roundf((vH-1) / 2.0f);
+ float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians
+ float cosVal = cosf(angleRad);
+ float sinVal = sinf(angleRad);
+
+ int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
+ int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
+ int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
+ int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
+ int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
+ int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
+
+ // trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor
+ int x = INT_MIN;
+ int y = INT_MIN;
+ while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) {
+ // scale down to integer (compiler will replace division with appropriate bitshift)
+ x = posx / Fixed_Scale;
+ y = posy / Fixed_Scale;
+ // advance to next position
+ posx += inc_x;
+ posy += inc_y;
+ }
return getPixelColorXY(x, y);
- }
+ break;
+ }
return 0;
}
#endif
@@ -1460,11 +1518,7 @@ uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, u
uint_fast16_t vLen = mapping ? virtualLength() : 1;
if (mapping && vLen > 1) paletteIndex = (i*255)/(vLen -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
- CRGB fastled_col;
- CRGBPalette16 curPal;
- if (transitional && _t) curPal = _t->_palT;
- else loadPalette(curPal, palette);
- fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
+ CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0);
}
@@ -1742,7 +1796,7 @@ void WS2812FX::service() {
_colors_t[0] = seg.currentColor(0, seg.colors[0]);
_colors_t[1] = seg.currentColor(1, seg.colors[1]);
_colors_t[2] = seg.currentColor(2, seg.colors[2]);
- seg.currentPalette(_currentPalette, seg.palette);
+ seg.setCurrentPalette(); // load actual palette
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
@@ -2151,7 +2205,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
segStops[s] = segStarts[s] + b->getLength();
#ifndef WLED_DISABLE_2D
- if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
+ if (isMatrix && segStops[s] <= Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight;
#endif
@@ -2224,6 +2278,8 @@ void WS2812FX::fixInvalidSegments() {
if (_segments[i].stop > _length) _segments[i].stop = _length;
}
}
+ // if any segments were deleted free memory
+ purgeSegments();
// this is always called as the last step after finalizeInit(), update covered bus types
for (segment &seg : _segments)
seg.refreshLightCapabilities();
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 76a5c9e1..34bfa493 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -40,6 +40,11 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte
#define DEBUG_PRINTF(x...)
#endif
#else
+ // un-define USER_PRINT macros from bus_wrapper.h
+ #undef USER_PRINT
+ #undef USER_PRINTF
+ #undef USER_PRINTLN
+ #undef USER_FLUSH
// WLEDMM use wled.h
#include "wled.h"
#endif
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index 327da0ad..64dcd5b9 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -18,6 +18,28 @@
#endif
// temporary end
+// WLEDMM TroyHacks support - SLOWPATH has priority over TWOPATH
+#ifdef WLEDMM_SLOWPATH
+#undef WLEDMM_TWOPATH
+#endif
+
+// WLEDMM repeat definition of USER_PRINT
+bool canUseSerial(void); // WLEDMM (wled_serial.cpp) returns true if Serial can be used for debug output (i.e. not configured for other purpose)
+#if defined(WLED_DEBUG_HOST)
+ #include "net_debug.h"
+ extern bool netDebugEnabled;
+ #define USER_PRINT(x) (netDebugEnabled || !canUseSerial())?NetDebug.print(x):Serial.print(x)
+ #define USER_PRINTLN(x) (netDebugEnabled || !canUseSerial())?NetDebug.println(x):Serial.println(x)
+ #define USER_PRINTF(x...) (netDebugEnabled || !canUseSerial())?NetDebug.printf(x):Serial.printf(x)
+ #define USER_FLUSH() (netDebugEnabled || !canUseSerial())?NetDebug.flush():Serial.flush()
+#else
+ #define USER_PRINT(x) {if (canUseSerial()) Serial.print(x);}
+ #define USER_PRINTLN(x) {if (canUseSerial()) Serial.println(x);}
+ #define USER_PRINTF(x...) {if (canUseSerial()) Serial.printf(x);}
+ #define USER_FLUSH() {if (canUseSerial()) Serial.flush();}
+#endif
+// WLEDMM end
+
//Hardware SPI Pins
#define P_8266_HS_MOSI 13
#define P_8266_HS_CLK 14
@@ -390,10 +412,14 @@ class PolyBus {
};
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
- #if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds
+ #if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds.
// NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation
// since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
+ #if defined(WLEDMM_TWOPATH)
+ if (channel > 1) channel--; // accommodate I2S1 which is used as 2nd bus on classic ESP32
+ #else
if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
+ #endif
#endif
#endif
void* busPtr = nullptr;
@@ -430,20 +456,20 @@ class PolyBus {
case I_8266_BB_UCS_4: busPtr = new B_8266_BB_UCS_4(len, pins[0]); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
- case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
+ case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); USER_PRINTF("(RMT #%u) ", channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
+ case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); USER_PRINT("(I2S #0) "); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
+ case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); USER_PRINT("(I2S #1) "); break;
#endif
// case I_32_BB_NEO_3: busPtr = new B_32_BB_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
- case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
+ case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); USER_PRINTF("(RGBW RMT #%u) ", channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
+ case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); USER_PRINT("(RGBW I2S #0) "); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
+ case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); USER_PRINT("(RGBW I2S #1) "); break;
#endif
// case I_32_BB_NEO_4: busPtr = new B_32_BB_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break;
@@ -1199,8 +1225,11 @@ class PolyBus {
if (num > 7) return I_NONE;
#else
if (num > 8) return I_NONE;
- //if (num == 1) offset = 2; // use I2S#1 as 2nd bus - seems to be a good compromise for performance, and reduces flickering for some users
- if (num == 0) offset = 2; // un-comment to use I2S#1 as 1st bus - sometimes helps, if you experience flickering during Wifi or filesystem activity.
+ #if defined(WLEDMM_TWOPATH)
+ if (num == 1) offset = 2; // use I2S#1 as 2nd bus - seems to be a good compromise for performance, and reduces flickering for some users
+ #else
+ if (num == 0) offset = 2; // use I2S#1 as 1st bus - sometimes helps, if you experience flickering during Wifi or filesystem activity.
+ #endif
#endif
#endif
#endif
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 54c32f9c..24e923c8 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -795,7 +795,7 @@ void serializeConfig() {
matrix["psl"] = strip.panelO.serpentine;
JsonArray panels = matrix.createNestedArray(F("panels"));
- for (uint8_t i=0; i 40 && (t) < 46)
#define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only
#define IS_2PIN(t) ((t) > 47)
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 4744fb38..1fcbfcaf 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -1951,6 +1951,18 @@ function readState(s,command=false)
if (s.error && s.error != 0) {
var errstr = "";
switch (s.error) {
+ case 1:
+ errstr = "Denied!";
+ break;
+ case 3:
+ errstr = "Buffer locked!";
+ break;
+ case 8:
+ errstr = "Effect RAM depleted!";
+ break;
+ case 9:
+ errstr = "JSON parsing error!";
+ break;
case 10:
errstr = "Could not mount filesystem!";
break;
@@ -1963,6 +1975,9 @@ function readState(s,command=false)
case 13:
errstr = "Missing ir.json.";
break;
+ case 14:
+ errstr = "Missing remote.json.";
+ break;
case 19:
errstr = "A filesystem error has occured.";
break;
diff --git a/wled00/improv.cpp b/wled00/improv.cpp
index 646201d3..eef8ad82 100644
--- a/wled00/improv.cpp
+++ b/wled00/improv.cpp
@@ -50,6 +50,7 @@ void handleImprovPacket() {
uint8_t rpcCommandType = 0;
char rpcData[128];
rpcData[0] = 0;
+ if (!Serial) return; // WLEDMM avoid reading from unconnected USB-CDC
while (!timeout) {
if (Serial.available() < 1) {
diff --git a/wled00/json.cpp b/wled00/json.cpp
index c03a61f0..5456b455 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -516,7 +516,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
//bool didSet = false;
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
Segment &sg = strip.getSegment(s);
- if (sg.isSelected()) {
+ if (sg.isActive() && sg.isSelected()) {
inDeepCall = true; // WLEDMM remember that we are going into recursion
deserializeSegment(segVar, s, presetId);
if (iAmGroot) inDeepCall = false; // WLEDMM toplevel -> reset recursion flag
@@ -731,11 +731,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
nl["dur"] = nightlightDelayMins;
nl["mode"] = nightlightMode;
nl[F("tbri")] = nightlightTargetBri;
- if (nightlightActive) {
- nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
- } else {
- nl[F("rem")] = -1;
- }
+ nl[F("rem")] = nightlightActive ? (int)(nightlightDelayMs - (millis() - nightlightStartTime)) / 1000 : -1; // seconds remaining
JsonObject udpn = root.createNestedObject("udpn");
udpn["send"] = notifyDirect;
@@ -901,9 +897,10 @@ String restartCode2Info(esp_reset_reason_t reason) {
void serializeInfo(JsonObject root)
{
root[F("ver")] = versionString;
- root[F("rel")] = releaseString; //WLEDMM to add bin name
root[F("vid")] = VERSION;
- //root[F("cn")] = WLED_CODENAME;
+ //root[F("cn")] = F(WLED_CODENAME); //WLEDMM removed
+ root[F("release")] = FPSTR(releaseString);
+ root[F("rel")] = FPSTR(releaseString); //WLEDMM to add bin name
JsonObject leds = root.createNestedObject("leds");
leds[F("count")] = strip.getLengthTotal();
@@ -1022,12 +1019,15 @@ void serializeInfo(JsonObject root)
wifi_info[F("txPower")] = (int) WiFi.getTxPower();
wifi_info[F("sleep")] = (bool) WiFi.getSleep();
#endif
- #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+ //#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+ #if CONFIG_IDF_TARGET_ESP32
root[F("arch")] = "esp32";
#else
root[F("arch")] = ESP.getChipModel();
#endif
root[F("core")] = ESP.getSdkVersion();
+ root[F("clock")] = ESP.getCpuFreqMHz();
+ root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024;
//root[F("maxalloc")] = ESP.getMaxAllocHeap();
#ifdef WLED_DEBUG
root[F("resetReason0")] = (int)rtc_get_reset_reason(0);
@@ -1047,6 +1047,8 @@ void serializeInfo(JsonObject root)
#else
root[F("arch")] = "esp8266";
root[F("core")] = ESP.getCoreVersion();
+ root[F("clock")] = ESP.getCpuFreqMHz();
+ root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024;
//root[F("maxalloc")] = ESP.getMaxFreeBlockSize();
#ifdef WLED_DEBUG
root[F("resetReason")] = (int)ESP.getResetInfoPtr()->reason;
diff --git a/wled00/wled.cpp b/wled00/wled.cpp
index 11ebcf72..71050d99 100644
--- a/wled00/wled.cpp
+++ b/wled00/wled.cpp
@@ -901,7 +901,7 @@ void WLED::initAP(bool resetAP)
USER_PRINT(F("Opening access point ")); // WLEDMM
USER_PRINTLN(apSSID); // WLEDMM
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0));
- WiFi.softAP(apSSID, apPass, apChannel, apHide);
+ WiFi.softAP(apSSID, apPass, apChannel, apHide, 8); // WLED-MM allow up to 8 clients for ad-hoc "in the field" syncing.
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif
diff --git a/wled00/wled.h b/wled00/wled.h
index a0e4283e..96530ac2 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
-#define VERSION 2405040
+#define VERSION 2405241
// 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_
@@ -271,16 +271,17 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument;
// int arr[]{0,1,2} becomes WLED_GLOBAL int arr[] _INIT_N(({0,1,2}));
#ifndef WLED_DEFINE_GLOBAL_VARS
-# define WLED_GLOBAL extern
-# define _INIT(x)
-# define _INIT_N(x)
+ #define WLED_GLOBAL extern
+ #define _INIT(x)
+ #define _INIT_N(x)
+ #define _INIT_PROGMEM(x)
#else
-# define WLED_GLOBAL
-# define _INIT(x) = x
-
-//needed to ignore commas in array definitions
-#define UNPACK( ... ) __VA_ARGS__
-# define _INIT_N(x) UNPACK x
+ #define WLED_GLOBAL
+ #define _INIT(x) = x
+ //needed to ignore commas in array definitions
+ #define UNPACK( ... ) __VA_ARGS__
+ #define _INIT_N(x) UNPACK x
+ #define _INIT_PROGMEM(x) PROGMEM = x
#endif
#define STRINGIFY(X) #X
@@ -290,9 +291,13 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument;
#define WLED_VERSION "dev"
#endif
+#ifndef WLED_RELEASE_NAME
+ #define WLED_RELEASE_NAME mdev_release
+#endif
+
// Global Variable definitions
WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION));
-WLED_GLOBAL char releaseString[] _INIT(TOSTRING(WLED_RELEASE_NAME)); //WLEDMM: to show on update page
+WLED_GLOBAL char releaseString[] _INIT_PROGMEM(TOSTRING(WLED_RELEASE_NAME)); //WLEDMM: to show on update page // somehow this will not work if using "const char releaseString[]
#define WLED_CODENAME "Hoshi"
// AP and OTA default passwords (for maximum security change them!)
diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp
index 221f21ad..9361891b 100644
--- a/wled00/wled_serial.cpp
+++ b/wled00/wled_serial.cpp
@@ -42,6 +42,7 @@ void updateBaudRate(uint32_t rate){
// RGB LED data return as JSON array. Slow, but easy to use on the other end.
void sendJSON(){
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) {
+ if (!Serial) return; // WLEDMM avoid writing to unconnected USB-CDC
uint16_t used = strip.getLengthTotal();
Serial.write('[');
for (uint16_t i=0; i("));
#if defined(CONFIG_IDF_TARGET_ESP32C3)
oappend(SET_F("ESP32-C3"));