Merge pull request #3280 from Aircoookie/alt-buffer

Bus-level global buffering
This commit is contained in:
Christian Schwinne 2023-07-25 17:01:49 +02:00 committed by GitHub
commit 93853613bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 785 additions and 679 deletions

View File

@ -11,7 +11,7 @@
# CI binaries # CI binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi
# Release binaries # Release binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
@ -175,7 +175,7 @@ upload_speed = 115200
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
lib_compat_mode = strict lib_compat_mode = strict
lib_deps = lib_deps =
fastled/FastLED @ 3.5.0 fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2 IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5 makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
@ -201,7 +201,7 @@ build_flags =
-DESP8266 -DESP8266
-DFP_IN_IROM -DFP_IN_IROM
;-Wno-deprecated-declarations ;-Wno-deprecated-declarations
-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C ;-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous ;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous
-Wno-misleading-indentation -Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e) ; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
@ -249,9 +249,8 @@ lib_deps =
;; ;;
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. ;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio. ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g build_flags = -g
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
-DARDUINO_ARCH_ESP32 -DESP32 -DARDUINO_ARCH_ESP32 -DESP32
@ -265,9 +264,8 @@ lib_deps =
[esp32s2] [esp32s2]
;; generic definitions for all ESP32-S2 boards ;; generic definitions for all ESP32-S2 boards
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2 -DARDUINO_ARCH_ESP32S2
@ -285,9 +283,8 @@ lib_deps =
[esp32c3] [esp32c3]
;; generic definitions for all ESP32-C3 boards ;; generic definitions for all ESP32-C3 boards
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3 -DARDUINO_ARCH_ESP32C3
@ -304,9 +301,8 @@ lib_deps =
[esp32s3] [esp32s3]
;; generic definitions for all ESP32-S3 boards ;; generic definitions for all ESP32-S3 boards
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g build_flags = -g
-DESP32 -DESP32
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
@ -450,6 +446,7 @@ board_build.flash_mode = qio
upload_speed = 460800 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps} lib_deps = ${esp32s2.lib_deps}
@ -462,6 +459,7 @@ board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip ;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
upload_speed = 460800 upload_speed = 460800
@ -478,7 +476,7 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
;-D WLED_DEBUG ;-D WLED_DEBUG
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv board_build.partitions = tools/WLED_ESP32_8MB.csv
@ -487,19 +485,18 @@ board_build.flash_mode = qio
; board_build.flash_mode = dio ;; try this if you have problems at startup ; board_build.flash_mode = dio ;; try this if you have problems at startup
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_PSRAM] [env:esp32s3dev_8MB_PSRAM_opi]
;; ESP32-TinyS3 development board, with 8MB FLASH and 8MB PSRAM (memory_type: qio_opi, qio_qspi, or opi_opi) ;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
;board = esp32s3box ; -> error: 'esp32_adc2gpio' was not declared in this scope board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM
platform = ${esp32s3.platform} platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages} platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 upload_speed = 921600
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM ; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
@ -508,6 +505,13 @@ board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_PSRAM_qspi]
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
extends = env:esp32s3dev_8MB_PSRAM_opi
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
[env:esp8285_4CH_MagicHome] [env:esp8285_4CH_MagicHome]
board = esp8285 board = esp8285
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}

View File

@ -0,0 +1,8 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
app1, app, ota_1, 0x310000,0x300000,
spiffs, data, spiffs, 0x610000,0x9E0000,
coredump, data, coredump,,64K
# to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x5000,
3 otadata, data, ota, 0xe000, 0x2000,
4 app0, app, ota_0, 0x10000, 0x300000,
5 app1, app, ota_1, 0x310000,0x300000,
6 spiffs, data, spiffs, 0x610000,0x9E0000,
7 coredump, data, coredump,,64K
8 # to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage

View File

@ -3,4 +3,5 @@ nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000, otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x200000, app0, app, ota_0, 0x10000, 0x200000,
app1, app, ota_1, 0x210000,0x200000, app1, app, ota_1, 0x210000,0x200000,
spiffs, data, spiffs, 0x410000,0x3F0000, spiffs, data, spiffs, 0x410000,0x3E0000,
coredump, data, coredump,,64K

1 # Name # Name, Type, SubType, Offset, Size, Flags Type SubType Offset Size Flags
3 otadata otadata, data, ota, 0xe000, 0x2000, data ota 0xe000 0x2000
4 app0 app0, app, ota_0, 0x10000, 0x200000, app ota_0 0x10000 0x200000
5 app1 app1, app, ota_1, 0x210000,0x200000, app ota_1 0x210000 0x200000
6 spiffs spiffs, data, spiffs, 0x410000,0x3E0000, data spiffs 0x410000 0x3F0000
7 coredump, data, coredump,,64K

View File

@ -289,7 +289,6 @@ uint16_t mode_dynamic(void) {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
if(SEGENV.call == 0) { if(SEGENV.call == 0) {
//SEGMENT.setUpLeds(); //lossless getPixelColor()
//SEGMENT.fill(BLACK); //SEGMENT.fill(BLACK);
for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8();
} }
@ -607,7 +606,6 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
uint16_t dissolve(uint32_t color) { uint16_t dissolve(uint32_t color) {
//bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness //bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(SEGCOLOR(1)); SEGMENT.fill(SEGCOLOR(1));
} }
@ -1206,7 +1204,6 @@ uint16_t mode_fireworks() {
const uint16_t height = SEGMENT.virtualHeight(); const uint16_t height = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(SEGCOLOR(1)); SEGMENT.fill(SEGCOLOR(1));
SEGENV.aux0 = UINT16_MAX; SEGENV.aux0 = UINT16_MAX;
SEGENV.aux1 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX;
@ -1215,19 +1212,21 @@ uint16_t mode_fireworks() {
bool valid1 = (SEGENV.aux0 < width*height); bool valid1 = (SEGENV.aux0 < width*height);
bool valid2 = (SEGENV.aux1 < width*height); bool valid2 = (SEGENV.aux1 < width*height);
uint8_t x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte
uint32_t sv1 = 0, sv2 = 0; uint32_t sv1 = 0, sv2 = 0;
if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux1);
if (!SEGENV.step) SEGMENT.blur(16); if (!SEGENV.step) SEGMENT.blur(16);
if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
for (int i=0; i<MAX(1, width/20); i++) { for (int i=0; i<MAX(1, width/20); i++) {
if (random8(129 - (SEGMENT.intensity >> 1)) == 0) { if (random8(129 - (SEGMENT.intensity >> 1)) == 0) {
uint16_t index = random16(width*height); uint16_t index = random16(width*height);
uint16_t j = index % width, k = index / width; x = index % width;
y = index / width;
uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0);
if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(j, k, col); if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, col);
else SEGMENT.setPixelColor(index, col); else SEGMENT.setPixelColor(index, col);
SEGENV.aux1 = SEGENV.aux0; // old spark SEGENV.aux1 = SEGENV.aux0; // old spark
SEGENV.aux0 = index; // remember where spark occured SEGENV.aux0 = index; // remember where spark occured
@ -1905,7 +1904,6 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
uint16_t mode_juggle(void) { uint16_t mode_juggle(void) {
if (SEGLEN == 1) return mode_static(); if (SEGLEN == 1) return mode_static();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4586,7 +4584,6 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
// initialize on first call // initialize on first call
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4596,22 +4593,22 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
for (size_t i = 0; i < 8; i++) { for (size_t i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i); x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i); y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i);
SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
} }
// inner stars // inner stars
for (size_t i = 0; i < 4; i++) { for (size_t i = 0; i < 4; i++) {
x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i); x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i); y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i);
SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
} }
// central white dot // central white dot
SEGMENT.setPixelColorXY(cols/2, rows/2, CHSV(0, 0, 255)); SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE);
// blur everything a bit // blur everything a bit
SEGMENT.blur(16); SEGMENT.blur(16);
return FRAMETIME; return FRAMETIME;
} // mode_2DBlackHole() } // mode_2DBlackHole()
static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2"; static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.,Solid;!;!;2;pal=11";
//////////////////////////// ////////////////////////////
@ -4624,7 +4621,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
SEGENV.aux0 = 0; // start with red hue SEGENV.aux0 = 0; // start with red hue
} }
@ -4678,7 +4674,6 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4705,7 +4700,6 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4751,7 +4745,6 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4783,7 +4776,6 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4818,7 +4810,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -4857,8 +4848,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
CRGB backgroundColor = SEGCOLOR(1); CRGB backgroundColor = SEGCOLOR(1);
if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) {
SEGENV.step = strip.now; SEGENV.step = strip.now;
SEGENV.aux0 = 0; SEGENV.aux0 = 0;
@ -4915,7 +4904,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
} // i,j } // i,j
// Rules of Life // Rules of Life
uint32_t col = prevLeds[XY(x,y)]; uint32_t col = uint32_t(prevLeds[XY(x,y)]) & 0x00FFFFFF; // uint32_t operator returns RGBA, we want RGBW -> cut off "alpha" byte
uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0); uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0);
if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness 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)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
@ -5124,7 +5113,6 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5269,7 +5257,6 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5317,7 +5304,6 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000};
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
SEGENV.step = 0; SEGENV.step = 0;
} }
@ -5367,7 +5353,6 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5395,7 +5380,6 @@ uint16_t mode_2DSindots(void) { // By: ldirko http
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5427,7 +5411,6 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5470,7 +5453,6 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi
byte *bump = reinterpret_cast<byte*>(SEGENV.data); byte *bump = reinterpret_cast<byte*>(SEGENV.data);
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5518,7 +5500,6 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5558,7 +5539,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -5627,7 +5607,6 @@ uint16_t mode_2Dcrazybees(void) {
bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data); bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data);
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
bee[i].posX = random8(0, cols); bee[i].posX = random8(0, cols);
@ -5697,7 +5676,6 @@ uint16_t mode_2Dghostrider(void) {
const size_t maxLighters = min(cols + rows, LIGHTERS_AM); const size_t maxLighters = min(cols + rows, LIGHTERS_AM);
if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; SEGENV.aux0 = cols;
SEGENV.aux1 = rows; SEGENV.aux1 = rows;
@ -5782,7 +5760,6 @@ uint16_t mode_2Dfloatingblobs(void) {
if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed
blob_t *blob = reinterpret_cast<blob_t*>(SEGENV.data); blob_t *blob = reinterpret_cast<blob_t*>(SEGENV.data);
if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; // re-initialise if virtual size changes SEGENV.aux0 = cols; // re-initialise if virtual size changes
SEGENV.aux1 = rows; SEGENV.aux1 = rows;
@ -5948,7 +5925,6 @@ uint16_t mode_2Ddriftrose(void) {
const float L = min(cols, rows) / 2.f; const float L = min(cols, rows) / 2.f;
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6103,7 +6079,6 @@ uint16_t mode_2DSwirl(void) {
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6150,7 +6125,6 @@ uint16_t mode_2DWaverly(void) {
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6379,7 +6353,6 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6507,7 +6480,6 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6733,7 +6705,6 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil
uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6802,7 +6773,6 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -6904,7 +6874,6 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -7089,7 +7058,6 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
SEGENV.aux0 = 255; SEGENV.aux0 = 255;
SEGMENT.custom1 = *binNum; SEGMENT.custom1 = *binNum;
@ -7206,7 +7174,6 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
@ -7419,7 +7386,6 @@ uint16_t mode_2Dsoap() {
// init // init
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
*noise32_x = random16(); *noise32_x = random16();
*noise32_y = random16(); *noise32_y = random16();
*noise32_z = random16(); *noise32_z = random16();
@ -7537,12 +7503,12 @@ uint16_t mode_2Doctopus() {
SEGENV.aux1 = rows; SEGENV.aux1 = rows;
*offsX = SEGMENT.custom1; *offsX = SEGMENT.custom1;
*offsY = SEGMENT.custom2; *offsY = SEGMENT.custom2;
const uint8_t C_X = cols / 2 + (SEGMENT.custom1 - 128)*cols/255; const int C_X = (cols / 2) + ((SEGMENT.custom1 - 128)*cols)/255;
const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255; const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255;
for (int x = 0; x < cols; x++) { for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) { for (int y = 0; y < rows; y++) {
rMap[XY(x, y)].angle = 40.7436f * atan2f(y - C_Y, x - C_X); // avoid 128*atan2()/PI rMap[XY(x, y)].angle = 40.7436f * atan2f((y - C_Y), (x - C_X)); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu
} }
} }
} }

View File

@ -329,7 +329,7 @@ typedef enum mapping1D2D {
M12_pCorner = 3 M12_pCorner = 3
} mapping1D2D_t; } mapping1D2D_t;
// segment, 72 bytes // segment, 80 bytes
typedef struct Segment { typedef struct Segment {
public: public:
uint16_t start; // start index / start X coordinate 2D (left) uint16_t start; // start index / start X coordinate 2D (left)
@ -370,7 +370,7 @@ typedef struct Segment {
}; };
uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows
uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows
char *name; char *name;
// runtime data // runtime data
unsigned long next_time; // millis() of next update unsigned long next_time; // millis() of next update
@ -378,9 +378,7 @@ typedef struct Segment {
uint32_t call; // call counter uint32_t call; // call counter
uint16_t aux0; // custom var uint16_t aux0; // custom var
uint16_t aux1; // custom var uint16_t aux1; // custom var
byte* data; // effect data pointer byte *data; // effect data pointer
CRGB* leds; // local leds[] array (may be a pointer to global)
static CRGB *_globalLeds; // global leds[] array
static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions)
private: private:
@ -394,10 +392,10 @@ typedef struct Segment {
uint8_t _reserved : 4; uint8_t _reserved : 4;
}; };
}; };
uint16_t _dataLen; uint16_t _dataLen;
static uint16_t _usedSegmentData; static uint16_t _usedSegmentData;
// transition data, valid only if transitional==true, holds values during transition // transition data, valid only if transitional==true, holds values during transition (72 bytes)
struct Transition { struct Transition {
uint32_t _colorT[NUM_COLORS]; uint32_t _colorT[NUM_COLORS];
uint8_t _briT; // temporary brightness uint8_t _briT; // temporary brightness
@ -408,7 +406,7 @@ typedef struct Segment {
//uint16_t _aux0, _aux1; // previous mode/effect runtime data //uint16_t _aux0, _aux1; // previous mode/effect runtime data
//uint32_t _step, _call; // previous mode/effect runtime data //uint32_t _step, _call; // previous mode/effect runtime data
//byte *_data; // previous mode/effect runtime data //byte *_data; // previous mode/effect runtime data
uint32_t _start; unsigned long _start; // must accommodate millis()
uint16_t _dur; uint16_t _dur;
Transition(uint16_t dur=750) Transition(uint16_t dur=750)
: _briT(255) : _briT(255)
@ -463,7 +461,6 @@ typedef struct Segment {
aux0(0), aux0(0),
aux1(0), aux1(0),
data(nullptr), data(nullptr),
leds(nullptr),
_capabilities(0), _capabilities(0),
_dataLen(0), _dataLen(0),
_t(nullptr) _t(nullptr)
@ -484,12 +481,10 @@ typedef struct Segment {
//Serial.print(F("Destroying segment:")); //Serial.print(F("Destroying segment:"));
//if (name) Serial.printf(" %s (%p)", name, name); //if (name) Serial.printf(" %s (%p)", name, name);
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //if (data) Serial.printf(" %d (%p)", (int)_dataLen, data);
//if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB));
//Serial.println(); //Serial.println();
//#endif //#endif
if (!Segment::_globalLeds && leds) { free(leds); leds = nullptr;} // reset to nullptr, to avoid race conditions if (name) { delete[] name; name = nullptr; }
if (name) delete[] name; if (_t) { transitional = false; delete _t; _t = nullptr; }
if (_t) delete _t;
deallocateData(); deallocateData();
} }
@ -497,7 +492,7 @@ typedef struct Segment {
Segment& operator= (Segment &&orig) noexcept; // move assignment Segment& operator= (Segment &&orig) noexcept; // move assignment
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0) + (!Segment::_globalLeds && leds?sizeof(CRGB)*length():0); } size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0); }
#endif #endif
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
@ -507,16 +502,16 @@ typedef struct Segment {
inline bool hasRGB(void) const { return _isRGB; } inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; } inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; } inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D) inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0 inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1)
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength(void) const { return grouping + spacing; } inline uint16_t groupLength(void) const { return grouping + spacing; }
inline uint8_t getLightCapabilities(void) const { return _capabilities; } inline uint8_t getLightCapabilities(void) const { return _capabilities; }
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; } static void addUsedSegmentData(int len) { _usedSegmentData += len; }
void 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); 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, uint8_t segId = 255);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed bool setColor(uint8_t slot, uint32_t c); //returns true if changed
void setCCT(uint16_t k); void setCCT(uint16_t k);
void setOpacity(uint8_t o); void setOpacity(uint8_t o);
@ -538,7 +533,6 @@ typedef struct Segment {
* Safe to call from interrupts and network requests. * Safe to call from interrupts and network requests.
*/ */
inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true) inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true)
void setUpLeds(void); // set up leds[] array for loseless getPixelColor()
// transition functions // transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change void startTransition(uint16_t dur); // transition has to start before actual segment values change
@ -579,7 +573,7 @@ typedef struct Segment {
uint16_t virtualHeight(void) const; uint16_t virtualHeight(void) const;
uint16_t nrOfVStrips(void) const; uint16_t nrOfVStrips(void) const;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment (for leds[]) uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
@ -692,7 +686,15 @@ class WS2812FX { // 96 bytes
customMappingSize(0), customMappingSize(0),
_lastShow(0), _lastShow(0),
_segment_index(0), _segment_index(0),
_mainSegment(0) _mainSegment(0),
_queuedChangesSegId(255),
_qStart(0),
_qStop(0),
_qStartY(0),
_qStopY(0),
_qGrouping(0),
_qSpacing(0),
_qOffset(0)
{ {
WS2812FX::instance = this; WS2812FX::instance = this;
_mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size())
@ -710,7 +712,6 @@ class WS2812FX { // 96 bytes
panel.clear(); panel.clear();
#endif #endif
customPalettes.clear(); customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) {free(Segment::_globalLeds); Segment::_globalLeds = nullptr;} // reset to nullptr, to avoid race conditions
} }
static WS2812FX* getInstance(void) { return instance; } static WS2812FX* getInstance(void) { return instance; }
@ -749,7 +750,7 @@ class WS2812FX { // 96 bytes
inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments.
inline void setShowCallback(show_callback cb) { _callback = cb; } inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; } inline void setTransition(uint16_t t) { _transitionDur = t; }
inline void appendSegment(const Segment &seg = Segment()) { _segments.push_back(seg); } inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); }
bool bool
checkSegmentAlignment(void), checkSegmentAlignment(void),
@ -757,8 +758,7 @@ class WS2812FX { // 96 bytes
hasCCTBus(void), hasCCTBus(void),
// return true if the strip is being sent pixel updates // return true if the strip is being sent pixel updates
isUpdating(void), isUpdating(void),
deserializeMap(uint8_t n=0), deserializeMap(uint8_t n=0);
useLedsArray = false;
inline bool isServicing(void) { return _isServicing; } inline bool isServicing(void) { return _isServicing; }
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
@ -900,13 +900,20 @@ class WS2812FX { // 96 bytes
uint16_t* customMappingTable; uint16_t* customMappingTable;
uint16_t customMappingSize; uint16_t customMappingSize;
uint32_t _lastShow; unsigned long _lastShow;
uint8_t _segment_index; uint8_t _segment_index;
uint8_t _mainSegment; uint8_t _mainSegment;
uint8_t _queuedChangesSegId;
uint16_t _qStart, _qStop, _qStartY, _qStopY;
uint8_t _qGrouping, _qSpacing;
uint16_t _qOffset;
uint8_t
estimateCurrentAndLimitBri(void);
void void
estimateCurrentAndLimitBri(void); setUpSegmentFromQueuedChanges(void);
}; };
extern const char JSON_mode_names[]; extern const char JSON_mode_names[];

View File

@ -189,20 +189,16 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
uint16_t height = virtualHeight(); // segment height in logical pixels uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
if (width == 0) return 0; // softhack007 avoid div/0 return isActive() ? (x%width) + (y%height) * width : 0;
if (height == 0) return (x%width); // softhack007 avoid div/0
return (x%width) + (y%height) * width;
} }
void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
@ -245,7 +241,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
// anti-aliased version of setPixelColorXY() // anti-aliased version of setPixelColorXY()
void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up if (!isActive()) return; // not active
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
@ -288,8 +284,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel // returns RGBW values of pixel
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
int i = XY(x,y); if (!isActive()) return 0; // not active
if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0); if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1; if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1; if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
@ -306,6 +302,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y); uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col); uint8_t r = R(col);
@ -325,12 +322,14 @@ void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
} }
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColorXY(x, y, pix); setPixelColorXY(x, y, pix);
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) { void Segment::blurRow(uint16_t row, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@ -358,6 +357,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix // blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) { void Segment::blurCol(uint16_t col, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@ -385,6 +385,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols; const uint16_t dim1 = vertical ? rows : cols;
@ -437,6 +438,7 @@ void Segment::blur1d(fract8 blur_amount) {
} }
void Segment::moveX(int8_t delta, bool wrap) { void Segment::moveX(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= cols) return; if (!delta || abs(delta) >= cols) return;
@ -454,6 +456,7 @@ void Segment::moveX(int8_t delta, bool wrap) {
} }
void Segment::moveY(int8_t delta, bool wrap) { void Segment::moveY(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= rows) return; if (!delta || abs(delta) >= rows) return;
@ -489,6 +492,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
} }
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
// Bresenhams Algorithm // Bresenhams Algorithm
int d = 3 - (2*radius); int d = 3 - (2*radius);
int y = radius, x = 0; int y = radius, x = 0;
@ -513,6 +517,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) { for (int16_t y = -radius; y <= radius; y++) {
@ -526,6 +531,7 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
} }
void Segment::nscale8(uint8_t scale) { void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
@ -535,6 +541,7 @@ void Segment::nscale8(uint8_t scale) {
//line function //line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
@ -559,6 +566,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas // draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM // only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) { void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) {
if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries chr -= 32; // align with font table entries
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
@ -594,6 +602,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) #define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
if (!isActive()) return; // not active
// extract the fractional parts and derive their inverses // extract the fractional parts and derive their inverses
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel // calculate the intensities for each affected pixel

View File

@ -74,7 +74,6 @@
// Segment class implementation // Segment class implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[] uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[]
CRGB *Segment::_globalLeds = nullptr;
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
uint16_t Segment::maxHeight = 1; uint16_t Segment::maxHeight = 1;
@ -82,22 +81,21 @@ uint16_t Segment::maxHeight = 1;
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --")); //DEBUG_PRINTLN(F("-- Copy segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
_t = nullptr; _t = nullptr;
if (leds && !Segment::_globalLeds) leds = nullptr;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --")); //DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.leds = nullptr; orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@ -109,23 +107,22 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copying segment --")); //DEBUG_PRINTLN(F("-- Copying segment --"));
if (this != &orig) { if (this != &orig) {
// clean destination // clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name; if (name) delete[] name;
if (_t) delete _t; if (_t) delete _t;
if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;}
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
_t = nullptr; _t = nullptr;
if (!Segment::_globalLeds) leds = nullptr;
// copy source data // copy source data
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
} }
return *this; return *this;
} }
@ -134,16 +131,16 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept { Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Moving segment --")); //DEBUG_PRINTLN(F("-- Moving segment --"));
if (this != &orig) { if (this != &orig) {
if (name) delete[] name; // free old name transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) delete _t; if (_t) { delete _t; _t = nullptr; }
if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;}
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr;
orig.leds = nullptr;
} }
return *this; return *this;
} }
@ -153,12 +150,7 @@ bool Segment::allocateData(size_t len) {
deallocateData(); deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
// do not use SPI RAM on ESP32 since it is slow // do not use SPI RAM on ESP32 since it is slow
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) data = (byte*) malloc(len);
//if (psramFound())
// data = (byte*) ps_malloc(len);
//else
//#endif
data = (byte*) malloc(len);
if (!data) return false; //allocation failed if (!data) return false; //allocation failed
Segment::addUsedSegmentData(len); Segment::addUsedSegmentData(len);
_dataLen = len; _dataLen = len;
@ -182,31 +174,11 @@ void Segment::deallocateData() {
* may free that data buffer. * may free that data buffer.
*/ */
void Segment::resetIfRequired() { void Segment::resetIfRequired() {
if (reset) { if (!reset) return;
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
//if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false);
}
}
void Segment::setUpLeds() { deallocateData();
// deallocation happens in resetIfRequired() as it is called when segment changes or in destructor next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
if (Segment::_globalLeds) reset = false;
#ifndef WLED_DISABLE_2D
leds = &Segment::_globalLeds[start + startY*Segment::maxWidth];
#else
leds = &Segment::_globalLeds[start];
#endif
else if (leds == nullptr && length() > 0) { //softhack007 quickfix - avoid malloc(0) which is undefined behaviour (should not happen, but i've seen it)
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards
//else
//#endif
leds = (CRGB*)malloc(sizeof(CRGB)*length());
}
} }
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
@ -331,7 +303,7 @@ void Segment::startTransition(uint16_t dur) {
// transition progression between 0-65535 // transition progression between 0-65535
uint16_t Segment::progress() { uint16_t Segment::progress() {
if (!transitional || !_t) return 0xFFFFU; if (!transitional || !_t) return 0xFFFFU;
uint32_t timeNow = millis(); unsigned long timeNow = millis();
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU; if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU;
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
} }
@ -360,7 +332,7 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
// blend palettes // blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // 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 // minimum blend time is 100ms maximum is 65535ms
uint32_t timeMS = millis() - _t->_start; unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends; uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette targetPalette = _t->_palT; // copy transitioning/temporary palette
@ -381,8 +353,9 @@ void Segment::handleTransition() {
} }
} }
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { // segId is given when called from network callback, changes are queued if that segment is currently in its effect function
//return if neither bounds nor grouping have changed void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t segId) {
// return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2); bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D
@ -391,10 +364,22 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
&& (!grp || (grouping == grp && spacing == spc)) && (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return; && (ofs == UINT16_MAX || ofs == offset)) return;
if (stop) fill(BLACK); //turn old segment range off if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0
grouping = grp;
spacing = spc;
} else {
grouping = 1;
spacing = 0;
}
if (ofs < UINT16_MAX) offset = ofs;
markForReset();
if (boundsUnchanged) return;
// apply change immediately
if (i2 <= i1) { //disable segment if (i2 <= i1) { //disable segment
stop = 0; stop = 0;
markForReset();
return; return;
} }
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
@ -407,13 +392,12 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y); stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y);
} }
#endif #endif
if (grp) { // safety check
grouping = grp; if (start >= stop || startY >= stopY) {
spacing = spc; stop = 0;
return;
} }
if (ofs < UINT16_MAX) offset = ofs; refreshLightCapabilities();
markForReset();
if (!boundsUnchanged) refreshLightCapabilities();
} }
@ -460,8 +444,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved // if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
if (fx != mode) { if (fx != mode) {
startTransition(strip.getTransition()); // set effect transitions if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
//markForReset(); // transition will handle this
mode = fx; mode = fx;
// load default values from effect string // load default values from effect string
@ -546,8 +529,7 @@ uint16_t Segment::virtualLength() const {
return vLen; return vLen;
} }
#endif #endif
uint16_t groupLen = groupLength(); uint16_t groupLen = groupLength(); // is always >= 1
if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ...
uint16_t vLength = (length() + groupLen - 1) / groupLen; uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength; return vLength;
@ -555,6 +537,7 @@ uint16_t Segment::virtualLength() const {
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{ {
if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif #endif
@ -622,8 +605,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
#endif #endif
if (leds) leds[i] = col;
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) { if (_bri_t < 255) {
@ -665,6 +646,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
// anti-aliased normalized version of setPixelColor() // anti-aliased normalized version of setPixelColor()
void Segment::setPixelColor(float i, uint32_t col, bool aa) void Segment::setPixelColor(float i, uint32_t col, bool aa)
{ {
if (!isActive()) return; // not active
int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows) int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows)
i -= int(i); i -= int(i);
@ -696,6 +678,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i) uint32_t Segment::getPixelColor(int i)
{ {
if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
int vStrip = i>>16; int vStrip = i>>16;
#endif #endif
@ -723,8 +706,6 @@ uint32_t Segment::getPixelColor(int i)
} }
#endif #endif
if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0);
if (reverse) i = virtualLength() - i - 1; if (reverse) i = virtualLength() - i - 1;
i *= groupLength(); i *= groupLength();
i += start; i += start;
@ -765,6 +746,11 @@ void Segment::refreshLightCapabilities() {
uint16_t segStartIdx = 0xFFFFU; uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0; uint16_t segStopIdx = 0;
if (!isActive()) {
_capabilities = 0;
return;
}
if (start < Segment::maxWidth * Segment::maxHeight) { if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments) // we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) { for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
@ -809,6 +795,7 @@ void Segment::refreshLightCapabilities() {
* Fills segment with color * Fills segment with color
*/ */
void Segment::fill(uint32_t c) { void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
@ -824,6 +811,7 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) { void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active
uint32_t col = getPixelColor(n); uint32_t col = getPixelColor(n);
uint8_t r = R(col); uint8_t r = R(col);
uint8_t g = G(col); uint8_t g = G(col);
@ -842,6 +830,7 @@ void Segment::addPixelColor(int n, uint32_t color, bool fast) {
} }
void Segment::fadePixelColor(uint16_t n, uint8_t fade) { void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
setPixelColor(n, pix); setPixelColor(n, pix);
} }
@ -850,6 +839,7 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
* fade out function, higher rate = quicker fade * fade out function, higher rate = quicker fade
*/ */
void Segment::fade_out(uint8_t rate) { void Segment::fade_out(uint8_t rate) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -887,7 +877,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8() // fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) { void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -902,7 +892,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/ */
void Segment::blur(uint8_t blur_amount) void Segment::blur(uint8_t blur_amount)
{ {
if (blur_amount == 0) return; // optimization: 0 means "don't blur" if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (is2D()) { if (is2D()) {
// compatibility with 2D // compatibility with 2D
@ -1068,24 +1058,6 @@ void WS2812FX::finalizeInit(void)
Segment::maxHeight = 1; Segment::maxHeight = 1;
} }
//initialize leds array. TBD: realloc if nr of leds change
if (Segment::_globalLeds) {
purgeSegments(true);
free(Segment::_globalLeds);
Segment::_globalLeds = nullptr;
}
if (useLedsArray) {
size_t arrSize = sizeof(CRGB) * getLengthTotal();
// softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards (see setUpLeds())
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
//else
//#endif
Segment::_globalLeds = (CRGB*) malloc(arrSize);
if (Segment::_globalLeds && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize);
}
//segments are created in makeAutoSegments(); //segments are created in makeAutoSegments();
DEBUG_PRINTLN(F("Loading custom palettes")); DEBUG_PRINTLN(F("Loading custom palettes"));
loadCustomPalettes(); // (re)load all custom palettes loadCustomPalettes(); // (re)load all custom palettes
@ -1094,7 +1066,7 @@ void WS2812FX::finalizeInit(void)
} }
void WS2812FX::service() { void WS2812FX::service() {
uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase; now = nowUp + timebase;
if (nowUp - _lastShow < MIN_SHOW_DELAY) return; if (nowUp - _lastShow < MIN_SHOW_DELAY) return;
bool doShow = false; bool doShow = false;
@ -1107,12 +1079,9 @@ void WS2812FX::service() {
// reset the segment runtime data if needed // reset the segment runtime data if needed
seg.resetIfRequired(); seg.resetIfRequired();
if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time // last condition ensures all solid segments are updated at the same time
if(nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) if (seg.isActive() && (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)))
{ {
if (seg.grouping == 0) seg.grouping = 1; //sanity check
doShow = true; doShow = true;
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
@ -1136,16 +1105,24 @@ void WS2812FX::service() {
seg.next_time = nowUp + delay; seg.next_time = nowUp + delay;
} }
if (_segment_index == _queuedChangesSegId) setUpSegmentFromQueuedChanges();
_segment_index++; _segment_index++;
} }
_virtualSegmentLength = 0; _virtualSegmentLength = 0;
busses.setSegmentCCT(-1); busses.setSegmentCCT(-1);
if(doShow) { _isServicing = false;
_triggered = false;
#ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow effects."));
#endif
if (doShow) {
yield(); yield();
show(); show();
} }
_triggered = false; #ifdef WLED_DEBUG
_isServicing = false; if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow strip."));
#endif
} }
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
@ -1174,7 +1151,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA) #define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
//you can set it to 0 if the ESP is powered by USB and the LEDs by external //you can set it to 0 if the ESP is powered by USB and the LEDs by external
void WS2812FX::estimateCurrentAndLimitBri() { uint8_t WS2812FX::estimateCurrentAndLimitBri() {
//power limit calculation //power limit calculation
//each LED can draw up 195075 "power units" (approx. 53mA) //each LED can draw up 195075 "power units" (approx. 53mA)
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step //one PU is the power it takes to have 1 channel 1 step brighter per brightness step
@ -1182,35 +1159,28 @@ void WS2812FX::estimateCurrentAndLimitBri() {
bool useWackyWS2815PowerModel = false; bool useWackyWS2815PowerModel = false;
byte actualMilliampsPerLed = milliampsPerLed; byte actualMilliampsPerLed = milliampsPerLed;
if(milliampsPerLed == 255) { if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
currentMilliamps = 0;
return _brightness;
}
if (milliampsPerLed == 255) {
useWackyWS2815PowerModel = true; useWackyWS2815PowerModel = true;
actualMilliampsPerLed = 12; // from testing an actual strip actualMilliampsPerLed = 12; // from testing an actual strip
} }
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation size_t powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power
currentMilliamps = 0;
busses.setBrightness(_brightness);
return;
}
uint16_t pLen = getLengthPhysical();
uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed;
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power
if (powerBudget > puPerMilliamp * pLen) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= puPerMilliamp * pLen;
} else {
powerBudget = 0;
}
uint32_t powerSum = 0;
size_t pLen = 0; //getLengthPhysical();
size_t powerSum = 0;
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) { for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(bNum); Bus *bus = busses.getBus(bNum);
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
uint16_t len = bus->getLength(); uint16_t len = bus->getLength();
pLen += len;
uint32_t busPowerSum = 0; uint32_t busPowerSum = 0;
for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED
uint32_t c = bus->getPixelColor(i); uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling
byte r = R(c), g = G(c), b = B(c), w = W(c); byte r = R(c), g = G(c), b = B(c), w = W(c);
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
@ -1222,51 +1192,75 @@ void WS2812FX::estimateCurrentAndLimitBri() {
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3; busPowerSum *= 3;
busPowerSum = busPowerSum >> 2; //same as /= 4 busPowerSum >>= 2; //same as /= 4
} }
powerSum += busPowerSum; powerSum += busPowerSum;
} }
uint32_t powerSum0 = powerSum; if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget
//powerSum *= _brightness; // for NPBrightnessBus powerBudget -= pLen;
powerSum *= 255; // no need to scale down powerSum - NPB-LG getPixelColor returns colors scaled down by brightness } else {
powerBudget = 0;
}
if (powerSum > powerBudget) //scale brightness down to stay in current limit // powerSum has all the values of channels summed (max would be pLen*765 as white is excluded) so convert to milliAmps
{ powerSum = (powerSum * actualMilliampsPerLed) / 765;
float scale = (float)powerBudget / (float)powerSum;
uint8_t newBri = _brightness;
if (powerSum * _brightness / 255 > powerBudget) { //scale brightness down to stay in current limit
float scale = (float)(powerBudget * 255) / (float)(powerSum * _brightness);
uint16_t scaleI = scale * 255; uint16_t scaleI = scale * 255;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
uint8_t newBri = scale8(_brightness, scaleB); newBri = scale8(_brightness, scaleB) + 1;
// to keep brightness uniform, sets virtual busses too - softhack007: apply reductions immediately
if (scaleB < 255) busses.setBrightness(scaleB, true); // NPB-LG has already applied brightness, so its suffifient to post-apply scaling ==> use scaleB instead of newBri
busses.setBrightness(newBri, false); // set new brightness for next frame
//currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; // for NPBrightnessBus
currentMilliamps = (powerSum0 * scaleB) / puPerMilliamp; // for NPBus-LG
} else {
currentMilliamps = powerSum / puPerMilliamp;
busses.setBrightness(_brightness, false); // set new brightness for next frame
} }
currentMilliamps = (powerSum * newBri) / 255;
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
currentMilliamps += pLen; //add standby power back to estimate currentMilliamps += pLen; //add standby power (1mA/LED) back to estimate
return newBri;
} }
void WS2812FX::show(void) { void WS2812FX::show(void) {
// avoid race condition, caputre _callback value // avoid race condition, caputre _callback value
show_callback callback = _callback; show_callback callback = _callback;
if (callback) callback(); if (callback) callback();
estimateCurrentAndLimitBri(); #ifdef WLED_DEBUG
static unsigned long sumMicros = 0, sumCurrent = 0;
static size_t calls = 0;
unsigned long microsStart = micros();
#endif
uint8_t newBri = estimateCurrentAndLimitBri();
busses.setBrightness(newBri); // "repaints" all pixels if brightness changed
#ifdef WLED_DEBUG
sumCurrent += micros() - microsStart;
#endif
// some buses send asynchronously and this method will return before // some buses send asynchronously and this method will return before
// all of the data has been sent. // all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
busses.show(); busses.show();
// restore bus brightness to its original value
// this is done right after show, so this is only OK if LED updates are completed before show() returns
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _brightness) busses.setBrightness(_brightness);
#ifdef WLED_DEBUG
sumMicros += micros() - microsStart;
if (++calls == 100) {
DEBUG_PRINTF("%d show calls: %lu[us] avg: %lu[us] (current: %lu[us] avg: %lu[us])\n", calls, sumMicros, sumMicros/calls, sumCurrent, sumCurrent/calls);
sumMicros = sumCurrent = 0;
calls = 0;
}
#endif
unsigned long now = millis(); unsigned long now = millis();
unsigned long diff = now - _lastShow; size_t diff = now - _lastShow;
uint16_t fpsCurr = 200; size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff; if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
_lastShow = now; _lastShow = now;
} }
@ -1323,6 +1317,8 @@ void WS2812FX::setCCT(uint16_t k) {
} }
} }
// direct=true either expects the caller to call show() themselves (realtime modes) or be ok waiting for the next frame for the change to apply
// direct=false immediately triggers an effect redraw
void WS2812FX::setBrightness(uint8_t b, bool direct) { void WS2812FX::setBrightness(uint8_t b, bool direct) {
if (gammaCorrectBri) b = gamma8(b); if (gammaCorrectBri) b = gamma8(b);
if (_brightness == b) return; if (_brightness == b) return;
@ -1332,12 +1328,12 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
seg.freeze = false; seg.freeze = false;
} }
} }
if (direct) { // setting brightness with NeoPixelBusLg has no effect on already painted pixels,
// would be dangerous if applied immediately (could exceed ABL), but will not output until the next show() // so we need to force an update to existing buffer
busses.setBrightness(b); busses.setBrightness(b);
} else { if (!direct) {
unsigned long t = millis(); unsigned long t = millis();
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
} }
} }
@ -1444,9 +1440,32 @@ Segment& WS2812FX::getSegment(uint8_t id) {
return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors
} }
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) { // sets new segment bounds, queues if that segment is currently running
if (n >= _segments.size()) return; void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
_segments[n].setUp(i1, i2, grouping, spacing, offset, startY, stopY); if (segId >= getSegmentsNum()) {
if (i2 <= i1) return; // do not append empty/inactive segments
appendSegment(Segment(0, strip.getLengthTotal()));
segId = getSegmentsNum()-1; // segments are added at the end of list
}
if (_queuedChangesSegId == segId) _queuedChangesSegId = 255; // cancel queued change if already queued for this segment
if (segId < getMaxSegments() && segId == getCurrSegmentId() && isServicing()) { // queue change to prevent concurrent access
// queuing a change for a second segment will lead to the loss of the first change if not yet applied
// however this is not a problem as the queued change is applied immediately after the effect function in that segment returns
_qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId;
return; // queued changes are applied immediately after effect function returns
}
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
}
void WS2812FX::setUpSegmentFromQueuedChanges() {
if (_queuedChangesSegId >= getSegmentsNum()) return;
getSegment(_queuedChangesSegId).setUp(_qStart, _qStop, _qGrouping, _qSpacing, _qOffset, _qStartY, _qStopY);
_queuedChangesSegId = 255;
} }
void WS2812FX::restartRuntime() { void WS2812FX::restartRuntime() {
@ -1614,7 +1633,7 @@ void WS2812FX::printSize() {
DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *))); DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t)); DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
size = getLengthTotal(); size = getLengthTotal();
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB)); if (useGlobalLedBuffer) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB));
} }
#endif #endif

View File

@ -91,96 +91,168 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) {
return RGBW32(r, g, b, w); return RGBW32(r, g, b, w);
} }
uint8_t *Bus::allocData(size_t size) {
if (_data) free(_data); // should not happen, but for safety
return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr);
}
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) {
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
, _skip(bc.skipAmount) //sacrificial pixels
, _colorOrder(bc.colorOrder)
, _colorOrderMap(com)
{
if (!IS_DIGITAL(bc.type) || !bc.count) return; if (!IS_DIGITAL(bc.type) || !bc.count) return;
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_frequencykHz = 0U; _frequencykHz = 0U;
_pins[0] = bc.pins[0]; _pins[0] = bc.pins[0];
if (IS_2PIN(bc.type)) { if (IS_2PIN(bc.type)) {
if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup(); return; cleanup();
return;
} }
_pins[1] = bc.pins[1]; _pins[1] = bc.pins[1];
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined _frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
} }
reversed = bc.reversed;
_needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814;
_skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip;
_iType = PolyBus::getI(bc.type, _pins, nr); _iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return; if (_iType == I_NONE) return;
uint16_t lenToCreate = _len; if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus _buffering = bc.doubleBuffer;
_busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr, _frequencykHz); uint16_t lenToCreate = bc.count;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
_valid = (_busPtr != nullptr); _valid = (_busPtr != nullptr);
_colorOrder = bc.colorOrder; DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], _pins[1], _iType);
DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, _len, bc.type, _pins[0],_pins[1],_iType);
} }
void BusDigital::show() { void BusDigital::show() {
PolyBus::show(_busPtr, _iType); if (!_valid) return;
if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
for (size_t i=0; i<_len; i++) {
size_t offset = i*channels;
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
uint32_t c;
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3)
switch (i%3) {
case 0: c = RGBW32(_data[offset] , _data[offset+1], _data[offset+2], 0); break;
case 1: c = RGBW32(_data[offset-1], _data[offset] , _data[offset+1], 0); break;
case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break;
}
} else {
c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0));
}
uint16_t pix = i;
if (_reversed) pix = _len - pix -1;
else pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
}
}
PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important
} }
bool BusDigital::canShow() { bool BusDigital::canShow() {
if (!_valid) return true;
return PolyBus::canShow(_busPtr, _iType); return PolyBus::canShow(_busPtr, _iType);
} }
void BusDigital::setBrightness(uint8_t b, bool immediate) { void BusDigital::setBrightness(uint8_t b) {
if (_bri == b) return;
//Fix for turning off onboard LED breaking bus //Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN #ifdef LED_BUILTIN
if (_bri == 0 && b > 0) { if (_bri == 0) { // && b > 0, covered by guard if above
if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) reinit();
} }
#endif #endif
Bus::setBrightness(b, immediate); uint8_t prevBri = _bri;
PolyBus::setBrightness(_busPtr, _iType, b, immediate); Bus::setBrightness(b);
PolyBus::setBrightness(_busPtr, _iType, b);
if (_buffering) return;
// must update/repaint every LED in the NeoPixelBus buffer to the new brightness
// the only case where repainting is unnecessary is when all pixels are set after the brightness change but before the next show
// (which we can't rely on)
uint16_t hwLen = _len;
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
for (uint_fast16_t i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0),prevBri);
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0);
}
} }
//If LEDs are skipped, it is possible to use the first as a status LED. //If LEDs are skipped, it is possible to use the first as a status LED.
//TODO only show if no new show due in the next 50ms //TODO only show if no new show due in the next 50ms
void BusDigital::setStatusPixel(uint32_t c) { void BusDigital::setStatusPixel(uint32_t c) {
if (_skip && canShow()) { if (_valid && _skip) {
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
PolyBus::show(_busPtr, _iType); if (canShow()) PolyBus::show(_busPtr, _iType);
} }
} }
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH_X3) c = autoWhiteCalc(c); if (!_valid) return;
if (Bus::hasWhite(_type)) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
if (reversed) pix = _len - pix -1; if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
else pix += _skip; size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); size_t offset = pix*channels;
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs if (Bus::hasRGB(_type)) {
uint16_t pOld = pix; _data[offset++] = R(c);
pix = IC_INDEX_WS2812_1CH_3X(pix); _data[offset++] = G(c);
uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co); _data[offset++] = B(c);
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
} }
if (Bus::hasWhite(_type)) _data[offset] = W(c);
} else {
if (_reversed) pix = _len - pix -1;
else pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint16_t pOld = pix;
pix = IC_INDEX_WS2812_1CH_3X(pix);
uint32_t cOld = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, pix, co),_bri);
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
}
}
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
} }
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
} }
// returns original color if global buffering is enabled, else returns lossly restored color from bus
uint32_t BusDigital::getPixelColor(uint16_t pix) { uint32_t BusDigital::getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1; if (!_valid) return 0;
else pix += _skip; if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs size_t offset = pix*channels;
uint16_t pOld = pix; uint32_t c;
pix = IC_INDEX_WS2812_1CH_3X(pix); if (!Bus::hasRGB(_type)) {
uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, pix, co); c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
switch (pOld % 3) { // get only the single channel } else {
case 0: c = RGBW32(G(c), G(c), G(c), G(c)); break; c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], Bus::hasWhite(_type) ? _data[offset+3] : 0);
case 1: c = RGBW32(R(c), R(c), R(c), R(c)); break; }
case 2: c = RGBW32(B(c), B(c), B(c), B(c)); break; return c;
} else {
if (_reversed) pix = _len - pix -1;
else pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint8_t r = R(c);
uint8_t g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
uint8_t b = _reversed ? G(c) : B(c);
switch (pix % 3) { // get only the single channel
case 0: c = RGBW32(g, g, g, g); break;
case 1: c = RGBW32(r, r, r, r); break;
case 2: c = RGBW32(b, b, b, b); break;
}
} }
return c; return c;
} }
return PolyBus::getPixelColor(_busPtr, _iType, pix, co);
} }
uint8_t BusDigital::getPins(uint8_t* pinArray) { uint8_t BusDigital::getPins(uint8_t* pinArray) {
@ -196,6 +268,7 @@ void BusDigital::setColorOrder(uint8_t colorOrder) {
} }
void BusDigital::reinit() { void BusDigital::reinit() {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins); PolyBus::begin(_busPtr, _iType, _pins);
} }
@ -205,13 +278,15 @@ void BusDigital::cleanup() {
_iType = I_NONE; _iType = I_NONE;
_valid = false; _valid = false;
_busPtr = nullptr; _busPtr = nullptr;
if (_data != nullptr) freeData();
pinManager.deallocatePin(_pins[1], PinOwner::BusDigital); pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
pinManager.deallocatePin(_pins[0], PinOwner::BusDigital); pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
} }
BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusPwm::BusPwm(BusConfig &bc)
_valid = false; : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
{
if (!IS_PWM(bc.type)) return; if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type); uint8_t numPins = NUM_PWM_PINS(bc.type);
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ; _frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
@ -229,7 +304,7 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
for (uint8_t i = 0; i < numPins; i++) { for (uint8_t i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i]; uint8_t currentPin = bc.pins[i];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return; deallocatePins(); return;
} }
_pins[i] = currentPin; //store only after allocatePin() succeeds _pins[i] = currentPin; //store only after allocatePin() succeeds
#ifdef ESP8266 #ifdef ESP8266
@ -239,7 +314,7 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
ledcAttachPin(_pins[i], _ledcStart + i); ledcAttachPin(_pins[i], _ledcStart + i);
#endif #endif
} }
reversed = bc.reversed; _data = _pwmdata; // avoid malloc() and use stack
_valid = true; _valid = true;
} }
@ -307,7 +382,7 @@ void BusPwm::show() {
uint8_t numPins = NUM_PWM_PINS(_type); uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) { for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255; uint8_t scaled = (_data[i] * _bri) / 255;
if (reversed) scaled = 255 - scaled; if (_reversed) scaled = 255 - scaled;
#ifdef ESP8266 #ifdef ESP8266
analogWrite(_pins[i], scaled); analogWrite(_pins[i], scaled);
#else #else
@ -342,8 +417,10 @@ void BusPwm::deallocatePins() {
} }
BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusOnOff::BusOnOff(BusConfig &bc)
_valid = false; : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
, _onoffdata(0)
{
if (bc.type != TYPE_ONOFF) return; if (bc.type != TYPE_ONOFF) return;
uint8_t currentPin = bc.pins[0]; uint8_t currentPin = bc.pins[0];
@ -352,7 +429,7 @@ BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
} }
_pin = currentPin; //store only after allocatePin() succeeds _pin = currentPin; //store only after allocatePin() succeeds
pinMode(_pin, OUTPUT); pinMode(_pin, OUTPUT);
reversed = bc.reversed; _data = &_onoffdata; // avoid malloc() and use stack
_valid = true; _valid = true;
} }
@ -363,18 +440,17 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
uint8_t g = G(c); uint8_t g = G(c);
uint8_t b = B(c); uint8_t b = B(c);
uint8_t w = W(c); uint8_t w = W(c);
_data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
_data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
} }
uint32_t BusOnOff::getPixelColor(uint16_t pix) { uint32_t BusOnOff::getPixelColor(uint16_t pix) {
if (!_valid) return 0; if (!_valid) return 0;
return RGBW32(_data, _data, _data, _data); return RGBW32(_data[0], _data[0], _data[0], _data[0]);
} }
void BusOnOff::show() { void BusOnOff::show() {
if (!_valid) return; if (!_valid) return;
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
} }
uint8_t BusOnOff::getPins(uint8_t* pinArray) { uint8_t BusOnOff::getPins(uint8_t* pinArray) {
@ -384,8 +460,10 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) {
} }
BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusNetwork::BusNetwork(BusConfig &bc)
_valid = false; : Bus(bc.type, bc.start, bc.autoWhite, bc.count)
, _broadcastLock(false)
{
switch (bc.type) { switch (bc.type) {
case TYPE_NET_ARTNET_RGB: case TYPE_NET_ARTNET_RGB:
_rgbw = false; _rgbw = false;
@ -401,18 +479,13 @@ BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
break; break;
} }
_UDPchannels = _rgbw ? 4 : 3; _UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
if (_data == nullptr) return;
memset(_data, 0, bc.count * _UDPchannels);
_len = bc.count;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_broadcastLock = false; _valid = (allocData(_len * _UDPchannels) != nullptr);
_valid = true;
} }
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return; if (!_valid || pix >= _len) return;
if (hasWhite()) c = autoWhiteCalc(c); if (_rgbw) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels; uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c); _data[offset] = R(c);
@ -424,7 +497,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
uint32_t BusNetwork::getPixelColor(uint16_t pix) { uint32_t BusNetwork::getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0; if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels; uint16_t offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0); return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (_rgbw ? _data[offset+3] : 0));
} }
void BusNetwork::show() { void BusNetwork::show() {
@ -444,8 +517,7 @@ uint8_t BusNetwork::getPins(uint8_t* pinArray) {
void BusNetwork::cleanup() { void BusNetwork::cleanup() {
_type = I_NONE; _type = I_NONE;
_valid = false; _valid = false;
if (_data != nullptr) free(_data); freeData();
_data = nullptr;
} }
@ -506,7 +578,7 @@ void BusManager::setStatusPixel(uint32_t c) {
} }
} }
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i]; Bus* b = busses[i];
uint16_t bstart = b->getStart(); uint16_t bstart = b->getStart();
@ -515,9 +587,9 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct)
} }
} }
void BusManager::setBrightness(uint8_t b, bool immediate) { void BusManager::setBrightness(uint8_t b) {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b, immediate); busses[i]->setBrightness(b);
} }
} }

View File

@ -18,6 +18,10 @@
#define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3) #define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3)
#define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs #define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs
// flag for using double buffering in BusDigital
extern bool useGlobalLedBuffer;
//temporary struct for passing bus configuration to bus //temporary struct for passing bus configuration to bus
struct BusConfig { struct BusConfig {
uint8_t type; uint8_t type;
@ -30,15 +34,25 @@ struct BusConfig {
uint8_t autoWhite; uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
uint16_t frequency; uint16_t frequency;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U) { bool doubleBuffer;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false)
: count(len)
, start(pstart)
, colorOrder(pcolorOrder)
, reversed(rev)
, skipAmount(skip)
, autoWhite(aw)
, frequency(clock_kHz)
, doubleBuffer(dblBfr)
{
refreshReq = (bool) GET_BIT(busType,7); refreshReq = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz; size_t nPins = 1;
uint8_t nPins = 1;
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2; else if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i]; for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
} }
//validates start and length and extends total if needed //validates start and length and extends total if needed
@ -54,6 +68,7 @@ struct BusConfig {
} }
}; };
// Defines an LED Strip and its color ordering. // Defines an LED Strip and its color ordering.
struct ColorOrderMapEntry { struct ColorOrderMapEntry {
uint16_t start; uint16_t start;
@ -64,9 +79,7 @@ struct ColorOrderMapEntry {
struct ColorOrderMap { struct ColorOrderMap {
void add(uint16_t start, uint16_t len, uint8_t colorOrder); void add(uint16_t start, uint16_t len, uint8_t colorOrder);
uint8_t count() const { uint8_t count() const { return _count; }
return _count;
}
void reset() { void reset() {
_count = 0; _count = 0;
@ -87,56 +100,63 @@ struct ColorOrderMap {
ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS]; ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
}; };
//parent class of BusDigital, BusPwm, and BusNetwork //parent class of BusDigital, BusPwm, and BusNetwork
class Bus { class Bus {
public: public:
Bus(uint8_t type, uint16_t start, uint8_t aw) Bus(uint8_t type, uint16_t start, uint8_t aw, uint16_t len = 1, bool reversed = false, bool refresh = false)
: _bri(255) : _type(type)
, _len(1) , _bri(255)
, _start(start)
, _len(len)
, _reversed(reversed)
, _valid(false) , _valid(false)
, _needsRefresh(false) , _needsRefresh(refresh)
, _data(nullptr) // keep data access consistent across all types of buses
{ {
_type = type;
_start = start;
_autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY; _autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY;
}; };
virtual ~Bus() {} //throw the bus under the bus virtual ~Bus() {} //throw the bus under the bus
virtual void show() = 0; virtual void show() = 0;
virtual bool canShow() { return true; } virtual bool canShow() { return true; }
virtual void setStatusPixel(uint32_t c) {} virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual uint32_t getPixelColor(uint16_t pix) { return 0; } virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; }; virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void cleanup() = 0; virtual void cleanup() = 0;
virtual uint8_t getPins(uint8_t* pinArray) { return 0; } virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
virtual uint16_t getLength() { return _len; } virtual uint16_t getLength() { return _len; }
virtual void setColorOrder() {} virtual void setColorOrder() {}
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; } virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; } virtual uint8_t skippedLeds() { return 0; }
virtual uint16_t getFrequency() { return 0U; } virtual uint16_t getFrequency() { return 0U; }
inline uint16_t getStart() { return _start; } inline void setReversed(bool reversed) { _reversed = reversed; }
inline void setStart(uint16_t start) { _start = start; } inline uint16_t getStart() { return _start; }
inline uint8_t getType() { return _type; } inline void setStart(uint16_t start) { _start = start; }
inline bool isOk() { return _valid; } inline uint8_t getType() { return _type; }
inline bool isOffRefreshRequired() { return _needsRefresh; } inline bool isOk() { return _valid; }
inline bool isReversed() { return _reversed; }
inline bool isOffRefreshRequired() { return _needsRefresh; }
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; } bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
virtual bool hasRGB() { virtual bool hasRGB(void) { return Bus::hasRGB(_type); }
if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; static bool hasRGB(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF) return false;
return true; return true;
} }
virtual bool hasWhite() { return Bus::hasWhite(_type); } virtual bool hasWhite(void) { return Bus::hasWhite(_type); }
static bool hasWhite(uint8_t type) { static bool hasWhite(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel
return false; return false;
} }
virtual bool hasCCT() { virtual bool hasCCT(void) { return Bus::hasCCT(_type); }
if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA || static bool hasCCT(uint8_t type) {
_type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true; if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA ||
type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH) return true;
return false; return false;
} }
static void setCCT(uint16_t cct) { static void setCCT(uint16_t cct) {
@ -155,107 +175,87 @@ class Bus {
inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; } inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; }
inline static uint8_t getGlobalAWMode() { return _gAWM; } inline static uint8_t getGlobalAWMode() { return _gAWM; }
bool reversed = false;
protected: protected:
uint8_t _type; uint8_t _type;
uint8_t _bri; uint8_t _bri;
uint16_t _start; uint16_t _start;
uint16_t _len; uint16_t _len;
bool _reversed;
bool _valid; bool _valid;
bool _needsRefresh; bool _needsRefresh;
uint8_t _autoWhiteMode; uint8_t _autoWhiteMode;
uint8_t *_data;
static uint8_t _gAWM; static uint8_t _gAWM;
static int16_t _cct; static int16_t _cct;
static uint8_t _cctBlend; static uint8_t _cctBlend;
uint32_t autoWhiteCalc(uint32_t c); uint32_t autoWhiteCalc(uint32_t c);
uint8_t *allocData(size_t size = 1);
void freeData() { if (_data != nullptr) free(_data); _data = nullptr; }
}; };
class BusDigital : public Bus { class BusDigital : public Bus {
public: public:
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com); BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
~BusDigital() { cleanup(); }
inline void show(); void show();
bool canShow(); bool canShow();
void setBrightness(uint8_t b);
void setBrightness(uint8_t b, bool immediate);
void setStatusPixel(uint32_t c); void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix);
uint8_t getColorOrder() {
return _colorOrder;
}
uint16_t getLength() {
return _len - _skip;
}
uint8_t getPins(uint8_t* pinArray);
void setColorOrder(uint8_t colorOrder); void setColorOrder(uint8_t colorOrder);
uint32_t getPixelColor(uint16_t pix);
uint8_t skippedLeds() { uint8_t getColorOrder() { return _colorOrder; }
return _skip; uint8_t getPins(uint8_t* pinArray);
} uint8_t skippedLeds() { return _skip; }
uint16_t getFrequency() { return _frequencykHz; }
uint16_t getFrequency() { return _frequencykHz; }
void reinit(); void reinit();
void cleanup(); void cleanup();
~BusDigital() {
cleanup();
}
private: private:
uint8_t _colorOrder = COL_ORDER_GRB; uint8_t _skip;
uint8_t _pins[2] = {255, 255}; uint8_t _colorOrder;
uint8_t _iType = 0; //I_NONE; uint8_t _pins[2];
uint8_t _skip = 0; uint8_t _iType;
uint16_t _frequencykHz = 0U; uint16_t _frequencykHz;
void * _busPtr = nullptr; void * _busPtr;
const ColorOrderMap &_colorOrderMap; const ColorOrderMap &_colorOrderMap;
bool _buffering; // temporary until we figure out why comparison "_data != nullptr" causes severe FPS drop
inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) {
if (restoreBri < 255) {
uint8_t* chan = (uint8_t*) &c;
for (uint_fast8_t i=0; i<4; i++) {
uint_fast16_t val = chan[i];
chan[i] = ((val << 8) + restoreBri) / (restoreBri + 1); //adding _bri slighly improves recovery / stops degradation on re-scale
}
}
return c;
}
}; };
class BusPwm : public Bus { class BusPwm : public Bus {
public: public:
BusPwm(BusConfig &bc); BusPwm(BusConfig &bc);
~BusPwm() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix); //does no index check
//does no index check uint8_t getPins(uint8_t* pinArray);
uint32_t getPixelColor(uint16_t pix);
void show();
uint8_t getPins(uint8_t* pinArray);
uint16_t getFrequency() { return _frequency; } uint16_t getFrequency() { return _frequency; }
void show();
void cleanup() { void cleanup() { deallocatePins(); }
deallocatePins();
}
~BusPwm() {
cleanup();
}
private: private:
uint8_t _pins[5] = {255, 255, 255, 255, 255}; uint8_t _pins[5];
uint8_t _data[5] = {0}; uint8_t _pwmdata[5];
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255; uint8_t _ledcStart;
#endif #endif
uint16_t _frequency = 0U; uint16_t _frequency;
void deallocatePins(); void deallocatePins();
}; };
@ -264,72 +264,46 @@ class BusPwm : public Bus {
class BusOnOff : public Bus { class BusOnOff : public Bus {
public: public:
BusOnOff(BusConfig &bc); BusOnOff(BusConfig &bc);
~BusOnOff() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix); uint32_t getPixelColor(uint16_t pix);
uint8_t getPins(uint8_t* pinArray);
void show(); void show();
void cleanup() { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); }
uint8_t getPins(uint8_t* pinArray);
void cleanup() {
pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
}
~BusOnOff() {
cleanup();
}
private: private:
uint8_t _pin = 255; uint8_t _pin;
uint8_t _data = 0; uint8_t _onoffdata;
}; };
class BusNetwork : public Bus { class BusNetwork : public Bus {
public: public:
BusNetwork(BusConfig &bc); BusNetwork(BusConfig &bc);
~BusNetwork() { cleanup(); }
bool hasRGB() { return true; } bool hasRGB() { return true; }
bool hasWhite() { return _rgbw; } bool hasWhite() { return _rgbw; }
bool canShow() { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix); uint32_t getPixelColor(uint16_t pix);
uint8_t getPins(uint8_t* pinArray);
void show(); void show();
bool canShow() {
// this should be a return value from UDP routine if it is still sending data out
return !_broadcastLock;
}
uint8_t getPins(uint8_t* pinArray);
uint16_t getLength() {
return _len;
}
void cleanup(); void cleanup();
~BusNetwork() {
cleanup();
}
private: private:
IPAddress _client; IPAddress _client;
uint8_t _UDPtype; uint8_t _UDPtype;
uint8_t _UDPchannels; uint8_t _UDPchannels;
bool _rgbw; bool _rgbw;
bool _broadcastLock; bool _broadcastLock;
byte *_data;
}; };
class BusManager { class BusManager {
public: public:
BusManager() {}; BusManager() : numBusses(0) {};
//utility to get the approx. memory usage of a given BusConfig //utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc); static uint32_t memUsage(BusConfig &bc);
@ -340,38 +314,24 @@ class BusManager {
void removeAll(); void removeAll();
void show(); void show();
void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setBrightness(uint8_t b, bool immediate=false); // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t getPixelColor(uint16_t pix);
bool canAllShow(); bool canAllShow();
void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c);
void setBrightness(uint8_t b);
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t getPixelColor(uint16_t pix);
Bus* getBus(uint8_t busNr); Bus* getBus(uint8_t busNr);
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t getTotalLength(); uint16_t getTotalLength();
inline uint8_t getNumBusses() const { return numBusses; }
inline void updateColorOrderMap(const ColorOrderMap &com) { inline void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); }
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); inline const ColorOrderMap& getColorOrderMap() const { return colorOrderMap; }
}
inline const ColorOrderMap& getColorOrderMap() const {
return colorOrderMap;
}
inline uint8_t getNumBusses() {
return numBusses;
}
private: private:
uint8_t numBusses = 0; uint8_t numBusses;
Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap colorOrderMap; ColorOrderMap colorOrderMap;

View File

@ -280,6 +280,7 @@ class PolyBus {
#endif #endif
if (clock_kHz) dotStar_strip->SetMethodSettings(NeoSpiSettings((uint32_t)clock_kHz*1000)); if (clock_kHz) dotStar_strip->SetMethodSettings(NeoSpiSettings((uint32_t)clock_kHz*1000));
} }
// Begin & initialize the PixelSettings for TM1814 strips. // Begin & initialize the PixelSettings for TM1814 strips.
template <class T> template <class T>
static void beginTM1814(void* busPtr) { static void beginTM1814(void* busPtr) {
@ -288,6 +289,7 @@ class PolyBus {
// Max current for each LED (22.5 mA). // Max current for each LED (22.5 mA).
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225)); tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225));
} }
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) { static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
@ -390,7 +392,8 @@ class PolyBus {
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Begin(); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Begin(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break;
} }
}; }
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) { static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
void* busPtr = nullptr; void* busPtr = nullptr;
switch (busType) { switch (busType) {
@ -491,104 +494,106 @@ class PolyBus {
} }
begin(busPtr, busType, pins, clock_kHz); begin(busPtr, busType, pins, clock_kHz);
return busPtr; return busPtr;
}; }
static void show(void* busPtr, uint8_t busType) {
static void show(void* busPtr, uint8_t busType, bool consistent = true) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->Show(); break; case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->Show(); break; case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->Show(); break; case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->Show(); break; case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->Show(); break; case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->Show(); break; case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->Show(); break; case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->Show(); break; case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->Show(); break; case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->Show(); break; case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->Show(); break; case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->Show(); break; case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->Show(); break; case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->Show(); break; case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->Show(); break; case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Show(); break; case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Show(); break; case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Show(); break; case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break; case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(); break; // case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Show(); break; // case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Show(consistent); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Show(); break; // case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Show(consistent); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break; case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(); break; case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break; case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(); break; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(consistent); break;
#endif #endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->Show(); break; case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Show(); break; case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(); break; // case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Show(); break; case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Show(consistent); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Show(); break; case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Show(consistent); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Show(); break; case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Show(consistent); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->Show(); break; case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->Show(consistent); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Show(); break; case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Show(consistent); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Show(); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Show(consistent); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Show(); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Show(consistent); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Show(); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Show(consistent); break;
} }
}; }
static bool canShow(void* busPtr, uint8_t busType) { static bool canShow(void* busPtr, uint8_t busType) {
switch (busType) { switch (busType) {
case I_NONE: return true; case I_NONE: return true;
@ -685,7 +690,8 @@ class PolyBus {
case I_SS_P98_3: return (static_cast<B_SS_P98_3*>(busPtr))->CanShow(); break; case I_SS_P98_3: return (static_cast<B_SS_P98_3*>(busPtr))->CanShow(); break;
} }
return true; return true;
}; }
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) { static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) {
uint8_t r = c >> 16; uint8_t r = c >> 16;
uint8_t g = c >> 8; uint8_t g = c >> 8;
@ -805,104 +811,106 @@ class PolyBus {
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
} }
}; }
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b, bool immediate) { // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM2_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM2_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM2_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_P98_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_P98_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); break;
} }
}; }
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
RgbwColor col(0,0,0,0); RgbwColor col(0,0,0,0);
switch (busType) { switch (busType) {

View File

@ -90,7 +90,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.cctBlending, hw_led[F("cb")]); CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
CJSON(strip.useLedsArray, hw_led[F("ld")]); CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// 2D Matrix Settings // 2D Matrix Settings
@ -134,7 +134,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS || !ins.isNull()) { if (fromFS || !ins.isNull()) {
uint8_t s = 0; // bus iterator uint8_t s = 0; // bus iterator
if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback
uint32_t mem = 0; uint32_t mem = 0, globalBufMem = 0;
uint16_t maxlen = 0;
bool busesChanged = false; bool busesChanged = false;
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
@ -160,12 +161,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode; uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode;
if (fromFS) { if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
mem += BusManager::memUsage(bc); mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip() if (useGlobalLedBuffer && start + length > maxlen) {
maxlen = start + length;
globalBufMem = maxlen * 4;
}
if (mem + globalBufMem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else { } else {
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
busesChanged = true; busesChanged = true;
} }
s++; s++;
@ -173,7 +178,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
doInitBusses = busesChanged; doInitBusses = busesChanged;
// finalization done in beginStrip() // finalization done in beginStrip()
} }
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus if (hw_led["rev"]) busses.getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
// read color order map configuration // read color order map configuration
JsonArray hw_com = hw[F("com")]; JsonArray hw_com = hw[F("com")];
@ -711,7 +716,7 @@ void serializeConfig() {
hw_led[F("cb")] = strip.cctBlending; hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps(); hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
hw_led[F("ld")] = strip.useLedsArray; hw_led[F("ld")] = useGlobalLedBuffer;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// 2D Matrix Settings // 2D Matrix Settings
@ -746,7 +751,7 @@ void serializeConfig() {
uint8_t nPins = bus->getPins(pins); uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]); for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]);
ins[F("order")] = bus->getColorOrder(); ins[F("order")] = bus->getColorOrder();
ins["rev"] = bus->reversed; ins["rev"] = bus->isReversed();
ins[F("skip")] = bus->skippedLeds(); ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType() & 0x7F; ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired(); ins["ref"] = bus->isOffRefreshRequired();

View File

@ -141,7 +141,7 @@
let len = parseInt(d.getElementsByName("LC"+n)[0].value); let len = parseInt(d.getElementsByName("LC"+n)[0].value);
len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too
let dbl = 0; let dbl = 0;
if (d.Sf.LD.checked) dbl = len * 3; // double buffering if (d.Sf.LD.checked) dbl = len * 4; // double buffering
if (t < 32) { if (t < 32) {
if (t==26 || t==29) len *= 2; // 16 bit LEDs if (t==26 || t==29) len *= 2; // 16 bit LEDs
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem

View File

@ -22,7 +22,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
int stop = elem["stop"] | -1; int stop = elem["stop"] | -1;
// if using vectors use this code to append segment // append segment
if (id >= strip.getSegmentsNum()) { if (id >= strip.getSegmentsNum()) {
if (stop <= 0) return false; // ignore empty/inactive segments if (stop <= 0) return false; // ignore empty/inactive segments
strip.appendSegment(Segment(0, strip.getLengthTotal())); strip.appendSegment(Segment(0, strip.getLengthTotal()));
@ -110,7 +110,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs; of = offsetAbs;
} }
if (stop > start && of > len -1) of = len -1; if (stop > start && of > len -1) of = len -1;
seg.setUp(start, stop, grp, spc, of, startY, stopY);
// update segment (delete if necessary)
// do not call seg.setUp() here, as it may cause a crash due to concurrent access if the segment is currently drawing effects
// WS2812FX handles queueing of the change
strip.setSegment(id, start, stop, grp, spc, of, startY, stopY);
if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else
@ -468,12 +472,14 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
if (segmentBounds) { if (segmentBounds) {
root["start"] = seg.start; root["start"] = seg.start;
root["stop"] = seg.stop; root["stop"] = seg.stop;
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) { if (strip.isMatrix) {
root[F("startY")] = seg.startY; root[F("startY")] = seg.startY;
root[F("stopY")] = seg.stopY; root[F("stopY")] = seg.stopY;
} }
#endif
} }
if (!forPreset) root["len"] = (seg.stop >= seg.start) ? (seg.stop - seg.start) : 0; if (!forPreset) root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping; root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing; root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset; root[F("of")] = seg.offset;
@ -1060,7 +1066,10 @@ void serveJson(AsyncWebServerRequest* request)
DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", lDoc.memoryUsage(), subJson); DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", lDoc.memoryUsage(), subJson);
size_t len = response->setLength(); #ifdef WLED_DEBUG
size_t len =
#endif
response->setLength();
DEBUG_PRINT(F("JSON content length: ")); DEBUG_PRINTLN(len); DEBUG_PRINT(F("JSON content length: ")); DEBUG_PRINTLN(len);
request->send(response); request->send(response);
@ -1090,9 +1099,13 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
for (size_t i= 0; i < used; i += n) for (size_t i= 0; i < used; i += n)
{ {
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map uint8_t r = R(c);
uint8_t g = qadd8(W(c), G(c)); uint8_t g = G(c);
uint8_t b = qadd8(W(c), B(c)); uint8_t b = B(c);
uint8_t w = W(c);
r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
g = scale8(qadd8(w, g), strip.getBrightness()); //G
b = scale8(qadd8(w, b), strip.getBrightness()); //B
olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0)); olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0));
} }
olen -= 1; olen -= 1;

View File

@ -195,7 +195,7 @@ void handleTransitions()
applyFinalBri(); applyFinalBri();
return; return;
} }
if (tper - tperLast < 0.004) return; if (tper - tperLast < 0.004f) return;
tperLast = tper; tperLast = tper;
briT = briOld + ((bri - briOld) * tper); briT = briOld + ((bri - briOld) * tper);
@ -205,7 +205,7 @@ void handleTransitions()
// legacy method, applies values from col, effectCurrent, ... to selected segments // legacy method, applies values from col, effectCurrent, ... to selected segments
void colorUpdated(byte callMode){ void colorUpdated(byte callMode) {
applyValuesToSelectedSegs(); applyValuesToSelectedSegs();
stateUpdated(callMode); stateUpdated(callMode);
} }

View File

@ -91,7 +91,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt());
strip.useLedsArray = request->hasArg(F("LD")); useGlobalLedBuffer = request->hasArg(F("LD"));
bool busesChanged = false; bool busesChanged = false;
for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) { for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
@ -153,7 +153,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
// actual finalization is done in WLED::loop() (removing old busses and adding new) // actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop // this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz); busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, useGlobalLedBuffer);
busesChanged = true; busesChanged = true;
} }
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed //doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
@ -797,7 +797,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
spcI = getNumVal(&req, pos); spcI = getNumVal(&req, pos);
} }
selseg.setUp(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY); strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0'; if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';

View File

@ -23,7 +23,7 @@ void WLED::reset()
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
ws.closeAll(1012); ws.closeAll(1012);
#endif #endif
long dly = millis(); unsigned long dly = millis();
while (millis() - dly < 450) { while (millis() - dly < 450) {
yield(); // enough time to send response to client yield(); // enough time to send response to client
} }
@ -35,10 +35,18 @@ void WLED::reset()
void WLED::loop() void WLED::loop()
{ {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
static unsigned long lastRun = 0;
unsigned long loopMillis = millis();
size_t loopDelay = loopMillis - lastRun;
if (lastRun == 0) loopDelay=0; // startup - don't have valid data from last run.
if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %ums.\n", loopDelay);
static unsigned long maxLoopMillis = 0;
static size_t avgLoopMillis = 0;
static unsigned long maxUsermodMillis = 0; static unsigned long maxUsermodMillis = 0;
static uint16_t avgUsermodMillis = 0; static size_t avgUsermodMillis = 0;
static unsigned long maxStripMillis = 0; static unsigned long maxStripMillis = 0;
static uint16_t avgStripMillis = 0; static size_t avgStripMillis = 0;
unsigned long stripMillis;
#endif #endif
handleTime(); handleTime();
@ -80,6 +88,9 @@ void WLED::loop()
yield(); yield();
} }
#ifdef WLED_DEBUG
stripMillis = millis();
#endif
if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled
{ {
if (apActive) dnsServer.processNextRequest(); if (apActive) dnsServer.processNextRequest();
@ -98,22 +109,18 @@ void WLED::loop()
handlePresets(); handlePresets();
yield(); yield();
#ifdef WLED_DEBUG
unsigned long stripMillis = millis();
#endif
if (!offMode || strip.isOffRefreshRequired()) if (!offMode || strip.isOffRefreshRequired())
strip.service(); strip.service();
#ifdef ESP8266 #ifdef ESP8266
else if (!noWifiSleep) else if (!noWifiSleep)
delay(1); //required to make sure ESP enters modem sleep (see #1184) delay(1); //required to make sure ESP enters modem sleep (see #1184)
#endif #endif
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
if (stripMillis > 50) DEBUG_PRINTLN("Slow strip.");
avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
} }
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
yield(); yield();
#ifdef ESP8266 #ifdef ESP8266
@ -152,11 +159,16 @@ void WLED::loop()
DEBUG_PRINTLN(F("Re-init busses.")); DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses) bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
busses.removeAll(); busses.removeAll();
uint32_t mem = 0; uint32_t mem = 0, globalBufMem = 0;
uint16_t maxlen = 0;
for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr) break; if (busConfigs[i] == nullptr) break;
mem += BusManager::memUsage(*busConfigs[i]); mem += BusManager::memUsage(*busConfigs[i]);
if (mem <= MAX_LED_MEMORY) { if (useGlobalLedBuffer && busConfigs[i]->start + busConfigs[i]->count > maxlen) {
maxlen = busConfigs[i]->start + busConfigs[i]->count;
globalBufMem = maxlen * 4;
}
if (mem + globalBufMem <= MAX_LED_MEMORY) {
busses.add(*busConfigs[i]); busses.add(*busConfigs[i]);
} }
delete busConfigs[i]; busConfigs[i] = nullptr; delete busConfigs[i]; busConfigs[i] = nullptr;
@ -177,8 +189,31 @@ void WLED::loop()
handleWs(); handleWs();
handleStatusLED(); handleStatusLED();
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
if (!strip.isUpdating())
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
// DEBUG serial logging (every 30s) // DEBUG serial logging (every 30s)
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
loopMillis = millis() - loopMillis;
if (loopMillis > 30) {
DEBUG_PRINTF("Loop took %lums.\n", loopMillis);
DEBUG_PRINTF("Usermods took %lums.\n", usermodMillis);
DEBUG_PRINTF("Strip took %lums.\n", stripMillis);
}
avgLoopMillis += loopMillis;
if (loopMillis > maxLoopMillis) maxLoopMillis = loopMillis;
if (millis() - debugTime > 29999) { if (millis() - debugTime > 29999) {
DEBUG_PRINTLN(F("---DEBUG INFO---")); DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis()); DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
@ -201,11 +236,13 @@ void WLED::loop()
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP()); DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
if (loops > 0) { // avoid division by zero if (loops > 0) { // avoid division by zero
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30); DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30);
DEBUG_PRINT(F("Loop time[ms]: ")); DEBUG_PRINT(avgLoopMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxLoopMillis);
DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis); DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis); DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis);
} }
strip.printSize(); strip.printSize();
loops = 0; loops = 0;
maxLoopMillis = 0;
maxUsermodMillis = 0; maxUsermodMillis = 0;
maxStripMillis = 0; maxStripMillis = 0;
avgUsermodMillis = 0; avgUsermodMillis = 0;
@ -213,21 +250,8 @@ void WLED::loop()
debugTime = millis(); debugTime = millis();
} }
loops++; loops++;
lastRun = millis();
#endif // WLED_DEBUG #endif // WLED_DEBUG
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
if (!strip.isUpdating())
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
} }
void WLED::enableWatchdog() { void WLED::enableWatchdog() {
@ -512,7 +536,7 @@ void WLED::initAP(bool resetAP)
DEBUG_PRINTLN(apSSID); DEBUG_PRINTLN(apSSID);
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); 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);
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2)) #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm); WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif #endif
@ -690,7 +714,7 @@ void WLED::initConnection()
WiFi.begin(clientSSID, clientPass); WiFi.begin(clientSSID, clientPass);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2)) #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm); WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif #endif
WiFi.setSleep(!noWifiSleep); WiFi.setSleep(!noWifiSleep);

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2307130 #define VERSION 2307180
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -331,12 +331,17 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
//if true, a segment per bus will be created on boot and LED settings save //if true, a segment per bus will be created on boot and LED settings save
//if false, only one segment spanning the total LEDs is created, //if false, only one segment spanning the total LEDs is created,
//but not on LED settings save if there is more than one segment currently //but not on LED settings save if there is more than one segment currently
WLED_GLOBAL bool autoSegments _INIT(false); WLED_GLOBAL bool autoSegments _INIT(false);
WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color #ifdef ESP8266
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct WLED_GLOBAL bool useGlobalLedBuffer _INIT(false); // double buffering disabled on ESP8266
WLED_GLOBAL bool gammaCorrectCol _INIT(true ); // use gamma correction on colors #else
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on ESP32
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value #endif
WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct
WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on colors
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color. WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
@ -508,7 +513,7 @@ WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use i
// wifi // wifi
WLED_GLOBAL bool apActive _INIT(false); WLED_GLOBAL bool apActive _INIT(false);
WLED_GLOBAL bool forceReconnect _INIT(false); WLED_GLOBAL bool forceReconnect _INIT(false);
WLED_GLOBAL uint32_t lastReconnectAttempt _INIT(0); WLED_GLOBAL unsigned long lastReconnectAttempt _INIT(0);
WLED_GLOBAL bool interfacesInited _INIT(false); WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false);

View File

@ -166,23 +166,24 @@ bool sendLiveLedsWs(uint32_t wsClient)
size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
size_t pos = (strip.isMatrix ? 4 : 2); // start of data size_t pos = (strip.isMatrix ? 4 : 2); // start of data
size_t bufSize = pos + (used/n)*3; size_t bufSize = pos + (used/n)*3;
size_t skipLines = 0;
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize); AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
if (!wsBuf) return false; //out of memory if (!wsBuf) return false; //out of memory
uint8_t* buffer = wsBuf->get(); uint8_t* buffer = wsBuf->get();
buffer[0] = 'L'; buffer[0] = 'L';
buffer[1] = 1; //version buffer[1] = 1; //version
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
size_t skipLines = 0;
if (strip.isMatrix) { if (strip.isMatrix) {
buffer[1] = 2; //version buffer[1] = 2; //version
buffer[2] = Segment::maxWidth; buffer[2] = Segment::maxWidth;
buffer[3] = Segment::maxHeight; buffer[3] = Segment::maxHeight;
if (Segment::maxWidth * Segment::maxHeight > MAX_LIVE_LEDS_WS*4) { if (used > MAX_LIVE_LEDS_WS*4) {
buffer[2] = Segment::maxWidth/4; buffer[2] = Segment::maxWidth/4;
buffer[3] = Segment::maxHeight/4; buffer[3] = Segment::maxHeight/4;
skipLines = 3; skipLines = 3;
} else if (Segment::maxWidth * Segment::maxHeight > MAX_LIVE_LEDS_WS) { } else if (used > MAX_LIVE_LEDS_WS) {
buffer[2] = Segment::maxWidth/2; buffer[2] = Segment::maxWidth/2;
buffer[3] = Segment::maxHeight/2; buffer[3] = Segment::maxHeight/2;
skipLines = 1; skipLines = 1;
@ -198,9 +199,13 @@ bool sendLiveLedsWs(uint32_t wsClient)
} }
#endif #endif
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
buffer[pos++] = qadd8(W(c), R(c)); //R, add white channel to RGB channels as a simple RGBW -> RGB map uint8_t r = R(c);
buffer[pos++] = qadd8(W(c), G(c)); //G uint8_t g = G(c);
buffer[pos++] = qadd8(W(c), B(c)); //B uint8_t b = B(c);
uint8_t w = W(c);
buffer[pos++] = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
buffer[pos++] = scale8(qadd8(w, g), strip.getBrightness()); //G
buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B
} }
wsc->binary(wsBuf); wsc->binary(wsBuf);

View File

@ -355,7 +355,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("CB"),strip.cctBlending); sappend('v',SET_F("CB"),strip.cctBlending);
sappend('v',SET_F("FR"),strip.getTargetFps()); sappend('v',SET_F("FR"),strip.getTargetFps());
sappend('v',SET_F("AW"),Bus::getGlobalAWMode()); sappend('v',SET_F("AW"),Bus::getGlobalAWMode());
sappend('c',SET_F("LD"),strip.useLedsArray); sappend('c',SET_F("LD"),useGlobalLedBuffer);
for (uint8_t s=0; s < busses.getNumBusses(); s++) { for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s); Bus* bus = busses.getBus(s);
@ -382,7 +382,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',lt,bus->getType()); sappend('v',lt,bus->getType());
sappend('v',co,bus->getColorOrder() & 0x0F); sappend('v',co,bus->getColorOrder() & 0x0F);
sappend('v',ls,bus->getStart()); sappend('v',ls,bus->getStart());
sappend('c',cv,bus->reversed); sappend('c',cv,bus->isReversed());
sappend('v',sl,bus->skippedLeds()); sappend('v',sl,bus->skippedLeds());
sappend('c',rf,bus->isOffRefreshRequired()); sappend('c',rf,bus->isOffRefreshRequired());
sappend('v',aw,bus->getAutoWhiteMode()); sappend('v',aw,bus->getAutoWhiteMode());