Merge branch 'MoonModules:mdev' into Strip_Level_Color_Adjust
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#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 <arduinoFFT.h>
|
||||
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
static ArduinoFFT<float> FFT = ArduinoFFT<float>( 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<float> FFT = ArduinoFFT<float>( 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<float> FFT = ArduinoFFT<float>( 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;
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <Wire.h>
|
||||
#include "wled.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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#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)) {
|
||||
|
||||
318
wled00/FX.cpp
318
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<Spark*>(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<numDrops;j++) {
|
||||
if (drops[j].colIndex == 0) { //init
|
||||
@@ -3724,24 +3725,26 @@ uint16_t mode_drip(void)
|
||||
|
||||
drops[j].col += map(SEGMENT.custom1, 0, 255, 1, 6); // swelling
|
||||
|
||||
if (random16() <= drops[j].col * SEGMENT.custom1 * SEGMENT.custom1 / 10 / 128) { // random drop
|
||||
uint32_t fallrate = (drops[j].col * (1 + SEGMENT.custom1 * SEGMENT.custom1)) / 192; // WLEDMM specific
|
||||
if (random16() <= (fallrate / 10)) { // random drop => 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<CRGB*>(SEGENV.data);
|
||||
uint16_t *crcBuffer = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize);
|
||||
if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed
|
||||
gameOfLife* gol = reinterpret_cast<gameOfLife*>(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<crcBufferLen && !repetition; i++) repetition = (crc == crcBuffer[i]); // (Ewowi)
|
||||
// same CRC would mean image did not change or was repeating itself
|
||||
// -> 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 {
|
||||
|
||||
12
wled00/FX.h
12
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<CRGBPalette16> 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)
|
||||
|
||||
@@ -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<noOfBlends; i++, _t->_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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -795,7 +795,7 @@ void serializeConfig() {
|
||||
matrix["psl"] = strip.panelO.serpentine;
|
||||
|
||||
JsonArray panels = matrix.createNestedArray(F("panels"));
|
||||
for (uint8_t i=0; i<strip.panel.size(); i++) {
|
||||
for (size_t i = 0; i < strip.panel.size(); i++) {
|
||||
JsonObject pnl = panels.createNestedObject();
|
||||
pnl["b"] = strip.panel[i].bottomStart;
|
||||
pnl["r"] = strip.panel[i].rightStart;
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused)
|
||||
#define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus)
|
||||
|
||||
#define IS_DIGITAL(t) ((t) & 0x10) //digital are 16-31 and 48-63
|
||||
#define IS_DIGITAL(t) (((t) & 0x10) || ((t)==TYPE_HUB75MATRIX)) //digital are 16-31 and 48-63 // WLEDMM added HUB75
|
||||
#define IS_PWM(t) ((t) > 40 && (t) < 46)
|
||||
#define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only
|
||||
#define IS_2PIN(t) ((t) > 47)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<PSRAM_Allocator>;
|
||||
// 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<PSRAM_Allocator>;
|
||||
#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!)
|
||||
|
||||
@@ -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<used; i++) {
|
||||
@@ -55,6 +56,7 @@ void sendJSON(){
|
||||
// RGB LED data returned as bytes in TPM2 format. Faster, and slightly less easy to use on the other end.
|
||||
void sendBytes(){
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) {
|
||||
if (!Serial) return; // WLEDMM avoid writing to unconnected USB-CDC
|
||||
Serial.write(0xC9); Serial.write(0xDA);
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t len = used*3;
|
||||
|
||||
@@ -186,6 +186,7 @@ void sendDataWs(AsyncWebSocketClient * client)
|
||||
// WLEDMM function to recover full-bright pixel (based on code from upstream alt-buffer, which is based on code from NeoPixelBrightnessBus)
|
||||
static uint32_t restoreColorLossy(uint32_t c, uint_fast8_t _restaurationBri) {
|
||||
if (_restaurationBri == 255) return c;
|
||||
if (_restaurationBri == 0) return 0;
|
||||
uint8_t* chan = (uint8_t*) &c;
|
||||
for (uint_fast8_t i=0; i<4; i++) {
|
||||
uint_fast16_t val = chan[i];
|
||||
|
||||
@@ -196,7 +196,11 @@ void appendGPIOinfo() {
|
||||
size_t roLen = strlen(ro_gpio);
|
||||
char pinString[10];
|
||||
for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3
|
||||
if(!pinManager.isPinOk(pinNr, false)) {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(BOARD_HAS_PSRAM)
|
||||
if ((!pinManager.isPinOk(pinNr, false)) || (pinManager.getPinOwner(pinNr) == PinOwner::SPI_RAM)) { // WLEDMM add SPIRAM pins as "reserved" (pico boards)
|
||||
#else
|
||||
if (!pinManager.isPinOk(pinNr, false)) {
|
||||
#endif
|
||||
sprintf(pinString, "%s%d", strlen(rsvd)==rsLen?"":",", pinNr);
|
||||
strcat(rsvd, pinString);
|
||||
}
|
||||
@@ -265,9 +269,9 @@ void appendGPIOinfo() {
|
||||
|
||||
// add info about max. # of pins
|
||||
oappend(SET_F("d.max_gpio="));
|
||||
#if defined(ESP32)
|
||||
#if defined(ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
oappendi(NUM_DIGITAL_PINS - 1);
|
||||
#else //8266
|
||||
#else //8266 (max=17), or esp32-S3 (max=48)
|
||||
oappendi(NUM_DIGITAL_PINS); //WLEDMM include pin 17 for Analog
|
||||
#endif
|
||||
oappend(SET_F(";"));
|
||||
@@ -842,7 +846,7 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W
|
||||
olen -= 2; //delete ";
|
||||
oappend(versionString);
|
||||
oappend(SET_F(" "));
|
||||
oappend(releaseString);
|
||||
oappend((char*)FPSTR(releaseString));
|
||||
oappend(SET_F(".bin<br>("));
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
oappend(SET_F("ESP32-C3"));
|
||||
|
||||
Reference in New Issue
Block a user