Merge remote-tracking branch 'upstream/mdev' into mdev

This commit is contained in:
Brandon502
2024-10-02 17:43:15 -04:00
36 changed files with 1495 additions and 607 deletions

View File

@@ -8,15 +8,15 @@ jobs:
name: Gather Environments name: Gather Environments
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Cache pip - name: Cache pip
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- uses: actions/setup-python@v4 - uses: actions/setup-python@v5
with: with:
python-version: '3.9' python-version: '3.9'
- name: Install PlatformIO - name: Install PlatformIO
@@ -38,21 +38,24 @@ jobs:
matrix: matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Cache pip - name: Cache pip
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Cache PlatformIO - name: Cache PlatformIO
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/.platformio path: ~/.platformio
key: ${{ runner.os }}-${{ matrix.environment}}-${{ hashFiles('platformio.ini') }} key: ${{ runner.os }}-${{ matrix.environment}}-${{ hashFiles('platformio.ini') }}
restore-keys: |
${{ runner.os }}-${{ matrix.environment}}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.9' python-version: '3.9'
- name: Install PlatformIO - name: Install PlatformIO
@@ -61,16 +64,16 @@ jobs:
env: env:
WLED_RELEASE: True WLED_RELEASE: True
run: pio run -e ${{ matrix.environment }} run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v2 - name: Rename Bin
run: mv -v .pio/build/${{ matrix.environment }}/firmware.bin firmware-${{ matrix.environment }}.bin
- uses: actions/upload-artifact@v4
with: with:
name: firmware-${{ matrix.environment }} name: firmware-${{ matrix.environment }}
path: | path: firmware-${{ matrix.environment }}.bin
build_output/firmware/*.bin - uses: actions/upload-artifact@v4
build_output/firmware/*.gz
- uses: actions/upload-artifact@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: firmware-release name: firmware-release-${{ matrix.environment }}
path: build_output/release/*.bin path: build_output/release/*.bin
release: release:
name: Create Release name: Create Release
@@ -78,9 +81,11 @@ jobs:
needs: [get_default_envs, build] needs: [get_default_envs, build]
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
steps: steps:
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v4
with: with:
name: firmware-release name: firmware-release-*
- name: List Artifacts
run: find ./
- name: Create draft release - name: Create draft release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:

47
boards/lilygo-t7-s3.json Normal file
View File

@@ -0,0 +1,47 @@
{
"build": {
"arduino":{
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi",
"partitions": "default_16MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_TTGO_T7_S3",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0X303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi",
"bluetooth"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "LILYGO T3-S3",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"url": "https://www.aliexpress.us/item/3256804591247074.html",
"vendor": "LILYGO"
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.1-b32.41", "version": "0.14.1-b32.41.dev",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wled", "name": "wled",
"version": "0.14.1-b32.41", "version": "0.14.1-b32.41.dev",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
"clean-css": "^4.2.3", "clean-css": "^4.2.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.1-b32.41", "version": "0.14.1-b32.41.dev",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {

View File

@@ -72,6 +72,7 @@ default_envs =
esp32_pico_4MB_M esp32_pico_4MB_M
esp32_4MB_PSRAM_S esp32_4MB_PSRAM_S
; esp32_4MB_PSRAM_REV3_S ;; experimental, optimized for WROVER-E with "revision3" chip ; esp32_4MB_PSRAM_REV3_S ;; experimental, optimized for WROVER-E with "revision3" chip
esp32S3_4MB_S
esp32S3_8MB_S ;; experimental, optimized for speed esp32S3_8MB_S ;; experimental, optimized for speed
esp32S3_8MB_M esp32S3_8MB_M
esp32S3_4MB_PSRAM_S ;; for lolin s3 mini, S3 zero, S3 super mini - optimized for speed esp32S3_4MB_PSRAM_S ;; for lolin s3 mini, S3 zero, S3 super mini - optimized for speed
@@ -187,6 +188,7 @@ build_flags =
-D DECODE_SONY=true -D DECODE_SONY=true
-D DECODE_SAMSUNG=true -D DECODE_SAMSUNG=true
-D DECODE_LG=true -D DECODE_LG=true
-D FASTLED_NO_FASTLED
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this breaks framework code on ESP32-C3 and ESP32-S2 ;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this breaks framework code on ESP32-C3 and ESP32-S2
-DWLED_USE_MY_CONFIG -DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT ; -D USERMOD_SENSORSTOMQTT
@@ -240,11 +242,13 @@ upload_speed = 115200
lib_compat_mode = strict lib_compat_mode = strict
lib_deps = lib_deps =
fastled/FastLED @ 3.6.0 fastled/FastLED @ 3.6.0
;; fastled/FastLED @ 3.7.1
;; https://github.com/softhack007/FastLED.git#ESP32-C6 ;; patched version needed for -C6
IRremoteESP8266 @ 2.8.2 IRremoteESP8266 @ 2.8.2
;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections ;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections
;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 ;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
;; https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit ;; https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit
https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1 ;; newer with bugfixes and stability improvements https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1 ;; newer with bugfixes and stability improvements
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI #TFT_eSPI
#For compatible OLED display uncomment following #For compatible OLED display uncomment following
@@ -293,6 +297,37 @@ lib_deps =
makuna/NeoPixelBus @ 2.7.5 makuna/NeoPixelBus @ 2.7.5
${env.lib_deps} ${env.lib_deps}
;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48
build_flags_compat =
-DESP8266
-DFP_IN_IROM
;;-Wno-deprecated-declarations
-Wno-misleading-indentation
;;-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
-DVTABLES_IN_FLASH
-DMIMETYPE_MINIMAL
-DWLED_SAVE_IRAM ;; needed to prevent linker error
;; this platform version was used for WLED 0.14.0
platform_compat = espressif8266@4.2.0
platform_packages_compat =
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0
;; experimental - for using older NeoPixelBus 2.7.9
lib_deps_compat =
ESPAsyncTCP @ 1.2.2
ESPAsyncUDP
ESP8266PWM
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.9
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
[esp32] [esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip #platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
platform = espressif32@3.5.0 platform = espressif32@3.5.0
@@ -1035,8 +1070,9 @@ HUB75_build_flags =
-D NO_GFX ;; Disable the use of Adafruit_GFX by the HUB75 driver -D NO_GFX ;; Disable the use of Adafruit_GFX by the HUB75 driver
-D NO_FAST_FUNCTIONS ;; If you are not using AdafruitGFX than you probably do not need this either, save memory/code size -D NO_FAST_FUNCTIONS ;; If you are not using AdafruitGFX than you probably do not need this either, save memory/code size
-D NO_CIE1931 ;; Do not use LED brightness compensation described in CIE 1931. We use FastLED dimming already -D NO_CIE1931 ;; Do not use LED brightness compensation described in CIE 1931. We use FastLED dimming already
;; HUB75_lib_deps = https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.10 ;; does not work any more -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips
HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#1e4c80a26454aca7b8129bd5a966b0af329d2703 ;; something strange is going on here ... -D WLEDMM_SLOWPATH ;; WLEDMM: do not use I2S for driving ws2812 LEDs (HUB75 driver needs I2S#1)
HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a9 ;; S3_LCD_DIV_NUM fix
HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes) HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes)
NetDebug_build_flags = NetDebug_build_flags =
@@ -1599,6 +1635,67 @@ lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps}
lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation
; RAM: [=== ] 28.1% (used 91960 bytes from 327680 bytes) ; RAM: [=== ] 28.1% (used 91960 bytes from 327680 bytes)
; Flash: [==========] 97.8% (used 1537777 bytes from 1572864 bytes) ; Flash: [==========] 97.8% (used 1537777 bytes from 1572864 bytes)
;; softhack007: my favourite HUB75 buildenv - fastest possible
[env:esp32_4MB_V4_HUB75_forum]
extends = esp32_4MB_V4_S_base
platform_packages = ${esp32_4MB_V4_S_base.platform_packages}
;; toolchain-xtensa-esp32@~11.2 ;; for trying out newer gcc @11.2.0+2022r1 ;; 25fps -> 30fps;; FastLED 3.7.1 required !!
build_unflags = ${esp32_4MB_V4_S_base.build_unflags}
${Speed_Flags.build_unflags} ;; to override -Os
;; -D WLED_ENABLE_DMX_INPUT
-DARDUINO_EVENT_RUNNING_CORE=1
;;-DCORE_DEBUG_LEVEL=0
;; -D NDEBUG
-D NO_CIE1931 ;; Do use LED brightness compensation described in CIE 1931
build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags}
;; -Wsuggest-attribute=const -Wsuggest-attribute=pure
;; -Wmissing-noreturn -Wmissing-return
;; -Wall -Wextra
-Wno-unused-value -Wno-format -Wno-type-limits
-D WLED_RELEASE_NAME=esp32_4MB_V4_HUB75
${Speed_Flags.build_flags} ;; -O2 -> optimize for speed instead of size
;; -D DEBUG
-D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET
-D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup
-D WLEDMM_FASTPATH ; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions.
-D WLED_DISABLE_LOXONE
-D WLED_DISABLE_ALEXA
-D WLED_DISABLE_HUESYNC
-D WLED_DISABLE_MQTT
-D WLED_DISABLE_INFRARED
-D WLED_DISABLE_ADALIGHT
;; -D WLED_DEBUG
;; -D SR_DEBUG
-D WLED_BOOTUPDELAY=350
-D WLED_ENABLE_HUB75MATRIX -DESP32_FORUM_PINOUT
;; -D HUB75_NO_DOUBLEBUFFER ;; -D PIXEL_COLOUR_DEPTH_BITS=12
${common_mm.animartrix_build_flags}
;;-DARDUINO_EVENT_RUNNING_CORE=0 ;; assign Wifi to core0, to have more CPU on core#1 (arduino loop)
;;-DARDUINO_RUNNING_CORE=1 ;; should be default, but does not hurt
-DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - seems to move more buffers into PSRAM
;; -D CONFIG_ESP32_REV_MIN=3 ;; disables PSRAM bug workarounds in the core, reducing the code size and improving overall performance.
-D JSON_BUFFER_SIZE=18432 -D MIN_HEAP_SIZE=6144
-D MAX_SEGMENT_DATA=40960 ;; default 32767
-D WLEDMM_SAVE_FLASH
-D WLED_DISABLE_BROWNOUT_DET
;;-DCORE_DEBUG_LEVEL=1
;;-D WLED_DISABLE_PARTICLESYSTEM1D
;;-D WLED_DISABLE_PARTICLESYSTEM2D
;;-D WLED_DISABLE_PARTICLESYSTEM_BUFFER
lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps}
${common_mm.animartrix_lib_deps}
lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation
;; ${common_mm.DMXin_lib_ignore}
board_build.partitions = ${esp32.big_partitions}
board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz)
board_build.flash_mode = qio ; (dio = dual i/o; more compatible than qio = quad i/o)
; compiled with ESP-IDF 4.4.1 ; compiled with ESP-IDF 4.4.1
[env:esp32_4MB_V4_M] [env:esp32_4MB_V4_M]
extends = esp32_4MB_V4_M_base extends = esp32_4MB_V4_M_base
@@ -1785,6 +1882,32 @@ lib_deps = ${esp32_4MB_V4_M_base.esp32_lib_deps}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# esp32-S3 environments # esp32-S3 environments
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
[env:esp32S3_4MB_S] ;; Use for HD-WF2
extends = esp32_4MB_V4_M_base
board = esp32-s3-devkitc-1
build_unflags =
-D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?)
-D USE_ALT_DISPLAY ;; four line display seems to have problems with I2C - it hangs during usermod setup
-D USERMOD_FOUR_LINE_DISPLAY ;; see above
-D USERMOD_ROTARY_ENCODER_UI ;; see above
-D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX)
build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation
${common_mm.build_flags_S}
${common_mm.HUB75_build_flags}
${common_mm.animartrix_build_flags}
-D WLED_RELEASE_NAME=esp32S3_4MB_S
-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
${common_mm.build_disable_sync_interfaces}
-D WLEDMM_FASTPATH ; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions.
; -D WLED_DEBUG
; -D SR_DEBUG
lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S}
${common_mm.HUB75_lib_deps}
${common_mm.animartrix_lib_deps}
[env:esp32S3_8MB_M] [env:esp32S3_8MB_M]
@@ -1799,6 +1922,8 @@ build_unflags =
build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation
${common_mm.build_flags_S} ${common_mm.build_flags_M} ${common_mm.build_flags_S} ${common_mm.build_flags_M}
${common_mm.HUB75_build_flags}
${common_mm.animartrix_build_flags}
; -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON -D WLED_USE_PSRAM ;; un-comment in case your board supports PSRAM ; -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON -D WLED_USE_PSRAM ;; un-comment in case your board supports PSRAM
-D WLED_RELEASE_NAME=esp32S3_8MB_M -D WLED_RELEASE_NAME=esp32S3_8MB_M
-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
@@ -1826,6 +1951,9 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
; -D SR_DEBUG ; -D SR_DEBUG
; -D MIC_LOGGER ; -D MIC_LOGGER
lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M}
${common_mm.HUB75_lib_deps}
${common_mm.animartrix_lib_deps}
;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation
board_build.partitions = tools/WLED_ESP32_8MB.csv board_build.partitions = tools/WLED_ESP32_8MB.csv
board_build.flash_mode = qio board_build.flash_mode = qio
@@ -1844,6 +1972,7 @@ board_build.flash_mode = qio ;; use "dio" if your board gets unstable with "qio"
build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" S3 buildenv build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" S3 buildenv
build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation
${common_mm.build_flags_S} ${common_mm.build_flags_M} ${common_mm.build_flags_S} ${common_mm.build_flags_M}
${common_mm.HUB75_build_flags}
-DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM ;; your board supports PSRAM -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_RELEASE_NAME=esp32S3_8MB_PSRAM_M -D WLED_RELEASE_NAME=esp32S3_8MB_PSRAM_M
-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
@@ -1868,10 +1997,12 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
; -D SR_DEBUG ; -D SR_DEBUG
; -D MIC_LOGGER ; -D MIC_LOGGER
lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M}
${common_mm.HUB75_lib_deps}
;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation
board_build.partitions = tools/WLED_ESP32_8MB.csv board_build.partitions = tools/WLED_ESP32_8MB.csv
; RAM: [==== ] 36.3% (used 118828 bytes from 327680 bytes) ; RAM: [== ] 21.1% (used 69156 bytes from 327680 bytes)
; Flash: [======= ] 70.7% (used 1483465 bytes from 2097152 bytes) ; Flash: [======== ] 75.9% (used 1591817 bytes from 2097152 bytes)
;; MM for ESP32-S3 boards - FASTPATH + optimize for speed ;; MM for ESP32-S3 boards - FASTPATH + optimize for speed
[env:esp32S3_8MB_S] [env:esp32S3_8MB_S]
@@ -2106,7 +2237,6 @@ monitor_filters = esp32_exception_decoder
; RAM: [== ] 21.8% (used 71304 bytes from 327680 bytes) ; RAM: [== ] 21.8% (used 71304 bytes from 327680 bytes)
; Flash: [======== ] 84.0% (used 1596970 bytes from 1900544 bytes) ; Flash: [======== ] 84.0% (used 1596970 bytes from 1900544 bytes)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# esp32-C3 environments # esp32-C3 environments
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -2394,12 +2524,17 @@ board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for
;https://www.athom.tech/blank-1/wled-esp32-music-addressable-led-strip-controller ;https://www.athom.tech/blank-1/wled-esp32-music-addressable-led-strip-controller
[env:athom_music_esp32_4MB_M] [env:athom_music_esp32_4MB_M]
extends = esp32_4MB_M_base extends = esp32_4MB_M_base
build_unflags = ${common.build_unflags}
-D USERMOD_ARTIFX ;; disabled to save some program space in flash
-D USERMOD_DALLASTEMPERATURE ;; disabled - flash space is too tight for this
-D USERMOD_ROTARY_ENCODER_UI ;; see above
build_flags = ${esp32_4MB_M_base.build_flags} build_flags = ${esp32_4MB_M_base.build_flags}
${Athom_PDMmic.build_flags} ${Athom_PDMmic.build_flags}
-D WLED_AP_SSID_UNIQUE -D WLED_AP_SSID_UNIQUE
-D WLED_RELEASE_NAME=athom_music_esp32_4MB_M -D WLED_RELEASE_NAME=athom_music_esp32_4MB_M
-D ABL_MILLIAMPS_DEFAULT=14500 ; max 15A -D ABL_MILLIAMPS_DEFAULT=14500 ; max 15A
-D WLED_DISABLE_MQTT -D WLED_DISABLE_LOXONE ; -D WLED_DISABLE_MQTT
-D WLED_DISABLE_LOXONE
-D WLED_DISABLE_ADALIGHT ;to get 4ld working -D WLED_DISABLE_ADALIGHT ;to get 4ld working
-D BTNPIN=0 -D RLYPIN=2 -D IRPIN=25 -D IRTYPE=9 -D LEDPIN=18 -D BTNPIN=0 -D RLYPIN=2 -D IRPIN=25 -D IRTYPE=9 -D LEDPIN=18
-D AUDIOPIN=-1 -D AUDIOPIN=-1
@@ -2411,8 +2546,9 @@ build_flags = ${esp32_4MB_M_base.build_flags}
; -D PIR_SENSOR_PIN=-1 ; -D PIR_SENSOR_PIN=-1
; -D PWM_PIN=-1 ; -D PWM_PIN=-1
; -D WLED_USE_MY_CONFIG ; -D WLED_USE_MY_CONFIG
; RAM: [=== ] 25.9% (used 84948 bytes from 327680 bytes) -D WLEDMM_SAVE_FLASH
; Flash: [==========] 95.9% (used 1509113 bytes from 1572864 bytes) ; RAM: [=== ] 26.3% (used 86204 bytes from 327680 bytes)
; Flash: [========= ] 93.6% (used 1471681 bytes from 1572864 bytes)
;https://shop.myhome-control.de/Elektronik/ ;https://shop.myhome-control.de/Elektronik/
[env:abc_wled_controller_v43_S] [env:abc_wled_controller_v43_S]
@@ -2532,6 +2668,8 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D LOLIN_WIFI_FIX ;; try this in case Wifi does not work -D LOLIN_WIFI_FIX ;; try this in case Wifi does not work
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-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
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
; -DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - seems to move more buffers into PSRAM
${common_mm.HUB75_build_flags} ${common_mm.HUB75_build_flags}
-D DEFAULT_LED_TYPE=101 -D DEFAULT_LED_TYPE=101
lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ;; ;; do not include ${esp32.lib_depsV4} !!!! lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ;; ;; do not include ${esp32.lib_depsV4} !!!!

View File

@@ -285,7 +285,7 @@ static volatile float micReal_max2 = 0.0f; // MicIn data max afte
// some prototypes, to ensure consistent interfaces // some prototypes, to ensure consistent interfaces
static float mapf(float x, float in_min, float in_max, float out_min, float out_max); // map function for float static float mapf(float x, float in_min, float in_max, float out_min, float out_max); // map function for float
static float fftAddAvg(int from, int to); // average of several FFT result bins static float fftAddAvg(int from, int to); // average of several FFT result bins
void FFTcode(void * parameter); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results void FFTcode(void * parameter) __attribute__((noreturn)); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results
static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass) static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass)
static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels, bool i2sFastpath); // post-processing and post-amp of GEQ channels static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels, bool i2sFastpath); // post-processing and post-amp of GEQ channels
@@ -2352,6 +2352,11 @@ class AudioReactive : public Usermod {
#endif #endif
} }
#if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH)
void loop2(void) {
loop();
}
#endif
bool getUMData(um_data_t **data) bool getUMData(um_data_t **data)
{ {

View File

@@ -93,7 +93,7 @@
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case. * if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
*/ */
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 6)) // fixed in IDF 4.4.5, however arduino-esp32 2.0.14 did an "I2S rollback" to 4.4.4 #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 8)) // should be fixed in IDF 4.4.5, however arduino-esp32 2.0.14 - 2.0.17 did an "I2S rollback" to 4.4.4
// espressif bug: only_left has no sound, left and right are swapped // espressif bug: only_left has no sound, left and right are swapped
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138) // https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918) // https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)

View File

@@ -303,6 +303,7 @@ class AutoPlaylistUsermod : public Usermod {
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG #ifdef USERMOD_AUTO_PLAYLIST_DEBUG
USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset);
#endif #endif
suspendPlaylist();
functionality_enabled = false; functionality_enabled = false;
} else if (currentPlaylist == musicPlaylist) { } else if (currentPlaylist == musicPlaylist) {
#ifdef USERMOD_AUTO_PLAYLIST_DEBUG #ifdef USERMOD_AUTO_PLAYLIST_DEBUG

View File

@@ -37,6 +37,9 @@
#define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16)) #define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16))
// WLEDMM replace abs8 by abs, as abs8 does not work for numbers >127
#define abs8(x) abs(x)
// effect utility functions // effect utility functions
static uint8_t sin_gap(uint16_t in) { static uint8_t sin_gap(uint16_t in) {
if (in & 0x100) return 0; if (in & 0x100) return 0;
@@ -98,6 +101,14 @@ static long map2(long x, long in_min, long in_max, long out_min, long out_max)
} }
static um_data_t* getAudioData() {
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
return um_data;
}
// effect functions // effect functions
/* /*
@@ -1994,11 +2005,7 @@ uint16_t mode_partyjerk() {
* step: pos * step: pos
*/ */
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
SEGENV.aux0++; SEGENV.aux0++;
@@ -2148,7 +2155,7 @@ uint16_t mode_fire_2012() {
// Step 4. Map from heat cells to LED colors // Step 4. Map from heat cells to LED colors
for (int j = 0; j < SEGLEN; j++) { for (int j = 0; j < SEGLEN; j++) {
SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j],byte(240)), 255, NOBLEND)); SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j], byte(240)), 255, NOBLEND));
} }
} }
}; };
@@ -2156,14 +2163,19 @@ uint16_t mode_fire_2012() {
for (int stripNr=0; stripNr<strips; stripNr++) for (int stripNr=0; stripNr<strips; stripNr++)
virtualStrip::runStrip(stripNr, &heat[stripNr * SEGLEN], it); virtualStrip::runStrip(stripNr, &heat[stripNr * SEGLEN], it);
if (SEGMENT.is2D()) SEGMENT.blur(32); if (SEGMENT.is2D()) {
uint8_t blurAmount = SEGMENT.custom2 >> 2;
if (blurAmount > 48) blurAmount += blurAmount-48; // extra blur when slider > 192 (bush burn)
if (blurAmount < 16) SEGMENT.blurCols(SEGMENT.custom2 >> 1); // no side-burn when slider < 64 (faster)
else SEGMENT.blur(blurAmount);
}
if (it != SEGENV.step) if (it != SEGENV.step)
SEGENV.step = it; SEGENV.step = it;
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1.5d;sx=64,ix=160,m12=1"; // bars WLEDMM 1.5d, static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,2D Blur,Boost;;!;1.5d;sx=64,ix=160,c2=128,m12=1"; // bars WLEDMM 1.5d,
// ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb
@@ -4922,17 +4934,18 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
} }
SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
unsigned long t = strip.now/128; // timebase const unsigned long ratio = 128; // rotation speed
unsigned long t = strip.now; // timebase
// outer stars // outer stars
for (size_t i = 0; i < 8; i++) { for (unsigned 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)/ratio);
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)/ratio);
SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 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)/ratio);
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)/ratio);
SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255));
} }
// central white dot // central white dot
@@ -4969,8 +4982,8 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
SEGMENT.fadeToBlackBy(40); SEGMENT.fadeToBlackBy(40);
for (size_t i = 0; i < numLines; i++) { for (size_t i = 0; i < numLines; i++) {
byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1)); byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1));
byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1)); byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (rows - 1));
byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24); byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24);
byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64); byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64);
CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND);
@@ -5054,10 +5067,12 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
if ((i + ms / 8) & 3) { if ((i + ms / 8) & 3) {
// draw a gradient line between x and x1 // draw a gradient line between x and x1
x = x / 2; x1 = x1 / 2; x = x / 2; x1 = x1 / 2;
uint8_t steps = abs8(x - x1) + 1; unsigned steps = abs8(x - x1) + 1;
bool positive = (x1 >= x); // direction of drawing
for (size_t k = 1; k <= steps; k++) { for (size_t k = 1; k <= steps; k++) {
uint8_t rate = k * 255 / steps; unsigned rate = k * 255 / steps;
uint8_t dx = lerp8by8(x, x1, rate); //unsigned dx = lerp8by8(x, x1, rate);
unsigned dx = positive? (x + k-1) : (x - k+1); // behaves the same as "lerp8by8" but does not create holes
//SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate)); //SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate));
SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look
SEGMENT.fadePixelColorXY(dx, i, rate); SEGMENT.fadePixelColorXY(dx, i, rate);
@@ -5364,7 +5379,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
SEGENV.step = strip.now; SEGENV.step = strip.now;
return FRAMETIME; return FRAMETIME;
} // mode_2Dgameoflife() } // mode_2Dgameoflife()
static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,Blur ☾,,,All Colors ☾,Overlay BG ☾,Wrap ☾,;!,!;!;2;sx=56,ix=2,c1=128,o1=0,o2=0,o3=1"; static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,Blur ☾,,,All Colors ☾,Overlay BG ☾,Wrap ☾;!,!;!;2;sx=56,ix=2,c1=128,o1=0,o2=0,o3=1";
///////////////////////// /////////////////////////
// 2D SnowFall // // 2D SnowFall //
@@ -5637,7 +5652,10 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
float ylocn = float(cos8(phase/2 + i*2)) / 255.0f; float ylocn = float(cos8(phase/2 + i*2)) / 255.0f;
//SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing //SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing
unsigned palIndex = (256*ylocn) + phase/2 + (i* SEGMENT.speed)/64; unsigned palIndex = (256*ylocn) + phase/2 + (i* SEGMENT.speed)/64;
SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing - color follows rotation //SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing - color follows rotation
// WLEDMM wu_pixel is 50% faster, and still lokks better
SEGMENT.wu_pixel(uint32_t(xlocn * (cols <<8)), uint32_t(ylocn * (rows <<8)),
CRGB(SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0)));
} }
} else } else
for (int i=0; i < 256; i ++) { for (int i=0; i < 256; i ++) {
@@ -6381,6 +6399,10 @@ uint16_t mode_2Dfloatingblobs(void) {
} }
SEGMENT.fadeToBlackBy(20); SEGMENT.fadeToBlackBy(20);
bool drawAA = (SEGMENT.custom1 > 0) && (SEGMENT.custom1 < 6); //WLEDMM
const uint16_t minDim = min(cols, rows); // WLEDMM use smaller dimension to find good blob size
float max_grow = min(minDim/4.f,2.f);
if (minDim>=24) max_grow =(minDim/8.0f); // WLEDMM allow bigger blobs
// Bounce balls around // Bounce balls around
for (size_t i = 0; i < Amount; i++) { for (size_t i = 0; i < Amount; i++) {
@@ -6389,18 +6411,18 @@ uint16_t mode_2Dfloatingblobs(void) {
if (blob->grow[i]) { if (blob->grow[i]) {
// enlarge radius until it is >= 4 // enlarge radius until it is >= 4
blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
if (blob->r[i] >= min(cols/4.f,2.f)) { if (blob->r[i] >= max_grow) {
blob->grow[i] = false; blob->grow[i] = false;
} }
} else { } else {
// reduce radius until it is < 1 // reduce radius until it is < 1
blob->r[i] -= (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; blob->r[i] -= (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
if (blob->r[i] < 1.f) { if (blob->r[i] < 0.8f) {
blob->grow[i] = true; blob->grow[i] = true;
} }
} }
uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0);
if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c); if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c, drawAA);
else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c); else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c);
// move x // move x
if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f));
@@ -6468,7 +6490,7 @@ uint16_t mode_2Dscrollingtext(void) {
if (SEGMENT.name) for (size_t i=0,j=0; i<maxLen; i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; if (SEGMENT.name) for (size_t i=0,j=0; i<maxLen; i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
const bool zero = strchr(text, '0') != nullptr; const bool zero = strchr(text, '0') != nullptr;
if (!strlen(text) || !strncmp_P(text,PSTR("#F"),2) || !strncmp_P(text,PSTR("#P"),2) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HH"),3) || !strncmp_P(text,PSTR("#MM"),3)) { // fallback if empty segment name: display date and time if (!strlen(text) || !strncmp_P(text,PSTR("#F"),2) || !strncmp_P(text,PSTR("#P"),2) || !strncmp_P(text,PSTR("#A"),2) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HH"),3) || !strncmp_P(text,PSTR("#MM"),3)) { // fallback if empty segment name: display date and time
char sec[5]= {'\0'}; char sec[5]= {'\0'};
byte AmPmHour = hour(localTime); byte AmPmHour = hour(localTime);
boolean isitAM = true; boolean isitAM = true;
@@ -6501,6 +6523,7 @@ uint16_t mode_2Dscrollingtext(void) {
SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1)); SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1));
} }
} }
bool drawShadow = (SEGMENT.check2) && (SEGMENT.custom1 == 0);
for (int i = 0; i < numberOfLetters; i++) { for (int i = 0; i < numberOfLetters; i++) {
if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen
uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
@@ -6509,7 +6532,7 @@ uint16_t mode_2Dscrollingtext(void) {
col1 = SEGCOLOR(0); col1 = SEGCOLOR(0);
col2 = SEGCOLOR(2); col2 = SEGCOLOR(2);
} }
SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2); SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2, drawShadow);
} }
return FRAMETIME; return FRAMETIME;
@@ -6620,11 +6643,7 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Ripple* ripples = reinterpret_cast<Ripple*>(SEGENV.data); Ripple* ripples = reinterpret_cast<Ripple*>(SEGENV.data);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
#ifdef ESP32 #ifdef ESP32
float FFT_MajorPeak = *(float*) um_data->u_data[4]; float FFT_MajorPeak = *(float*) um_data->u_data[4];
@@ -6720,11 +6739,7 @@ uint16_t mode_2DSwirl(void) {
uint8_t nj = (cols - 1) - j; uint8_t nj = (cols - 1) - j;
uint16_t ms = strip.now; uint16_t ms = strip.now;
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg??? float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg???
int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; int16_t volumeRaw = *(int16_t*) um_data->u_data[1];
@@ -6757,11 +6772,7 @@ uint16_t mode_2DWaverly(void) {
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
float soundPressure = *(float*) um_data->u_data[9]; float soundPressure = *(float*) um_data->u_data[9];
float agcSensitivity= *(float*) um_data->u_data[10]; float agcSensitivity= *(float*) um_data->u_data[10];
@@ -6773,20 +6784,26 @@ uint16_t mode_2DWaverly(void) {
long t = strip.now / 2; long t = strip.now / 2;
for (int i = 0; i < cols; i++) { for (int i = 0; i < cols; i++) {
uint16_t thisVal = volumeSmth*SEGMENT.intensity/64 * inoise8(i * 45 , t , t)/64; // WLEDMM back to SR code //uint16_t thisVal = volumeSmth*SEGMENT.intensity/64 * inoise8(i * 45 , t , t)/64; // WLEDMM back to SR code
uint16_t thisMax = map(thisVal, 0, 512, 0, rows); unsigned thisVal = unsigned(volumeSmth*SEGMENT.intensity) * inoise8(i * 45 , t , t) / (64*64); // WLEDMM same result but more accurate
for (int j = 0; j < thisMax; j++) { //int thisMax = map(thisVal, 0, 512, 0, rows);
int thisMax = (thisVal * rows) / 512; // WLEDMM same result, just faster
int thisMax2 = min(int(rows), thisMax); // WLEDMM limit height to visible are
for (int j = 0; j < thisMax2; j++) {
//int jmap = map(j, 0, thisMax, 250, 0);
int jmap = 250 - ((j * 250) / thisMax); // WLEDMM same result, just faster
if (!SEGENV.check1) if (!SEGENV.check1)
SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, jmap, 255, LINEARBLEND));
SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, jmap, 255, LINEARBLEND));
} }
} }
SEGMENT.blur(16); SEGMENT.blur(16);
return FRAMETIME; return FRAMETIME;
} // mode_2DWaverly() } // mode_2DWaverly()
static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly ☾@Amplification,Sensitivity,,,,No Clouds,Sound Pressure,AGC debug;;!;2v;ix=64,si=0"; // Beatsin static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly ☾@Fade Rate,Amplification,,,,No Clouds,Sound Pressure,AGC debug;;!;2v;ix=64,si=0"; // Beatsin
#endif // WLED_DISABLE_2D #endif // WLED_DISABLE_2D
@@ -6809,11 +6826,7 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
//SEGMENT.fade_out(240); //SEGMENT.fade_out(240);
@@ -6861,11 +6874,7 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
// printUmData(); // printUmData();
@@ -6912,11 +6921,7 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data); Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
// int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; //WLEDMM: this variable not used here // int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; //WLEDMM: this variable not used here
float soundPressure = *(float*) um_data->u_data[9]; float soundPressure = *(float*) um_data->u_data[9];
@@ -6989,11 +6994,7 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter ☾@Rate of f
// * JUGGLES // // * JUGGLES //
////////////////////// //////////////////////
uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor()
@@ -7016,11 +7017,7 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;
uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. With some enhancements by @softhack007 uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. With some enhancements by @softhack007
// 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
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
float soundPressure = *(float*) um_data->u_data[9]; float soundPressure = *(float*) um_data->u_data[9];
@@ -7072,11 +7069,7 @@ static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix ☾@!,Brightness,
uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
// Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1. // Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1.
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
@@ -7115,11 +7108,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline.
CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
if (SEGENV.call == 0) SEGMENT.fill(BLACK); if (SEGENV.call == 0) SEGMENT.fill(BLACK);
@@ -7143,11 +7132,7 @@ static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;01v;m12=2
/////////////////////// ///////////////////////
uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline.
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor()
@@ -7185,11 +7170,7 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
} }
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 16; uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 16;
@@ -7223,11 +7204,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
if (!SEGENV.allocateData(sizeof(plasphase))) return mode_static(); //allocation failed if (!SEGENV.allocateData(sizeof(plasphase))) return mode_static(); //allocation failed
Plasphase* plasmoip = reinterpret_cast<Plasphase*>(SEGENV.data); Plasphase* plasmoip = reinterpret_cast<Plasphase*>(SEGENV.data);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
@@ -7266,11 +7243,7 @@ uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
uint8_t fadeVal = map2(SEGMENT.speed,0,255, 224, 254); uint8_t fadeVal = map2(SEGMENT.speed,0,255, 224, 254);
uint16_t pos = random16(SEGLEN); // Set a random starting position. uint16_t pos = random16(SEGLEN); // Set a random starting position.
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
uint8_t *binNum = (uint8_t*)um_data->u_data[7]; uint8_t *binNum = (uint8_t*)um_data->u_data[7];
@@ -7318,11 +7291,7 @@ uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline.
} }
SEGMENT.fade_out(fadeVal); SEGMENT.fade_out(fadeVal);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
if (volumeRaw > 1) { if (volumeRaw > 1) {
@@ -7347,10 +7316,7 @@ uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed
uint8_t *myVals = reinterpret_cast<uint8_t*>(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low. uint8_t *myVals = reinterpret_cast<uint8_t*>(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0]; float volumeSmth = *(float*) um_data->u_data[0];
if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor()
@@ -7380,11 +7346,7 @@ static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels
uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. uint16_t mode_blurz(void) { // Blurz. 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
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
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) {
@@ -7414,11 +7376,7 @@ static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color
uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
// Hint: Looks best with segment brightness set to max (use global brightness to reduce brightness) // Hint: Looks best with segment brightness set to max (use global brightness to reduce brightness)
// 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
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
@@ -7466,11 +7424,7 @@ uint16_t mode_DJLight(void) { // Written by Stefan Petrick, Ad
// No need to prevent from executing on single led strips, only mid will be set (mid = 0) // No need to prevent from executing on single led strips, only mid will be set (mid = 0)
const int mid = SEGLEN / 2; const int mid = SEGLEN / 2;
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
@@ -7536,11 +7490,7 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
// Start frequency = 60 Hz and log10(60) = 1.78 // Start frequency = 60 Hz and log10(60) = 1.78
// End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10 // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*)um_data->u_data[SEGENV.check1 ? 8:4]; // WLEDMM may use FFT_MajorPeakSmth float FFT_MajorPeak = *(float*)um_data->u_data[SEGENV.check1 ? 8:4]; // WLEDMM may use FFT_MajorPeakSmth
float my_magnitude = *(float*)um_data->u_data[5] / 4.0f; float my_magnitude = *(float*)um_data->u_data[5] / 4.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
@@ -7581,11 +7531,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting
/////////////////////// ///////////////////////
uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung.
// No need to prevent from executing on single led strips, we simply change pixel 0 each time and avoid the shift // No need to prevent from executing on single led strips, we simply change pixel 0 each time and avoid the shift
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*)um_data->u_data[4]; float FFT_MajorPeak = *(float*)um_data->u_data[4];
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
@@ -7622,9 +7568,9 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
} }
// shift the pixels one pixel up // shift the pixels one pixel up
SEGMENT.setPixelColor(0, color);
// if SEGLEN equals 1 this loop won't execute // if SEGLEN equals 1 this loop won't execute
for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
SEGMENT.setPixelColor(0, color);
} }
return FRAMETIME; return FRAMETIME;
@@ -7640,11 +7586,7 @@ static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound e
// SEGMENT.speed select faderate // SEGMENT.speed select faderate
// SEGMENT.intensity select colour index // SEGMENT.intensity select colour index
uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*)um_data->u_data[4]; float FFT_MajorPeak = *(float*)um_data->u_data[4];
float my_magnitude = *(float*)um_data->u_data[5] / 16.0f; float my_magnitude = *(float*)um_data->u_data[5] / 16.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
@@ -7687,11 +7629,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta
// Depending on the music stream you have you might find it useful to change the frequency mapping. // Depending on the music stream you have you might find it useful to change the frequency mapping.
uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. With some enhancements by @softhack007 uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. With some enhancements by @softhack007
// As before, this effect can also work on single pixels, we just lose the shifting effect // As before, this effect can also work on single pixels, we just lose the shifting effect
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*)um_data->u_data[4]; float FFT_MajorPeak = *(float*)um_data->u_data[4];
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
@@ -7761,11 +7699,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data); Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*)um_data->u_data[4]; float FFT_MajorPeak = *(float*)um_data->u_data[4];
float volumeSmth = *(float*)um_data->u_data[0]; float volumeSmth = *(float*)um_data->u_data[0];
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)
@@ -7814,11 +7748,7 @@ static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq ☾@Rate of fall,
// ** Noisemove // // ** Noisemove //
////////////////////// //////////////////////
uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0}; uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
@@ -7847,11 +7777,7 @@ static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin
// ** Rocktaves // // ** Rocktaves //
////////////////////// //////////////////////
uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*) um_data->u_data[8]; // WLEDMM use FFT_MajorPeakSmth float FFT_MajorPeak = *(float*) um_data->u_data[8]; // WLEDMM use FFT_MajorPeakSmth
float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
@@ -7895,11 +7821,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
// effect can work on single pixels, we just lose the shifting effect // effect can work on single pixels, we just lose the shifting effect
if (SEGENV.call == 0) SEGMENT.fill(BLACK); if (SEGENV.call == 0) SEGMENT.fill(BLACK);
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
float FFT_MajorPeak = *(float*) um_data->u_data[4]; float FFT_MajorPeak = *(float*) um_data->u_data[4];
uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
@@ -7960,11 +7882,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed
uint16_t *previousBarHeight = reinterpret_cast<uint16_t*>(SEGENV.data); //array of previous bar heights per frequency band uint16_t *previousBarHeight = reinterpret_cast<uint16_t*>(SEGENV.data); //array of previous bar heights per frequency band
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0}; uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
@@ -8071,11 +7989,7 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
bandInc = (NUMB_BANDS / cols); bandInc = (NUMB_BANDS / cols);
} }
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0}; uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
@@ -8156,7 +8070,7 @@ uint16_t mode_2DAkemi(void) {
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {SEGMENT.setUpLeds(); SEGMENT.fill(BLACK);} if (SEGENV.call == 0) {SEGMENT.fill(BLACK);}
uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF;
counter = counter >> 8; counter = counter >> 8;
@@ -8164,10 +8078,7 @@ uint16_t mode_2DAkemi(void) {
const float lightFactor = 0.15f; const float lightFactor = 0.15f;
const float normalFactor = 0.4f; const float normalFactor = 0.4f;
um_data_t *um_data = nullptr; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0}; uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
float base = fftResult[0]/255.0f; float base = fftResult[0]/255.0f;
@@ -8200,11 +8111,12 @@ uint16_t mode_2DAkemi(void) {
//add geq left and right //add geq left and right
if (um_data) { if (um_data) {
for (int x=0; x < cols/8; x++) { int xMax = cols/8;
uint16_t band = x * cols/8; for (int x=0; x < xMax; x++) {
size_t band = map2(x, 0, max(xMax,4), 0, 15); // map 0..cols/8 to 16 GEQ bands
CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
band = constrain(band, 0, 15); band = constrain(band, 0, 15);
uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
for (int y=0; y < barHeight; y++) { for (int y=0; y < barHeight; y++) {
SEGMENT.setPixelColorXY(x, rows/2-y, color); SEGMENT.setPixelColorXY(x, rows/2-y, color);
@@ -8553,11 +8465,7 @@ uint16_t mode_GEQLASER(void) {
uint16_t horizon = map2(SEGMENT.custom1,0,255,rows-1,0); uint16_t horizon = map2(SEGMENT.custom1,0,255,rows-1,0);
uint8_t depth = SEGMENT.custom2; // depth of perspective. 255 = infinite ("laser") uint8_t depth = SEGMENT.custom2; // depth of perspective. 255 = infinite ("laser")
um_data_t *um_data; um_data_t *um_data = getAudioData();
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
uint8_t heights[NUM_GEQ_CHANNELS] = { 0 }; uint8_t heights[NUM_GEQ_CHANNELS] = { 0 };
@@ -8661,6 +8569,84 @@ static const char _data_FX_MODE_GEQLASER[] PROGMEM = "GEQ 3D ☾@Speed,Front Fil
#endif // WLED_DISABLE_2D #endif // WLED_DISABLE_2D
/*
@title MoonModules WLED - Painbrush Effect
@file included in FX.cpp
@repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED
@Authors https://github.com/MoonModules/WLED/commits/mdev/
@Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details)
@license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
This function is part of the MoonModules WLED fork also known as "WLED-MM".
WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with WLED-MM. If not, see <https://www.gnu.org/licenses/>.
*/
///////////////////////
// 2D Paintbrush //
///////////////////////
uint16_t mode_2DPaintbrush() {
// Author: @TroyHacks
// @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (!SEGENV.allocateData(4)) return mode_static(); //allocation failed
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
SEGENV.aux0 = 0;
}
bool phase_chaos = SEGMENT.check3;
bool soft = SEGMENT.check2;
bool color_chaos = SEGMENT.check1;
CRGB color;
byte numLines = map8(SEGMENT.intensity,1,16);
SEGENV.aux0++; // hue
SEGMENT.fadeToBlackBy(map8(SEGENV.custom1,10,128));
um_data_t *um_data = getAudioData();
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
SEGENV.aux1 = phase_chaos?random8():0;
for (size_t i = 0; i < numLines; i++) {
byte bin = map(i,0,numLines,0,15);
byte x1 = beatsin8(max(16,int(SEGMENT.speed))/16*1 + fftResult[0]/16, 0, (cols-1), fftResult[bin], SEGENV.aux1);
byte x2 = beatsin8(max(16,int(SEGMENT.speed))/16*2 + fftResult[0]/16, 0, (cols-1), fftResult[bin], SEGENV.aux1);
byte y1 = beatsin8(max(16,int(SEGMENT.speed))/16*3 + fftResult[0]/16, 0, (rows-1), fftResult[bin], SEGENV.aux1);
byte y2 = beatsin8(max(16,int(SEGMENT.speed))/16*4 + fftResult[0]/16, 0, (rows-1), fftResult[bin], SEGENV.aux1);
int length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
length = map8(fftResult[bin],0,length);
if (length > max(1,int(SEGMENT.custom3))) {
if (color_chaos) {
color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND);
} else {
uint16_t colorIndex = map(i,0,numLines,0,255);
color = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0);
}
SEGMENT.drawLine(x1,y1,x2,y2,color,soft,length);
}
}
return FRAMETIME;
} // mode_2DPaintbrush()
static const char _data_FX_MODE_2DPAINTBRUSH[] PROGMEM = "Paintbrush ☾@Oscillator Offset,# of lines,Fade Rate,,Min Length,Color Chaos,Anti-aliasing,Phase Chaos;!,,Peaks;!;2f;sx=160,ix=255,c1=80,c2=255,c3=0,pal=72,o1=0,o2=1,o3=0";
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// mode data // mode data
@@ -8908,6 +8894,8 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_GEQLASER, &mode_GEQLASER, _data_FX_MODE_GEQLASER); // audio addEffect(FX_MODE_GEQLASER, &mode_GEQLASER, _data_FX_MODE_GEQLASER); // audio
addEffect(FX_MODE_2DPAINTBRUSH, &mode_2DPaintbrush, _data_FX_MODE_2DPAINTBRUSH); // audio
#endif // WLED_DISABLE_2D #endif // WLED_DISABLE_2D
} }

View File

@@ -33,7 +33,7 @@
bool canUseSerial(void); // WLEDMM implemented in wled_serial.cpp bool canUseSerial(void); // WLEDMM implemented in wled_serial.cpp
void strip_wait_until_idle(String whoCalledMe); // WLEDMM implemented in FX_fcn.cpp void strip_wait_until_idle(String whoCalledMe); // WLEDMM implemented in FX_fcn.cpp
bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.cpp bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented in FX_fcn.cpp
#define FASTLED_INTERNAL //remove annoying pragma messages #define FASTLED_INTERNAL //remove annoying pragma messages
#define USE_GET_MILLISECOND_TIMER #define USE_GET_MILLISECOND_TIMER
@@ -87,11 +87,13 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.
#ifndef MAX_NUM_SEGMENTS #ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32 #define MAX_NUM_SEGMENTS 32
#endif #endif
#ifndef MAX_SEGMENT_DATA
#if defined(ARDUINO_ARCH_ESP32S2) #if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA 24576 #define MAX_SEGMENT_DATA 24576
#else #else
#define MAX_SEGMENT_DATA 32767 #define MAX_SEGMENT_DATA 32767
#endif #endif
#endif
#endif #endif
/* How much data bytes each segment should max allocate to leave enough space for other segments, /* How much data bytes each segment should max allocate to leave enough space for other segments,
@@ -348,10 +350,10 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.
#define FX_MODE_STARBURST_AR 192 // WLED-SR audioreactive fireworks starburst #define FX_MODE_STARBURST_AR 192 // WLED-SR audioreactive fireworks starburst
// #define FX_MODE_PALETTE_AR 193 // WLED-SR audioreactive palette // #define FX_MODE_PALETTE_AR 193 // WLED-SR audioreactive palette
#define FX_MODE_FIREWORKS_AR 194 // WLED-SR audioreactive fireworks #define FX_MODE_FIREWORKS_AR 194 // WLED-SR audioreactive fireworks
#define FX_MODE_GEQLASER 195 // WLED-MM GEQ Laser #define FX_MODE_GEQLASER 195 // WLED-MM GEQ Laser
#define FX_MODE_2DSNOWFALL 196 // WLED-MM Snowfall #define FX_MODE_2DPAINTBRUSH 196 // WLED-MM Paintbrush
#define FX_MODE_2DSNOWFALL 197 // WLED-MM Snowfall
#define MODE_COUNT 197 #define MODE_COUNT 198
typedef enum mapping1D2D { typedef enum mapping1D2D {
M12_Pixels = 0, M12_Pixels = 0,
@@ -435,6 +437,21 @@ typedef struct Segment {
static size_t _usedSegmentData; // WLEDMM uint16_t is too small static size_t _usedSegmentData; // WLEDMM uint16_t is too small
void setPixelColorXY_fast(int x, int y,uint32_t c, uint32_t scaled_col, int cols, int rows); // set relative pixel within segment with color - faster, but no error checking!!! void setPixelColorXY_fast(int x, int y,uint32_t c, uint32_t scaled_col, int cols, int rows); // set relative pixel within segment with color - faster, but no error checking!!!
bool _isSimpleSegment = false; // simple = no grouping or spacing - mirror, transpose or reverse allowed
bool _isSuperSimpleSegment = false; // superSimple = no grouping or spacing, no mirror - only transpose or reverse allowed
#ifdef WLEDMM_FASTPATH
// WLEDMM cache some values that won't change while drawing a frame
bool _isValid2D = false;
uint8_t _brightness = 255; // final pixel brightness - including transitions and segment opacity
bool _firstFill = true; // dirty HACK support
uint16_t _2dWidth = 0; // virtualWidth
uint16_t _2dHeight = 0; // virtualHeight
void setPixelColorXY_slow(int x, int y, uint32_t c); // set relative pixel within segment with color - full slow version
#else
void setPixelColorXY_slow(int x, int y, uint32_t c) { setPixelColorXY(x,y,c); } // not FASTPATH - slow is the normal
#endif
// perhaps this should be per segment, not static // perhaps this should be per segment, not static
static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette())
@@ -578,7 +595,7 @@ typedef struct Segment {
void setCCT(uint16_t k); void setCCT(uint16_t k);
void setOpacity(uint8_t o); void setOpacity(uint8_t o);
void setOption(uint8_t n, bool val); void setOption(uint8_t n, bool val);
void setMode(uint8_t fx, bool loadDefaults = false); void setMode(uint8_t fx, bool loadDefaults = false, bool sliderDefaultsOnly = false);
void setPalette(uint8_t pal); void setPalette(uint8_t pal);
uint8_t differs(Segment& b) const; uint8_t differs(Segment& b) const;
void refreshLightCapabilities(void); void refreshLightCapabilities(void);
@@ -588,6 +605,7 @@ typedef struct Segment {
bool allocateData(size_t len); bool allocateData(size_t len);
void deallocateData(void); void deallocateData(void);
void resetIfRequired(void); void resetIfRequired(void);
void startFrame(void); // cache a few values that don't change while an effect is drawing
/** /**
* Flags that before the next effect is calculated, * Flags that before the next effect is calculated,
* the internal segment state should be reset. * the internal segment state should be reset.
@@ -626,7 +644,7 @@ typedef struct Segment {
void setPixelColor(float i, uint32_t c, bool aa = true); void setPixelColor(float i, uint32_t c, bool aa = true);
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
uint32_t __attribute__((pure)) getPixelColor(int i); // WLEDMM attribute added uint32_t __attribute__((pure)) getPixelColor(int i) const; // WLEDMM attribute added
// 1D support functions (some implement 2D as well) // 1D support functions (some implement 2D as well)
void blur(uint8_t, bool smear = false); void blur(uint8_t, bool smear = false);
void fill(uint32_t c); void fill(uint32_t c);
@@ -638,11 +656,22 @@ typedef struct Segment {
inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
void fadePixelColor(uint16_t n, uint8_t fade); void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos); uint8_t get_random_wheel_index(uint8_t pos) const;
uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
uint32_t __attribute__((pure)) color_wheel(uint8_t pos); uint32_t __attribute__((pure)) color_wheel(uint8_t pos);
// 2D Blur: shortcuts for bluring columns or rows only (50% faster than full 2D blur)
inline void blurCols(fract8 blur_amount, bool smear = false) { // blur all columns
const unsigned cols = virtualWidth();
for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear);
}
inline void blurRows(fract8 blur_amount, bool smear = false) { // blur all rows
const unsigned rows = virtualHeight();
for ( unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear);
}
// 2D matrix // 2D matrix
#ifndef WLEDMM_FASTPATH
inline uint16_t virtualWidth() const { // WLEDMM use fast types, and make function inline inline uint16_t virtualWidth() const { // WLEDMM use fast types, and make function inline
uint_fast16_t groupLen = groupLength(); uint_fast16_t groupLen = groupLength();
uint_fast16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen; uint_fast16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
@@ -655,22 +684,53 @@ typedef struct Segment {
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vHeight; return vHeight;
} }
#else
inline uint16_t virtualWidth() const { return(_2dWidth);} // WLEDMM get pre-calculated virtualWidth
inline uint16_t virtualHeight() const { return(_2dHeight);} // WLEDMM get pre-calculated virtualHeight
uint16_t calc_virtualWidth() const {
uint_fast16_t groupLen = groupLength();
uint_fast16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vWidth;
}
uint16_t calc_virtualHeight() const {
uint_fast16_t groupLen = groupLength();
uint_fast16_t vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vHeight;
}
#endif
uint16_t nrOfVStrips(void) const; uint16_t nrOfVStrips(void) const;
void createjMap(); //WLEDMM jMap void createjMap(); //WLEDMM jMap
void deletejMap(); //WLEDMM jMap void deletejMap(); //WLEDMM jMap
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
inline uint16_t XY(uint_fast16_t x, uint_fast16_t y) { // support function to get relative index within segment (for leds[]) // WLEDMM inline for speed inline uint16_t XY(uint_fast16_t x, uint_fast16_t y) const { // support function to get relative index within segment (for leds[]) // WLEDMM inline for speed
uint_fast16_t width = virtualWidth(); // segment width in logical pixels uint_fast16_t width = max(uint16_t(1), virtualWidth()); // segment width in logical pixels -- softhack007 avoid div/0
uint_fast16_t height = virtualHeight(); // segment height in logical pixels uint_fast16_t height = max(uint16_t(1), virtualHeight()); // segment height in logical pixels -- softhack007 avoid div/0
if (width == 0) return 0; // softhack007 avoid div/0
if (height == 0) return (x%width); // softhack007 avoid div/0
return (x%width) + (y%height) * width; return (x%width) + (y%height) * width;
} }
//void setPixelColorXY_fast(int x, int y,uint32_t c); // set relative pixel within segment with color - wrapper for _fast #ifdef WLEDMM_FASTPATH
// WLEDMM this is a "gateway" function - we either call _fast or fall back to "slow"
inline void setPixelColorXY(int x, int y, uint32_t col) {
if (!_isSimpleSegment) { // slow path
setPixelColorXY_slow(x, y, col);
} else { // fast path
// some sanity checks
if (!_isValid2D) return; // not active
if ((unsigned(x) >= _2dWidth) || (unsigned(y) >= _2dHeight)) return; // check if (x,y) are out-of-range - due to 2's complement, this also catches negative values
if (!_brightness && !transitional) return; // black-out
uint32_t scaled_col = (_brightness == 255) ? col : color_fade(col, _brightness); // calculate final color
setPixelColorXY_fast(x, y, col, scaled_col, int(_2dWidth), int(_2dHeight)); // call "fast" function
}
}
#else
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
#endif
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
@@ -680,7 +740,7 @@ typedef struct Segment {
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
//#endif //#endif
uint32_t __attribute__((pure)) getPixelColorXY(int x, int y); uint32_t __attribute__((pure)) getPixelColorXY(int x, int y) const;
// 2D support functions // 2D support functions
void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend);
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
@@ -696,13 +756,13 @@ typedef struct Segment {
void move(uint8_t dir, uint8_t delta, bool wrap = false); void move(uint8_t dir, uint8_t delta, bool wrap = false);
void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); void fillCircle(unsigned cx, unsigned cy, int radius, uint32_t col, bool soft);
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } inline void fillCircle(unsigned cx, unsigned cy, int radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint8_t depth = UINT8_MAX); void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint8_t depth = UINT8_MAX);
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint8_t depth = UINT8_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, depth); } // automatic inline inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint8_t depth = UINT8_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, depth); } // automatic inline
void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0); void drawArc(unsigned x0, unsigned y0, int radius, uint32_t color, uint32_t fillColor = 0);
inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline inline void drawArc(unsigned x0, unsigned y0, int radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, bool drawShadow = false);
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c); void wu_pixel(uint32_t x, uint32_t y, CRGB c);
//void blur1d(fract8 blur_amount); // blur all rows in 1 dimension //void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
@@ -746,7 +806,7 @@ typedef struct Segment {
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}
inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
#endif #endif
uint8_t * getAudioPalette(int pal); //WLEDMM netmindz ar palette uint8_t * getAudioPalette(int pal) const; //WLEDMM netmindz ar palette
} segment; } segment;
//static int segSize = sizeof(Segment); //static int segSize = sizeof(Segment);
@@ -869,64 +929,66 @@ class WS2812FX { // 96 bytes
bool bool
checkSegmentAlignment(void), checkSegmentAlignment(void),
hasRGBWBus(void), hasRGBWBus(void) const,
hasCCTBus(void), hasCCTBus(void) const,
// return true if the strip is being sent pixel updates // return true if the strip is being sent pixel updates
isUpdating(void), isUpdating(void) const,
deserializeMap(uint8_t n=0), deserializeMap(uint8_t n=0),
useLedsArray = false; useLedsArray = false;
inline bool isServicing(void) { return _isServicing; } inline bool isServicing(void) const { return _isServicing; }
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} inline bool hasWhiteChannel(void) const {return _hasWhiteChannel;}
inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} inline bool isOffRefreshRequired(void) const {return _isOffRefreshRequired;}
uint8_t uint8_t
paletteFade, paletteFade,
paletteBlend, paletteBlend,
milliampsPerLed, milliampsPerLed,
cctBlending, cctBlending,
getActiveSegmentsNum(void), getActiveSegmentsNum(void) const,
getFirstSelectedSegId(void), getFirstSelectedSegId(void) __attribute__((pure)),
getLastActiveSegmentId(void), getLastActiveSegmentId(void) const,
getActiveSegsLightCapabilities(bool selectedOnly = false), getActiveSegsLightCapabilities(bool selectedOnly = false) __attribute__((pure)),
setPixelSegment(uint8_t n); setPixelSegment(uint8_t n);
inline uint8_t getBrightness(void) { return _brightness; } inline uint8_t getBrightness(void) const { return _brightness; }
inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) inline uint8_t getSegmentsNum(void) const { return _segments.size(); } // returns currently present segments
inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments inline uint8_t getCurrSegmentId(void) const { return _segment_index; }
inline uint8_t getCurrSegmentId(void) { return _segment_index; } inline uint8_t getMainSegmentId(void) const { return _mainSegment; }
inline uint8_t getMainSegmentId(void) { return _mainSegment; } inline uint8_t getTargetFps() const { return _targetFps; }
inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count inline uint8_t getModeCount() const { return _modeCount; }
inline uint8_t getTargetFps() { return _targetFps; } inline static constexpr uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value)
inline uint8_t getModeCount() { return _modeCount; } inline static constexpr uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count
uint16_t uint16_t
ablMilliampsMax, ablMilliampsMax,
currentMilliamps, currentMilliamps,
getLengthPhysical(void), getLengthPhysical(void) const,
__attribute__((pure)) getLengthTotal(void), // will include virtual/nonexistent pixels in matrix //WLEDMM attribute added getLengthPhysical2(void) const, // WLEDMM total length including HUB75, network busses excluded
getFps(); __attribute__((pure)) getLengthTotal(void) const, // will include virtual/nonexistent pixels in matrix //WLEDMM attribute added
getFps() const;
inline uint16_t getFrameTime(void) { return _frametime; } inline uint16_t getFrameTime(void) const { return _frametime; }
inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } inline uint16_t getMinShowDelay(void) const { return MIN_SHOW_DELAY; }
inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H inline uint16_t getLength(void) const { return _length; } // 2D matrix may have less pixels than W*H
inline uint16_t getTransition(void) { return _transitionDur; } inline uint16_t getTransition(void) const { return _transitionDur; }
uint32_t uint32_t
now, now,
timebase; timebase;
uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t); // WLEDMM attribute pure = does not have side-effects uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t) const; // WLEDMM attribute pure = does not have side-effects
uint32_t __attribute__((pure)) getPixelColorRestored(uint_fast16_t i) const;// WLEDMM gets the original color from the driver (without downscaling by _bri)
inline uint32_t getLastShow(void) { return _lastShow; } inline uint32_t getLastShow(void) const { return _lastShow; }
inline uint32_t segColor(uint8_t i) { return _colors_t[i]; } inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; }
const char * const char *
getModeData(uint8_t id = 0) { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); }
const char ** const char **
getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data
Segment& getSegment(uint8_t id); Segment& getSegment(uint8_t id) __attribute__((pure));
inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; }
inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; }
inline Segment* getSegments(void) { return &(_segments[0]); } inline Segment* getSegments(void) { return &(_segments[0]); }
@@ -993,7 +1055,7 @@ class WS2812FX { // 96 bytes
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
uint32_t uint32_t
getPixelColorXY(uint16_t, uint16_t); getPixelColorXY(uint16_t, uint16_t) const;
// end 2D support // end 2D support
@@ -1008,6 +1070,8 @@ class WS2812FX { // 96 bytes
std::vector<segment> _segments; std::vector<segment> _segments;
friend class Segment; friend class Segment;
uint32_t getPixelColorXYRestored(uint16_t x, uint16_t y) const; // WLEDMM gets the original color from the driver (without downscaling by _bri)
private: private:
uint16_t _length; uint16_t _length;
uint8_t _brightness; uint8_t _brightness;

View File

@@ -66,9 +66,26 @@ void WS2812FX::setUpMatrix() {
USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight);
// WLEDMM check if mapping table is necessary (avoiding heap fragmentation)
#if defined(WLED_ENABLE_HUB75MATRIX)
bool needLedMap = (loadedLedmap >0); // ledmap loaded
needLedMap |= WLED_FS.exists(F("/2d-gaps.json")); // gapFile found
needLedMap |= panel.size() > 1; // 2D config: more than one panel
if (panel.size() == 1) {
Panel &p = panel[0];
needLedMap |= p.serpentine; // panel serpentine
needLedMap |= p.vertical; // panel not horizotal
needLedMap |= p.bottomStart | p.rightStart; // panel not top left, or not left->light
needLedMap |= (p.xOffset > 0) || (p.yOffset > 0); // panel does not start at (0,0)
}
#else
bool needLedMap = true; // always use ledMaps on non-HUB75 builds
#endif
//WLEDMM recreate customMappingTable if more space needed //WLEDMM recreate customMappingTable if more space needed
if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) {
size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks
if (!needLedMap) size = 0; // softhack007
USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", size, customMappingTableSize); USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", size, customMappingTableSize);
//if (customMappingTable != nullptr) delete[] customMappingTable; //if (customMappingTable != nullptr) delete[] customMappingTable;
//customMappingTable = new uint16_t[size]; //customMappingTable = new uint16_t[size];
@@ -88,8 +105,9 @@ void WS2812FX::setUpMatrix() {
if (customMappingTable != nullptr) customMappingTableSize = size; if (customMappingTable != nullptr) customMappingTableSize = size;
} }
if (customMappingTable != nullptr) { if ((customMappingTable != nullptr) || (!needLedMap)) { // softhack007
customMappingSize = Segment::maxWidth * Segment::maxHeight; customMappingSize = Segment::maxWidth * Segment::maxHeight;
if (!needLedMap) customMappingSize = 0; // softhack007
// fill with empty in case we don't fill the entire matrix // fill with empty in case we don't fill the entire matrix
for (size_t i = 0; i< customMappingTableSize; i++) { //WLEDMM use customMappingTableSize for (size_t i = 0; i< customMappingTableSize; i++) { //WLEDMM use customMappingTableSize
@@ -130,6 +148,7 @@ void WS2812FX::setUpMatrix() {
releaseJSONBufferLock(); releaseJSONBufferLock();
} }
if (needLedMap && customMappingTable != nullptr) { // softhack007
uint16_t x, y, pix=0; //pixel uint16_t x, y, pix=0; //pixel
for (size_t pan = 0; pan < panel.size(); pan++) { for (size_t pan = 0; pan < panel.size(); pan++) {
Panel &p = panel[pan]; Panel &p = panel[pan];
@@ -146,6 +165,7 @@ void WS2812FX::setUpMatrix() {
} }
} }
} }
}
// delete gap array as we no longer need it // delete gap array as we no longer need it
if (gapTable) {delete[] gapTable; gapTable=nullptr;} // softhack prevent dangling pointer if (gapTable) {delete[] gapTable; gapTable=nullptr;} // softhack prevent dangling pointer
@@ -171,13 +191,31 @@ void WS2812FX::setUpMatrix() {
//WLEDMM: no resetSegments here, only do it in set.cpp/handleSettingsSet - as we want t0 maintain the segment settings after setup has changed //WLEDMM: no resetSegments here, only do it in set.cpp/handleSettingsSet - as we want t0 maintain the segment settings after setup has changed
} }
} }
#ifdef WLED_ENABLE_HUB75MATRIX
// softhack007 hack: delete mapping table in case it only contains "identity"
if (customMappingTable != nullptr && customMappingTableSize > 0) {
bool isIdentity = true;
for (size_t i = 0; (i< customMappingSize) && isIdentity; i++) { //WLEDMM use customMappingTableSize
if (customMappingTable[i] != (uint16_t)i ) isIdentity = false;
}
if (isIdentity) {
free(customMappingTable); customMappingTable = nullptr;
USER_PRINTF("!setupmatrix: customMappingTable is not needed. Dropping %d bytes.\n", customMappingTableSize * sizeof(uint16_t));
customMappingTableSize = 0;
customMappingSize = 0;
loadedLedmap = 0; //WLEDMM
}
}
#endif
#else #else
isMatrix = false; // no matter what config says isMatrix = false; // no matter what config says
#endif #endif
} }
// absolute matrix version of setPixelColor(), without error checking // absolute matrix version of setPixelColor(), without error checking
void IRAM_ATTR WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally void IRAM_ATTR __attribute__((hot)) WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally
{ {
uint_fast16_t index = y * Segment::maxWidth + x; uint_fast16_t index = y * Segment::maxWidth + x;
if (index < customMappingSize) index = customMappingTable[index]; if (index < customMappingSize) index = customMappingTable[index];
@@ -200,7 +238,7 @@ void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM
} }
// returns RGBW values of pixel // returns RGBW values of pixel
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t __attribute__((hot)) WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) const {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types
#else #else
@@ -211,19 +249,48 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
return busses.getPixelColor(index); return busses.getPixelColor(index);
} }
uint32_t __attribute__((hot)) WS2812FX::getPixelColorXYRestored(uint16_t x, uint16_t y) const { // WLEDMM gets the original color from the driver (without downscaling by _bri)
#ifndef WLED_DISABLE_2D
uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types
#else
uint16_t index = x;
#endif
if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return 0;
return busses.getPixelColorRestored(index);
}
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Segment:: routines // Segment:: routines
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// WLEDMM cache some values so we don't need to re-calc then for each pixel
void Segment::startFrame(void) {
_isSimpleSegment = (grouping == 1) && (spacing == 0); // we can handle pixels faster when no grouping or spacing is involved
_isSuperSimpleSegment = !mirror && !mirror_y && (grouping == 1) && (spacing == 0); // fastest - we only draw one pixel per call
#ifdef WLEDMM_FASTPATH
_isValid2D = isActive() && is2D();
_brightness = currentBri(on ? opacity : 0);
// if (reverse_y) _isSimpleSegment = false; // for A/B testing
_2dWidth = is2D() ? calc_virtualWidth() : virtualLength();
_2dHeight = calc_virtualHeight();
#if 0 && defined(WLED_ENABLE_HUB75MATRIX)
_firstFill = true; // dirty HACK
#endif
#endif
}
// WLEDMM end
// 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)
// WLEDMM Segment::XY()is declared inline, see FX.h // WLEDMM Segment::XY()is declared inline, see FX.h
// Simplified version of Segment::setPixelColorXY - without error checking. Does not support grouping or spacing // Simplified version of Segment::setPixelColorXY - without error checking. Does not support grouping or spacing
// * expects scaled color (final brightness) as additional input parameter, plus segment virtualWidth() and virtualHeight() // * expects scaled color (final brightness) as additional input parameter, plus segment virtualWidth() and virtualHeight()
void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_t scaled_col, int cols, int rows) //WLEDMM void IRAM_ATTR __attribute__((hot)) Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_t scaled_col, int cols, int rows) //WLEDMM
{ {
unsigned i = UINT_MAX; unsigned i = UINT_MAX;
bool sameColor = false; bool sameColor = false;
@@ -245,7 +312,11 @@ void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_
// set the requested pixel // set the requested pixel
strip.setPixelColorXY_fast(start + x, startY + y, scaled_col); strip.setPixelColorXY_fast(start + x, startY + y, scaled_col);
#ifdef WLEDMM_FASTPATH
bool simpleSegment = _isSuperSimpleSegment;
#else
bool simpleSegment = !mirror && !mirror_y; bool simpleSegment = !mirror && !mirror_y;
#endif
if (simpleSegment) return; // WLEDMM shortcut when no mirroring needed if (simpleSegment) return; // WLEDMM shortcut when no mirroring needed
// handle mirroring // handle mirroring
@@ -260,15 +331,19 @@ void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_
else strip.setPixelColorXY_fast(start + x, startY + hei_ - y - 1, scaled_col); else strip.setPixelColorXY_fast(start + x, startY + hei_ - y - 1, scaled_col);
} }
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY_fast(wid_ - x - 1, hei_ - y - 1, scaled_col); strip.setPixelColorXY_fast(start + wid_ - x - 1, startY + hei_ - y - 1, scaled_col);
} }
} }
// normal Segment::setPixelColorXY with error checking, and support for grouping / spacing // normal Segment::setPixelColorXY with error checking, and support for grouping / spacing
#ifdef WLEDMM_FASTPATH
void IRAM_ATTR_YN Segment::setPixelColorXY_slow(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally, renamed to "_slow"
#else
void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally
#endif
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up if ((Segment::maxHeight==1) || !isActive()) return; // not a matrix set-up
const int_fast16_t cols = virtualWidth(); // WLEDMM optimization const int_fast16_t cols = virtualWidth(); // WLEDMM optimization
const int_fast16_t rows = virtualHeight(); const int_fast16_t rows = virtualHeight();
@@ -297,15 +372,19 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM:
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
// WLEDMM shortcut when no grouping/spacing used // WLEDMM shortcut when no grouping/spacing used
#ifdef WLEDMM_FASTPATH
bool simpleSegment = _isSuperSimpleSegment;
#else
bool simpleSegment = !mirror && !mirror_y && (grouping == 1) && (spacing == 0); bool simpleSegment = !mirror && !mirror_y && (grouping == 1) && (spacing == 0);
#endif
if (simpleSegment) { if (simpleSegment) {
strip.setPixelColorXY(start + x, startY + y, col); strip.setPixelColorXY(start + x, startY + y, col);
return; return;
} }
const int_fast16_t glen_ = groupLength(); // WLEDMM optimization const uint_fast16_t glen_ = groupLength(); // WLEDMM optimization
const int_fast16_t wid_ = width(); const uint_fast16_t wid_ = width();
const int_fast16_t hei_ = height(); const uint_fast16_t hei_ = height();
x *= glen_; // expand to physical pixels x *= glen_; // expand to physical pixels
y *= glen_; // expand to physical pixels y *= glen_; // expand to physical pixels
@@ -328,18 +407,20 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM:
else strip.setPixelColorXY(start + xX, startY + hei_ - yY - 1, col); else strip.setPixelColorXY(start + xX, startY + hei_ - yY - 1, col);
} }
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY(wid_ - xX - 1, hei_ - yY - 1, col); strip.setPixelColorXY(start + wid_ - xX - 1, startY + hei_ - yY - 1, col);
} }
} }
} }
} }
// WLEDMM setPixelColorXY(float x, float y, uint32_t col, ..) is depricated. use wu_pixel(x,y,col) instead.
// anti-aliased version of setPixelColorXY() // anti-aliased version of setPixelColorXY()
void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast) // WLEDMM some speedups due to fast int and faster sqrt16 void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast) // WLEDMM some speedups due to fast int and faster sqrt16
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up if (Segment::maxHeight==1) return; // not a matrix set-up
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
#if 0 // depricated
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@@ -383,10 +464,16 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast
} else { } else {
setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col); setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col);
} }
#else // replacement using wu_pixel
unsigned px = x * (virtualWidth() <<8);
unsigned py = y * (virtualHeight() <<8);
wu_pixel(px, py, CRGB(col));
#endif
} }
// returns RGBW values of pixel // returns RGBW values of pixel
uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) { uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const {
if (x<0 || y<0 || !isActive()) return 0; // not active or out-of range if (x<0 || y<0 || !isActive()) return 0; // not active or out-of range
if (ledsrgb) { if (ledsrgb) {
int i = XY(x,y); int i = XY(x,y);
@@ -395,10 +482,11 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) {
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
x *= groupLength(); // expand to physical pixels const uint_fast16_t groupLength_ = groupLength(); // WLEDMM small optimization
y *= groupLength(); // expand to physical pixels x *= groupLength_; // expand to physical pixels
y *= groupLength_; // expand to physical pixels
if (x >= width() || y >= height()) return 0; if (x >= width() || y >= height()) return 0;
return strip.getPixelColorXY(start + x, startY + y); return strip.getPixelColorXYRestored(start + x, startY + y);
} }
// Blends the specified color with the existing pixel color. // Blends the specified color with the existing pixel color.
@@ -409,17 +497,19 @@ 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 IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { void IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active // if (!isActive()) return; // not active //WLEDMM sanity check is repeated in getPixelColorXY / setPixelColorXY
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 //WLEDMM
uint32_t col = getPixelColorXY(x,y); uint32_t oldCol = getPixelColorXY(x,y);
col = color_add(col, color, fast); uint32_t col = color_add(oldCol, color, fast);
setPixelColorXY(x, y, col); if (col != oldCol) setPixelColorXY(x, y, col);
} }
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 // if (!isActive()) return; // not active //WLEDMM sanity check is repeated in getPixelColorXY / setPixelColorXY
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); CRGB pix = CRGB(getPixelColorXY(x,y));
setPixelColorXY(x, y, pix); CRGB oldPix = pix;
pix = pix.nscale8_video(fade);
if (pix != oldPix) setPixelColorXY(int(x), int(y), pix);
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
@@ -521,7 +611,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
for (int j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
int x = vertical ? i : j; int x = vertical ? i : j;
int y = vertical ? j : i; int y = vertical ? j : i;
setPixelColorXY(x, y, out[j]); if (in[j] != out[j]) setPixelColorXY(x, y, out[j]);
} }
} }
@@ -635,19 +725,26 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t 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::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) { void Segment::fillCircle(unsigned cx, unsigned cy, int radius, uint32_t col, bool soft) {
if (!isActive() || radius == 0) return; // not active if (!isActive() || radius <= 0) return; // not active
// draw soft bounding circle // draw soft bounding circle
if (soft) drawCircle(cx, cy, radius, col, soft); if (soft) drawCircle(cx, cy, radius, col, soft);
// fill it
const int cols = virtualWidth(); const int cols = virtualWidth();
const int rows = virtualHeight(); const int rows = virtualHeight();
for (int y = -radius; y <= radius; y++) {
for (int x = -radius; x <= radius; x++) { const int_fast32_t maxRadius2 = radius * radius - (((radius > 3) && !soft) ? 1:0); // WLEDMM pre-compute r^2; '-1' removes spikes from bigger blobs
if (x * x + y * y <= radius * radius && // WLEDMM pre-compute boundaries
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 && const int startx = max(-radius, -int(cx));
int16_t(cx)+x<cols && int16_t(cy)+y<rows) const int endx = min(radius, cols-1-int(cx));
const int starty = max(-radius, -int(cy));
const int endy = min(radius, rows-1-int(cy));
// fill it - WLEDMM optimized
for (int y = starty; y <= endy; y++) {
for (int x = startx; x <= endx; x++) {
if ((x * x + y * y) <= maxRadius2) {
setPixelColorXY(cx + x, cy + y, col); setPixelColorXY(cx + x, cy + y, col);
}
} }
} }
} }
@@ -674,7 +771,11 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
uint32_t scaled_col = c; uint32_t scaled_col = c;
if (simpleSegment) { if (simpleSegment) {
// segment brightness must be pre-calculated for the "fast" setPixelColorXY variant! // segment brightness must be pre-calculated for the "fast" setPixelColorXY variant!
#ifdef WLEDMM_FASTPATH
uint8_t _bri_t = _brightness;
#else
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
#endif
if (!_bri_t && !transitional) return; if (!_bri_t && !transitional) return;
if (_bri_t < 255) scaled_col = color_fade(c, _bri_t); if (_bri_t < 255) scaled_col = color_fade(c, _bri_t);
} }
@@ -699,7 +800,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// single pixel (line length == 0) // single pixel (line length == 0)
if (dx+dy == 0) { if (dx+dy == 0) {
if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows); if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows);
else setPixelColorXY(x0, y0, c); else setPixelColorXY_slow(x0, y0, c);
return; return;
} }
@@ -735,7 +836,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
for (;;) { for (;;) {
// if (x0 >= cols || y0 >= rows) break; // WLEDMM we hit the edge - should never happen // if (x0 >= cols || y0 >= rows) break; // WLEDMM we hit the edge - should never happen
if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows); if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows);
else setPixelColorXY(x0, y0, c); else setPixelColorXY_slow(x0, y0, c);
if (x0==x1 && y0==y1) break; if (x0==x1 && y0==y1) break;
int e2 = err; int e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; } if (e2 >-dx) { err -= dy; x0 += sx; }
@@ -744,27 +845,34 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
} }
} }
void Segment::drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor) { void Segment::drawArc(unsigned x0, unsigned y0, int radius, uint32_t color, uint32_t fillColor) {
if (!isActive()) return; // not active if (!isActive() || (radius <=0)) return; // not active
// float step = degrees / (2.85f*MAX(radius,1)); float minradius = float(radius) - .5;
// for (float rad = 0.0f; rad <= degrees+step/2; rad += step) { float maxradius = float(radius) + .5;
// // may want to try float version as well (with or without antialiasing) // WLEDMM pre-calculate values to speed up the loop
// int x = roundf(sin_t(rad) * radius); const int minradius2 = roundf(minradius * minradius);
// int y = roundf(cos_t(rad) * radius); const int maxradius2 = roundf(maxradius * maxradius);
// setPixelColorXY(x+x0, y+y0, c);
// }
float minradius = radius - .5;
float maxradius = radius + .5;
for (int x=0; x<virtualWidth(); x++) for (int y=0; y<virtualHeight(); y++) {
int newX = x - x0; // WLEDMM only loop over surrounding square (50% faster)
int newY = y - y0; const int width = virtualWidth();
const int height = virtualHeight();
const int startx = max(0, int(x0)-radius-1);
const int endx = min(width, int(x0)+radius+1);
const int starty = max(0, int(y0)-radius-1);
const int endy = min(height, int(y0)+radius+1);
if (newX*newX + newY*newY >= minradius * minradius && newX*newX + newY*newY <= maxradius * maxradius) for (int x=startx; x<endx; x++) for (int y=starty; y<endy; y++) {
int newX2 = x - int(x0); newX2 *= newX2; // (distance from centerX) ^2
int newY2 = y - int(y0); newY2 *= newY2; // (distance from centerY) ^2
int distance2 = newX2 + newY2;
if ((distance2 >= minradius2) && (distance2 <= maxradius2)) {
setPixelColorXY(x, y, color); setPixelColorXY(x, y, color);
} else {
if (fillColor != 0) if (fillColor != 0)
if (newX*newX + newY*newY < minradius * minradius) if (distance2 < minradius2)
setPixelColorXY(x, y, fillColor); setPixelColorXY(x, y, fillColor);
}
} }
} }
@@ -814,7 +922,7 @@ bool Segment::jsonToPixels(char * name, uint8_t fileNr) {
// 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, bool drawShadow) {
if (!isActive()) return; // not active 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
@@ -824,6 +932,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
CRGB col = CRGB(color); CRGB col = CRGB(color);
CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col); CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col);
uint32_t bgCol = SEGCOLOR(1);
//if (w<5 || w>6 || h!=8) return; //if (w<5 || w>6 || h!=8) return;
for (int i = 0; i<h; i++) { // character height for (int i = 0; i<h; i++) { // character height
@@ -831,19 +940,39 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
if (y0 < 0) continue; // drawing off-screen if (y0 < 0) continue; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen if (y0 >= rows) break; // drawing off-screen
uint8_t bits = 0; uint8_t bits = 0;
uint8_t bits_up = 0; // WLEDMM this is the previous line: font[(chr * h) + i -1]
switch (font) { switch (font) {
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]);
case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); break; // 5x8 font if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_4x6[(chr * h) + i -1]);
case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); break; // 6x8 font break; // 5x8 font
case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); break; // 7x9 font case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]);
case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x8[(chr * h) + i -1]);
break; // 5x8 font
case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]);
if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_6x8[(chr * h) + i -1]);
break; // 6x8 font
case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]);
if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_7x9[(chr * h) + i -1]);
break; // 7x9 font
case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]);
if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x12[(chr * h) + i -1]);
break; // 5x12 font
default: return; default: return;
} }
col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND);
for (int j = 0; j<w; j++) { // character width for (int j = 0; j<w; j++) { // character width
int16_t x0 = x + (w-1) - j; int16_t x0 = x + (w-1) - j;
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen if ((x0 >= 0) || (x0 < cols)) {
if ((bits>>(j+(8-w))) & 0x01) { // bit set & drawing on-screen
setPixelColorXY(x0, y0, col); setPixelColorXY(x0, y0, col);
} else {
if (drawShadow) {
// WLEDMM
if ((j < (w-1)) && (bits>>(j+(8-w) +1)) & 0x01) setPixelColorXY(x0, y0, bgCol); // blank when pixel to the right is set
else if ((j > 0) && (bits>>(j+(8-w) -1)) & 0x01) setPixelColorXY(x0, y0, bgCol);// blank when pixel to the left is set
else if ((bits_up>>(j+(8-w))) & 0x01) setPixelColorXY(x0, y0, bgCol); // blank when pixel above is set
}
}
} }
} }
} }
@@ -859,11 +988,14 @@ void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)}; WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)};
// multiply the intensities by the colour, and saturating-add them to the pixels // multiply the intensities by the colour, and saturating-add them to the pixels
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1)); int wu_x = (x >> 8) + (i & 1); // WLEDMM precalculate x
int wu_y = (y >> 8) + ((i >> 1) & 1); // WLEDMM precalculate y
CRGB led = getPixelColorXY(wu_x, wu_y);
CRGB oldLed = led;
led.r = qadd8(led.r, c.r * wu[i] >> 8); led.r = qadd8(led.r, c.r * wu[i] >> 8);
led.g = qadd8(led.g, c.g * wu[i] >> 8); led.g = qadd8(led.g, c.g * wu[i] >> 8);
led.b = qadd8(led.b, c.b * wu[i] >> 8); led.b = qadd8(led.b, c.b * wu[i] >> 8);
setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led); if (led != oldLed) setPixelColorXY(wu_x, wu_y, led); // WLEDMM don't repaint same color
} }
} }
#undef WU_WEIGHT #undef WU_WEIGHT

View File

@@ -102,6 +102,11 @@ Segment::Segment(const Segment &orig) {
DEBUG_PRINTLN(F("-- Copy segment constructor --")); DEBUG_PRINTLN(F("-- Copy segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment)); //WLEDMM copy to this memcpy((void*)this, (void*)&orig, sizeof(Segment)); //WLEDMM copy to this
transitional = false; // copied segment cannot be in transition transitional = false; // copied segment cannot be in transition
// WLEDMM temporarily prevent any fast draw calls to the new segment
// _isValid2D = false;
_isSimpleSegment = false;
_isSuperSimpleSegment = false;
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
@@ -132,7 +137,7 @@ void Segment::allocLeds() {
ledsrgbSize = ledsrgb?size:0; ledsrgbSize = ledsrgb?size:0;
if (ledsrgb == nullptr) { if (ledsrgb == nullptr) {
USER_PRINTLN("allocLeds failed!!"); USER_PRINTLN("allocLeds failed!!");
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag errorFlag = ERR_LOW_BUF; // WLEDMM raise errorflag
} }
} }
else { else {
@@ -143,8 +148,17 @@ void Segment::allocLeds() {
// move constructor --> moves everything (including buffer) from orig to this // move constructor --> moves everything (including buffer) from orig to this
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
DEBUG_PRINTLN(F("-- Move segment constructor --")); DEBUG_PRINTLN(F("-- Move segment constructor --"));
// WLEDMM temporarily prevent any fast draw calls to old and new segment
orig._isSimpleSegment = false;
orig._isSuperSimpleSegment = false;
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more orig.transitional = false; // old segment cannot be in transition any more
#ifdef WLEDMM_FASTPATH
// WLEDMM prevent any draw calls to old segment
orig._isValid2D = false;
#endif
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@@ -169,6 +183,12 @@ Segment& Segment::operator= (const Segment &orig) {
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; transitional = false;
// WLEDMM prevent any fast draw calls to this segment until the next frame starts
//_isValid2D = false;
_isSimpleSegment = false;
_isSuperSimpleSegment = false;
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
@@ -196,7 +216,16 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) { delete _t; _t = nullptr; } if (_t) { delete _t; _t = nullptr; }
if (ledsrgb && !Segment::_globalLeds) free(ledsrgb); //WLEDMM: not needed anymore as we will use leds from copy. no need to nullify ledsrgb as it gets new value in memcpy if (ledsrgb && !Segment::_globalLeds) free(ledsrgb); //WLEDMM: not needed anymore as we will use leds from copy. no need to nullify ledsrgb as it gets new value in memcpy
// WLEDMM temporarily prevent any fast draw calls to old and new segment
orig._isSimpleSegment = false;
orig._isSuperSimpleSegment = false;
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
#ifdef WLEDMM_FASTPATH
// WLEDMM temporarily prevent any draw calls to old segment
orig._isValid2D = false;
#endif
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@@ -266,6 +295,7 @@ void Segment::resetIfRequired() {
deallocateData(); deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false); reset = false; // setOption(SEG_OPTION_RESET, false);
startFrame(); // WLEDMM update cached propoerties
} }
} }
@@ -564,15 +594,11 @@ void Segment::setOption(uint8_t n, bool val) {
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast
} }
void Segment::setMode(uint8_t fx, bool loadDefaults) { void Segment::setMode(uint8_t fx, bool loadDefaults, bool sliderDefaultsOnly) {
//WLEDMM: return to old setting if not explicitly set //WLEDMM: return to old setting if not explicitly set
static int16_t oldMap = -1; static int16_t oldMap = -1;
static int16_t oldSim = -1; static int16_t oldSim = -1;
static int16_t oldPalette = -1; static int16_t oldPalette = -1;
static byte oldReverse = -1;
static byte oldMirror = -1;
static byte oldReverse_y = -1;
static byte oldMirror_y = -1;
// 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) {
@@ -591,14 +617,16 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o1"); check1 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o1"); check1 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
//WLEDMM: return to old setting if not explicitly set if (!sliderDefaultsOnly) {
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) {if (oldMap==-1) oldMap = map1D2D; map1D2D = constrain(sOpt, 0, 7);} else {if (oldMap!=-1) map1D2D = oldMap; oldMap = -1;} //WLEDMM: return to old setting if not explicitly set
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) {if (oldSim==-1) oldSim = soundSim; soundSim = constrain(sOpt, 0, 1);} else {if (oldSim!=-1) soundSim = oldSim; oldSim = -1;} sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) {if (oldMap==-1) oldMap = map1D2D; map1D2D = constrain(sOpt, 0, 7);} else {if (oldMap!=-1) map1D2D = oldMap; oldMap = -1;}
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) {if (oldReverse==-1) oldReverse = reverse; reverse = (bool)sOpt;} else {if (oldReverse!=-1) reverse = oldReverse==1; oldReverse = -1;} sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) {if (oldSim==-1) oldSim = soundSim; soundSim = constrain(sOpt, 0, 1);} else {if (oldSim!=-1) soundSim = oldSim; oldSim = -1;}
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) {if (oldMirror==-1) oldMirror = mirror; mirror = (bool)sOpt;} else {if (oldMirror!=-1) mirror = oldMirror==1; oldMirror = -1;} // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) {if (oldReverse_y==-1) oldReverse_y = reverse_y; reverse_y = (bool)sOpt;} else {if (oldReverse_y!=-1) reverse_y = oldReverse_y==1; oldReverse_y = -1;} sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) {if (oldMirror_y==-1) oldMirror_y = mirror_y; mirror_y = (bool)sOpt;} else {if (oldMirror_y!=-1) mirror_y = oldMirror_y==1; oldMirror_y = -1;} // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) {if (oldPalette==-1) oldPalette = palette; setPalette(sOpt);} else {if (oldPalette!=-1) setPalette(oldPalette); oldPalette = -1;} sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) {if (oldPalette==-1) oldPalette = palette; setPalette(sOpt);} else {if (oldPalette!=-1) setPalette(oldPalette); oldPalette = -1;}
}
} }
if (!fadeTransition) markForReset(); // WLEDMM quickfix for effect "double startup" bug. -> only works when "Crossfade" is disabled (led settings) if (!fadeTransition) markForReset(); // WLEDMM quickfix for effect "double startup" bug. -> only works when "Crossfade" is disabled (led settings)
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
@@ -673,7 +701,11 @@ class JMapC {
if (size > 0) if (size > 0)
return size; return size;
else else
#ifndef WLEDMM_FASTPATH
return SEGMENT.virtualWidth() * SEGMENT.virtualHeight(); //pixels return SEGMENT.virtualWidth() * SEGMENT.virtualHeight(); //pixels
#else
return SEGMENT.calc_virtualWidth() * SEGMENT.calc_virtualHeight(); // calc pixel sizes
#endif
} }
void setPixelColor(uint16_t i, uint32_t col) { void setPixelColor(uint16_t i, uint32_t col) {
updatejMapDoc(); updatejMapDoc();
@@ -765,7 +797,11 @@ class JMapC {
jMapFile.close(); jMapFile.close();
maxWidth++; maxHeight++; maxWidth++; maxHeight++;
#ifndef WLEDMM_FASTPATH
scale = min(SEGMENT.virtualWidth() / maxWidth, SEGMENT.virtualHeight() / maxHeight); // WLEDMM use native min/max scale = min(SEGMENT.virtualWidth() / maxWidth, SEGMENT.virtualHeight() / maxHeight); // WLEDMM use native min/max
#else
scale = min(SEGMENT.calc_virtualWidth() / maxWidth, SEGMENT.calc_virtualHeight() / maxHeight); // WLEDMM re-calc width/heiht from active settings
#endif
dataSize += sizeof(jVectorMap); dataSize += sizeof(jVectorMap);
USER_PRINT("dataSize "); USER_PRINT("dataSize ");
USER_PRINT(dataSize); USER_PRINT(dataSize);
@@ -803,10 +839,13 @@ constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big"
constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50 constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50
constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL" constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL"
constexpr int Pinwheel_Steps_XL = 368; constexpr int Pinwheel_Steps_XL = 368;
constexpr int Pinwheel_Size_XL = 58; // larger than this -> use "XXL"
constexpr int Pinwheel_Steps_XXL = 456;
constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians
constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians
constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians
constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians
constexpr float Int_to_Rad_XXL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XXL; // conversion: from 0...456 to Radians
constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction)
@@ -816,8 +855,9 @@ static float getPinwheelAngle(int i, int vW, int vH) {
if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small; if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small;
if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med; if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med;
if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big; if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big;
if (maxXY <= Pinwheel_Size_XL) return float(i) * Int_to_Rad_XL;
// else // else
return float(i) * Int_to_Rad_XL; return float(i) * Int_to_Rad_XXL;
} }
// Pinwheel helper function: matrix dimensions to number of rays // Pinwheel helper function: matrix dimensions to number of rays
static int getPinwheelLength(int vW, int vH) { static int getPinwheelLength(int vW, int vH) {
@@ -825,8 +865,9 @@ static int getPinwheelLength(int vW, int vH) {
if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small; if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small;
if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium; if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium;
if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big; if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big;
if (maxXY <= Pinwheel_Size_XL) return Pinwheel_Steps_XL;
// else // else
return Pinwheel_Steps_XL; return Pinwheel_Steps_XXL;
} }
#endif #endif
@@ -873,7 +914,7 @@ uint16_t Segment::virtualLength() const {
} }
//WLEDMM used for M12_sBlock //WLEDMM used for M12_sBlock
void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH, uint16_t vStrip) { static void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH, uint16_t vStrip) {
float i2; float i2;
if (i<=SEGLEN*0.25) { //top, left to right if (i<=SEGLEN*0.25) { //top, left to right
i2 = i/(SEGLEN*0.25); i2 = i/(SEGLEN*0.25);
@@ -898,7 +939,7 @@ void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH,
} }
void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally void IRAM_ATTR_YN __attribute__((hot)) Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally
{ {
if (!isActive()) return; // not active if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@@ -927,42 +968,51 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT
if (i==0) if (i==0)
setPixelColorXY(0, 0, col); setPixelColorXY(0, 0, col);
else { else {
//WLEDMM: drawArc(0, 0, i, col); could work as alternative if (!_isSuperSimpleSegment) {
// WLEDMM: drawArc() is faster if it's NOT "super simple" as the regular M12_pArc
// can do "useSymmetry" to speed things along, but a more complicated segment likey
// uses mirroring which generates a symmetry speed-up, or other things which mean
// less pixels are calculated.
drawArc(0, 0, i, col);
} else {
//WLEDMM: some optimizations for the drawing loop
// pre-calculate loop limits, exploit symmetry at 45deg
float radius = float(i);
// float step = HALF_PI / (2.85f * radius); // upstream uses this
float step = HALF_PI / (M_PI * radius); // WLEDMM we use the correct circumference
bool useSymmetry = (max(vH, vW) > 20); // for segments wider than 20 pixels, we exploit symmetry
unsigned numSteps;
if (useSymmetry) numSteps = 1 + ((HALF_PI/2.0f + step/2.0f) / step); // with symmetry
else numSteps = 1 + ((HALF_PI + step/2.0f) / step); // without symmetry
//WLEDMM: some optimizations for the drawing loop float rad = 0.0f;
// pre-calculate loop limits, exploit symmetry at 45deg for (unsigned count = 0; count < numSteps; count++) {
float radius = float(i); // may want to try float version as well (with or without antialiasing)
// float step = HALF_PI / (2.85f * radius); // upstream uses this // int x = max(0, min(vW-1, (int)roundf(sinf(rad) * radius)));
float step = HALF_PI / (M_PI * radius); // WLEDMM we use the correct circumference // int y = max(0, min(vH-1, (int)roundf(cosf(rad) * radius)));
bool useSymmetry = (max(vH, vW) > 20); // for segments wider than 20 pixels, we exploit symmetry int x = roundf(sinf(rad) * radius);
unsigned numSteps; int y = roundf(cosf(rad) * radius);
if (useSymmetry) numSteps = 1 + ((HALF_PI/2.0f + step/2.0f) / step); // with symmetry setPixelColorXY(x, y, col);
else numSteps = 1 + ((HALF_PI + step/2.0f) / step); // without symmetry if(useSymmetry) setPixelColorXY(y, x, col);// WLEDMM
rad += step;
}
float rad = 0.0f; // // Bresenhams Algorithm (may not fill every pixel)
for (unsigned count = 0; count < numSteps; count++) { // int d = 3 - (2*i);
// may want to try float version as well (with or without antialiasing) // int y = i, x = 0;
int x = roundf(sinf(rad) * radius); // while (y >= x) {
int y = roundf(cosf(rad) * radius); // setPixelColorXY(x, y, col);
setPixelColorXY(x, y, col); // setPixelColorXY(y, x, col);
if(useSymmetry) setPixelColorXY(y, x, col);// WLEDMM // x++;
rad += step; // if (d > 0) {
// y--;
// d += 4 * (x - y) + 10;
// } else {
// d += 4 * x + 6;
// }
// }
} }
// Bresenhams Algorithm (may not fill every pixel)
//int d = 3 - (2*i);
//int y = i, x = 0;
//while (y >= x) {
// setPixelColorXY(x, y, col);
// setPixelColorXY(y, x, col);
// x++;
// if (d > 0) {
// y--;
// d += 4 * (x - y) + 10;
// } else {
// d += 4 * x + 6;
// }
//}
} }
break; break;
case M12_pCorner: { case M12_pCorner: {
@@ -1057,7 +1107,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT
// set pixel // set pixel
if (x != lastX || y != lastY) { // only paint if pixel position is different if (x != lastX || y != lastY) { // only paint if pixel position is different
if (simpleSegment) setPixelColorXY_fast(x, y, col, scaled_col, vW, vH); if (simpleSegment) setPixelColorXY_fast(x, y, col, scaled_col, vW, vH);
else setPixelColorXY(x, y, col); else setPixelColorXY_slow(x, y, col);
} }
lastX = x; lastX = x;
lastY = y; lastY = y;
@@ -1162,7 +1212,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
} }
} }
uint32_t Segment::getPixelColor(int i) uint32_t __attribute__((hot)) Segment::getPixelColor(int i) const
{ {
if (!isActive()) return 0; // not active if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@@ -1254,7 +1304,7 @@ uint32_t Segment::getPixelColor(int i)
if (offset < INT16_MAX) i += offset; // WLEDMM if (offset < INT16_MAX) i += offset; // WLEDMM
if ((i >= stop) && (stop>0)) i -= length(); // WLEDMM avoid negative index (stop = 0 is a possible value) if ((i >= stop) && (stop>0)) i -= length(); // WLEDMM avoid negative index (stop = 0 is a possible value)
if (i<0) i=0; // WLEDMM just to be 100% sure if (i<0) i=0; // WLEDMM just to be 100% sure
return strip.getPixelColor(i); return strip.getPixelColorRestored(i);
} }
uint8_t Segment::differs(Segment& b) const { uint8_t Segment::differs(Segment& b) const {
@@ -1336,8 +1386,20 @@ void Segment::refreshLightCapabilities() {
/* /*
* Fills segment with color - WLEDMM using faster sPC if possible * Fills segment with color - WLEDMM using faster sPC if possible
*/ */
void Segment::fill(uint32_t c) { void __attribute__((hot)) Segment::fill(uint32_t c) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
#if 0 && defined(WLED_ENABLE_HUB75MATRIX) && defined(WLEDMM_FASTPATH)
// DIRTY HACK - this ignores the first fill(black) in each frame, knowing that HUB75 has already blanked out the display.
if (_firstFill) {
_firstFill = false;
if (c == BLACK) {
if (ledsrgb && ledsrgbSize > 0) memset(ledsrgb, 0, ledsrgbSize);
return;
}
}
#endif
const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types
const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D
@@ -1351,12 +1413,12 @@ void Segment::fill(uint32_t c) {
if (_bri_t < 255) scaled_col = color_fade(c, _bri_t); if (_bri_t < 255) scaled_col = color_fade(c, _bri_t);
} }
// fill 2D segment // fill 2D segment
for(int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { for(unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) {
if (simpleSegment) setPixelColorXY_fast(x, y, c, scaled_col, cols, rows); if (simpleSegment) setPixelColorXY_fast(x, y, c, scaled_col, cols, rows);
else setPixelColorXY(x, y, c); else setPixelColorXY_slow(x, y, c);
} }
} else { // fill 1D strip } else { // fill 1D strip
for (int x = 0; x < cols; x++) setPixelColor(x, c); for (unsigned x = 0; x < cols; x++) setPixelColor(int(x), c);
} }
} }
@@ -1395,7 +1457,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 __attribute__((hot)) Segment::fade_out(uint8_t rate) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types
const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D
@@ -1409,8 +1471,8 @@ void Segment::fade_out(uint8_t rate) {
int g2 = G(color2); int g2 = G(color2);
int b2 = B(color2); int b2 = B(color2);
for (uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) { for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) {
uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); uint32_t color = is2D() ? getPixelColorXY(int(x), int(y)) : getPixelColor(int(x));
if (color == color2) continue; // WLEDMM speedup - pixel color = target color, so nothing to do if (color == color2) continue; // WLEDMM speedup - pixel color = target color, so nothing to do
int w1 = W(color); int w1 = W(color);
int r1 = R(color); int r1 = R(color);
@@ -1427,15 +1489,17 @@ void Segment::fade_out(uint8_t rate) {
rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1;
gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1;
bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1;
uint32_t colorNew = RGBW32(r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); // WLEDMM
//if ((wdelta == 0) && (rdelta == 0) && (gdelta == 0) && (bdelta == 0)) continue; // WLEDMM delta = zero => no change // causes problem with text overlay if (colorNew != color) { // WLEDMM speedup - do not repaint the same color
if (is2D()) setPixelColorXY((uint16_t)x, (uint16_t)y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); if (is2D()) setPixelColorXY(int(x), int(y), colorNew);
else setPixelColor((uint16_t)x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); else setPixelColor(int(x), colorNew);
}
} }
} }
// fades all pixels to black using nscale8() // fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) { void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) {
if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types
const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D
@@ -1443,8 +1507,13 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
// WLEDMM minor optimization // WLEDMM minor optimization
if(is2D()) { if(is2D()) {
for (uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) { for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) {
setPixelColorXY((uint16_t)x, (uint16_t)y, CRGB(getPixelColorXY(x,y)).nscale8(scaledown)); uint32_t cc = getPixelColorXY(int(x),int(y)); // WLEDMM avoid RGBW32 -> CRGB -> RGBW32 conversion
uint32_t cc2 = color_fade(cc, scaledown); // fade
#ifdef WLEDMM_FASTPATH
if (cc2 != cc) // WLEDMM only re-paint if faded color is different - normally disabled - causes problem with text overlay
#endif
setPixelColorXY(int(x), int(y), cc2);
} }
} else { } else {
for (uint_fast16_t x = 0; x < cols; x++) { for (uint_fast16_t x = 0; x < cols; x++) {
@@ -1456,7 +1525,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
/* /*
* blurs segment content, source: FastLED colorutils.cpp * blurs segment content, source: FastLED colorutils.cpp
*/ */
void Segment::blur(uint8_t blur_amount, bool smear) { void __attribute__((hot)) Segment::blur(uint8_t blur_amount, bool smear) {
if (!isActive() || 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()) {
@@ -1517,7 +1586,7 @@ uint32_t Segment::color_wheel(uint8_t pos) {
/* /*
* Returns a new, random wheel index with a minimum distance of 42 from pos. * Returns a new, random wheel index with a minimum distance of 42 from pos.
*/ */
uint8_t Segment::get_random_wheel_index(uint8_t pos) { // WLEDMM use fast int types, use native min/max uint8_t Segment::get_random_wheel_index(uint8_t pos) const { // WLEDMM use fast int types, use native min/max
uint_fast8_t r = 0, x = 0, y = 0, d = 0; uint_fast8_t r = 0, x = 0, y = 0, d = 0;
while(d < 42) { while(d < 42) {
@@ -1538,7 +1607,7 @@ uint8_t Segment::get_random_wheel_index(uint8_t pos) { // WLEDMM use fast int ty
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
* @returns Single color from palette * @returns Single color from palette
*/ */
uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) // WLEDMM use fast int types uint32_t __attribute__((hot)) Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) // WLEDMM use fast int types
{ {
// default palette or no RGB support on segment // default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
@@ -1558,7 +1627,7 @@ uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, u
} }
//WLEDMM netmindz ar palette //WLEDMM netmindz ar palette
uint8_t * Segment::getAudioPalette(int pal) { uint8_t * Segment::getAudioPalette(int pal) const {
// https://forum.makerforums.info/t/hi-is-it-possible-to-define-a-gradient-palette-at-runtime-the-define-gradient-palette-uses-the/63339 // https://forum.makerforums.info/t/hi-is-it-possible-to-define-a-gradient-palette-at-runtime-the-define-gradient-palette-uses-the/63339
um_data_t *um_data; um_data_t *um_data;
@@ -1835,6 +1904,7 @@ void WS2812FX::service() {
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
seg.startFrame(); // WLEDMM
// effect blending (execute previous effect) // effect blending (execute previous effect)
// actual code may be a bit more involved as effects have runtime data including allocated memory // actual code may be a bit more involved as effects have runtime data including allocated memory
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
@@ -1866,13 +1936,19 @@ void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
busses.setPixelColor(i, col); busses.setPixelColor(i, col);
} }
uint32_t WS2812FX::getPixelColor(uint_fast16_t i) // WLEDMM fast int types uint32_t WS2812FX::getPixelColor(uint_fast16_t i) const // WLEDMM fast int types
{ {
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return 0; if (i >= _length) return 0;
return busses.getPixelColor(i); return busses.getPixelColor(i);
} }
uint32_t WS2812FX::getPixelColorRestored(uint_fast16_t i) const // WLEDMM gets the original color from the driver (without downscaling by _bri)
{
if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return 0;
return busses.getPixelColorRestored(i);
}
//DISCLAIMER //DISCLAIMER
//The following function attemps to calculate the current LED power usage, //The following function attemps to calculate the current LED power usage,
@@ -1917,7 +1993,8 @@ void WS2812FX::estimateCurrentAndLimitBri() {
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 auto btype = bus->getType();
if (EXCLUDE_FROM_ABL(btype)) continue; // WLEDMM exclude non-ABL and network busses
uint16_t len = bus->getLength(); uint16_t len = bus->getLength();
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
@@ -2004,7 +2081,7 @@ void WS2812FX::show(void) {
* Returns a true value if any of the strips are still being updated. * Returns a true value if any of the strips are still being updated.
* On some hardware (ESP32), strip updates are done asynchronously. * On some hardware (ESP32), strip updates are done asynchronously.
*/ */
bool WS2812FX::isUpdating() { bool WS2812FX::isUpdating() const {
return !busses.canAllShow(); return !busses.canAllShow();
} }
@@ -2012,7 +2089,7 @@ bool WS2812FX::isUpdating() {
* Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough. * Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough.
* Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies * Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies
*/ */
uint16_t WS2812FX::getFps() { uint16_t WS2812FX::getFps() const {
if (millis() - _lastShow > 2000) return 0; if (millis() - _lastShow > 2000) return 0;
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
return ((_cumulativeFps500 + 250) / 500); // +250 for proper rounding return ((_cumulativeFps500 + 250) / 500); // +250 for proper rounding
@@ -2103,14 +2180,14 @@ void WS2812FX::setMainSegmentId(uint8_t n) {
return; return;
} }
uint8_t WS2812FX::getLastActiveSegmentId(void) { uint8_t WS2812FX::getLastActiveSegmentId(void) const {
for (size_t i = _segments.size() -1; i > 0; i--) { for (size_t i = _segments.size() -1; i > 0; i--) {
if (_segments[i].isActive()) return i; if (_segments[i].isActive()) return i;
} }
return 0; return 0;
} }
uint8_t WS2812FX::getActiveSegmentsNum(void) { uint8_t WS2812FX::getActiveSegmentsNum(void) const {
uint8_t c = 0; uint8_t c = 0;
for (size_t i = 0; i < _segments.size(); i++) { for (size_t i = 0; i < _segments.size(); i++) {
if (_segments[i].isActive()) c++; if (_segments[i].isActive()) c++;
@@ -2118,17 +2195,30 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
return c; return c;
} }
uint16_t WS2812FX::getLengthTotal(void) { // WLEDMM fast int types uint16_t WS2812FX::getLengthTotal(void) const { // WLEDMM fast int types
uint_fast16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D uint_fast16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
return len; return len;
} }
uint16_t WS2812FX::getLengthPhysical(void) { // WLEDMM fast int types uint16_t WS2812FX::getLengthPhysical(void) const { // WLEDMM fast int types
uint_fast16_t len = 0; uint_fast16_t len = 0;
for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses auto btype = bus->getType();
if (EXCLUDE_FROM_ABL(btype)) continue; //exclude HUB75, and non-physical network busses
len += bus->getLength();
}
return len;
}
//WLEDMM - getLengthPhysical plus plysical busses not supporting ABL (i.e. HUB75)
uint16_t WS2812FX::getLengthPhysical2(void) const {
uint_fast16_t len = 0;
for (unsigned b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
auto btype = bus->getType();
if (IS_VIRTUAL(btype)) continue;
len += bus->getLength(); len += bus->getLength();
} }
return len; return len;
@@ -2137,7 +2227,7 @@ uint16_t WS2812FX::getLengthPhysical(void) { // WLEDMM fast int types
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw. //used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
//returns if there is an RGBW bus (supports RGB and White, not only white) //returns if there is an RGBW bus (supports RGB and White, not only white)
//not influenced by auto-white mode, also true if white slider does not affect output white channel //not influenced by auto-white mode, also true if white slider does not affect output white channel
bool WS2812FX::hasRGBWBus(void) { bool WS2812FX::hasRGBWBus(void) const {
for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
@@ -2146,7 +2236,7 @@ bool WS2812FX::hasRGBWBus(void) {
return false; return false;
} }
bool WS2812FX::hasCCTBus(void) { bool WS2812FX::hasCCTBus(void) const {
if (cctFromRgb && !correctWB) return false; if (cctFromRgb && !correctWB) return false;
for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
@@ -2490,6 +2580,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
uint16_t maxHeight = atoi(cleanUpName(fileName)); uint16_t maxHeight = atoi(cleanUpName(fileName));
//DEBUG_PRINTF(" (\"height\": %s) \n", fileName) //DEBUG_PRINTF(" (\"height\": %s) \n", fileName)
#ifndef WLEDMM_NO_MAP_RESET
//WLEDMM: support ledmap file properties width and height: if found change segment //WLEDMM: support ledmap file properties width and height: if found change segment
if (maxWidth * maxHeight > 0) { if (maxWidth * maxHeight > 0) {
Segment::maxWidth = maxWidth; Segment::maxWidth = maxWidth;
@@ -2498,6 +2589,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
} }
else else
setUpMatrix(); //reset segment sizes to panels setUpMatrix(); //reset segment sizes to panels
#endif
} }
USER_PRINTF("deserializeMap %d x %d\n", Segment::maxWidth, Segment::maxHeight); USER_PRINTF("deserializeMap %d x %d\n", Segment::maxWidth, Segment::maxHeight);

View File

@@ -9,6 +9,36 @@
#include "bus_wrapper.h" #include "bus_wrapper.h"
#include "bus_manager.h" #include "bus_manager.h"
// WLEDMM functions to get/set bits in an array - based on functions created by Brandon for GOL
// toDo : make this a class that's completely defined in a header file
bool getBitFromArray(const uint8_t* byteArray, size_t position) { // get bit value
size_t byteIndex = position / 8;
unsigned bitIndex = position % 8;
uint8_t byteValue = byteArray[byteIndex];
return (byteValue >> bitIndex) & 1;
}
void setBitInArray(uint8_t* byteArray, size_t position, bool value) { // set bit - with error handling for nullptr
//if (byteArray == nullptr) return;
size_t byteIndex = position / 8;
unsigned bitIndex = position % 8;
if (value)
byteArray[byteIndex] |= (1 << bitIndex);
else
byteArray[byteIndex] &= ~(1 << bitIndex);
}
size_t getBitArrayBytes(size_t num_bits) { // number of bytes needed for an array with num_bits bits
return (num_bits + 7) / 8;
}
void setBitArray(uint8_t* byteArray, size_t numBits, bool value) { // set all bits to same value
if (byteArray == nullptr) return;
size_t len = getBitArrayBytes(numBits);
if (value) memset(byteArray, 0xFF, len);
else memset(byteArray, 0x00, len);
}
//WLEDMM: #define DEBUGOUT(x) netDebugEnabled?NetDebug.print(x):Serial.print(x) not supported in this file as netDebugEnabled not in scope //WLEDMM: #define DEBUGOUT(x) netDebugEnabled?NetDebug.print(x):Serial.print(x) not supported in this file as netDebugEnabled not in scope
#if 0 #if 0
//colors.cpp //colors.cpp
@@ -49,13 +79,6 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte
#include "wled.h" #include "wled.h"
#endif #endif
//color mangling macros
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
#define R(c) (byte((c) >> 16))
#define G(c) (byte((c) >> 8))
#define B(c) (byte(c))
#define W(c) (byte((c) >> 24))
void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) { if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
@@ -86,7 +109,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul
} }
uint32_t Bus::autoWhiteCalc(uint32_t c) { uint32_t Bus::autoWhiteCalc(uint32_t c) const {
uint8_t aWM = _autoWhiteMode; uint8_t aWM = _autoWhiteMode;
if (_gAWM != AW_GLOBAL_DISABLED) aWM = _gAWM; if (_gAWM != AW_GLOBAL_DISABLED) aWM = _gAWM;
if (aWM == RGBW_MODE_MANUAL_ONLY) return c; if (aWM == RGBW_MODE_MANUAL_ONLY) return c;
@@ -182,7 +205,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
} }
uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) { uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) const {
if (reversed) pix = _len - pix -1; if (reversed) pix = _len - pix -1;
else pix += _skip; else pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
@@ -200,7 +223,7 @@ uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) {
return PolyBus::getPixelColor(_busPtr, _iType, pix, co); return PolyBus::getPixelColor(_busPtr, _iType, pix, co);
} }
uint8_t BusDigital::getPins(uint8_t* pinArray) { uint8_t BusDigital::getPins(uint8_t* pinArray) const {
uint8_t numPins = IS_2PIN(_type) ? 2 : 1; uint8_t numPins = IS_2PIN(_type) ? 2 : 1;
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins; return numPins;
@@ -317,7 +340,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
} }
//does no index check //does no index check
uint32_t BusPwm::getPixelColor(uint16_t pix) { uint32_t BusPwm::getPixelColor(uint16_t pix) const {
if (!_valid) return 0; if (!_valid) return 0;
#if 1 #if 1
// WLEDMM stick with the old code - we don't have cctICused // WLEDMM stick with the old code - we don't have cctICused
@@ -356,7 +379,7 @@ void BusPwm::show() {
} }
} }
uint8_t BusPwm::getPins(uint8_t* pinArray) { uint8_t BusPwm::getPins(uint8_t* pinArray) const {
if (!_valid) return 0; if (!_valid) return 0;
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++) {
@@ -408,7 +431,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
_data = 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) const {
if (!_valid) return 0; if (!_valid) return 0;
return RGBW32(_data, _data, _data, _data); return RGBW32(_data, _data, _data, _data);
} }
@@ -418,7 +441,7 @@ void BusOnOff::show() {
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data);
} }
uint8_t BusOnOff::getPins(uint8_t* pinArray) { uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
if (!_valid) return 0; if (!_valid) return 0;
pinArray[0] = _pin; pinArray[0] = _pin;
return 1; return 1;
@@ -467,7 +490,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (_rgbw) _data[offset+3] = W(c); if (_rgbw) _data[offset+3] = W(c);
} }
uint32_t BusNetwork::getPixelColor(uint16_t pix) { uint32_t BusNetwork::getPixelColor(uint16_t pix) const {
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] << 24) : 0);
@@ -480,7 +503,7 @@ void BusNetwork::show() {
_broadcastLock = false; _broadcastLock = false;
} }
uint8_t BusNetwork::getPins(uint8_t* pinArray) { uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
for (uint8_t i = 0; i < 4; i++) { for (uint8_t i = 0; i < 4; i++) {
pinArray[i] = _client[i]; pinArray[i] = _client[i];
} }
@@ -500,12 +523,35 @@ void BusNetwork::cleanup() {
#warning "HUB75 driver enabled (experimental)" #warning "HUB75 driver enabled (experimental)"
BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
size_t lastHeap = ESP.getFreeHeap();
mxconfig.double_buff = false; // default to off, known to cause issue with some effects but needs more memory _valid = false;
fourScanPanel = nullptr; fourScanPanel = nullptr;
mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer
// mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver
// mxconfig.latch_blanking = 3;
// mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz
// mxconfig.min_refresh_rate = 90;
mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left.
// How many panels we have connected, cap at sane value
mxconfig.chain_length = max((uint8_t) 1, min(bc.pins[0], (uint8_t) 4)); // prevent bad data preventing boot due to low memory
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM)
if(bc.pins[0] > 4) {
USER_PRINTLN("WARNING, chain limited to 4");
}
# else
// Disable this check if you are want to try bigger setups and accept you
// might need to do full erase to recover from memory relayed boot-loop if you push too far
if(mxconfig.mx_height >= 64 && (bc.pins[0] > 1)) {
USER_PRINTLN("WARNING, only single panel can be used of 64 pixel boards due to memory");
//mxconfig.chain_length = 1;
}
#endif
switch(bc.type) { switch(bc.type) {
case 101: case 101:
mxconfig.mx_width = 32; mxconfig.mx_width = 32;
@@ -533,12 +579,13 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
break; break;
} }
if(mxconfig.mx_height >= 64 && (bc.pins[0] > 1)) { #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)// classic esp32, or esp32-s2: reduce bitdepth for large panels
USER_PRINT("WARNING, only single panel can be used of 64 pixel boards due to memory") if (mxconfig.mx_height >= 64) {
mxconfig.chain_length = 1; if (mxconfig.chain_length * mxconfig.mx_width > 192) mxconfig.setPixelColorDepthBits(3);
} else if (mxconfig.chain_length * mxconfig.mx_width > 64) mxconfig.setPixelColorDepthBits(4);
else mxconfig.setPixelColorDepthBits(8);
// mxconfig.driver = HUB75_I2S_CFG::SHIFTREG; } else mxconfig.setPixelColorDepthBits(8);
#endif
#if defined(ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3) // MatrixPortal ESP32-S3 #if defined(ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3) // MatrixPortal ESP32-S3
@@ -546,8 +593,6 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
USER_PRINTLN("MatrixPanel_I2S_DMA - Matrix Portal S3 config"); USER_PRINTLN("MatrixPanel_I2S_DMA - Matrix Portal S3 config");
mxconfig.double_buff = true; // <------------- Turn on double buffer
mxconfig.gpio.r1 = 42; mxconfig.gpio.r1 = 42;
mxconfig.gpio.g1 = 41; mxconfig.gpio.g1 = 41;
mxconfig.gpio.b1 = 40; mxconfig.gpio.b1 = 40;
@@ -565,6 +610,76 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
mxconfig.gpio.d = 35; mxconfig.gpio.d = 35;
mxconfig.gpio.e = 21; mxconfig.gpio.e = 21;
#elif defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM)// ESP32-S3
USER_PRINTLN("MatrixPanel_I2S_DMA - S3 with PSRAM");
mxconfig.gpio.r1 = 1;
mxconfig.gpio.g1 = 2;
mxconfig.gpio.b1 = 42;
// 4th pin is GND
mxconfig.gpio.r2 = 41;
mxconfig.gpio.g2 = 40;
mxconfig.gpio.b2 = 39;
mxconfig.gpio.e = 38;
mxconfig.gpio.a = 45;
mxconfig.gpio.b = 48;
mxconfig.gpio.c = 47;
mxconfig.gpio.d = 21;
mxconfig.gpio.clk = 18;
mxconfig.gpio.lat = 8;
mxconfig.gpio.oe = 3;
// 16th pin is GND
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // ESP32-S3
// Huidu HD-WF2 ESP32-S3
// https://www.aliexpress.com/item/1005002258734810.html
// https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/issues/433
USER_PRINTLN("MatrixPanel_I2S_DMA - HD-WF2 S3 config");
mxconfig.gpio.r1 = 2;
mxconfig.gpio.g1 = 6;
mxconfig.gpio.b1 = 10;
mxconfig.gpio.r2 = 3;
mxconfig.gpio.g2 = 7;
mxconfig.gpio.b2 = 11;
mxconfig.gpio.lat = 33;
mxconfig.gpio.oe = 35;
mxconfig.gpio.clk = 34;
mxconfig.gpio.a = 39;
mxconfig.gpio.b = 38;
mxconfig.gpio.c = 37;
mxconfig.gpio.d = 36;
mxconfig.gpio.e = 21;
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // ESP32-S2
// Huidu HD-WF1 ESP32-S2
// https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/issues/433
USER_PRINTLN("MatrixPanel_I2S_DMA - HD-WF1 S2 config");
mxconfig.gpio.r1 = 2;
mxconfig.gpio.g1 = 6;
mxconfig.gpio.b1 = 3;
mxconfig.gpio.r2 = 4;
mxconfig.gpio.g2 = 8;
mxconfig.gpio.b2 = 5;
mxconfig.gpio.lat = 33;
mxconfig.gpio.oe = 35;
mxconfig.gpio.clk = 34;
mxconfig.gpio.a = 39;
mxconfig.gpio.b = 38;
mxconfig.gpio.c = 37;
mxconfig.gpio.d = 36;
mxconfig.gpio.e = 12;
#elif defined(ESP32_FORUM_PINOUT) // Common format for boards designed for SmartMatrix #elif defined(ESP32_FORUM_PINOUT) // Common format for boards designed for SmartMatrix
USER_PRINTLN("MatrixPanel_I2S_DMA - ESP32_FORUM_PINOUT"); USER_PRINTLN("MatrixPanel_I2S_DMA - ESP32_FORUM_PINOUT");
@@ -624,9 +739,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
#endif #endif
mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u (type %u) length: %u, %u bits/pixel.\n", mxconfig.mx_width, mxconfig.mx_height, bc.type, mxconfig.chain_length, mxconfig.getPixelColorDepthBits() * 3);
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); lastHeap = ESP.getFreeHeap();
USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length);
// OK, now we can create our matrix object // OK, now we can create our matrix object
display = new MatrixPanel_I2S_DMA(mxconfig); display = new MatrixPanel_I2S_DMA(mxconfig);
@@ -655,14 +769,50 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
USER_PRINTLN("MatrixPanel_I2S_DMA created"); USER_PRINTLN("MatrixPanel_I2S_DMA created");
// let's adjust default brightness // let's adjust default brightness
display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100%
_bri = 25;
delay(24); // experimental
DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(lastHeap - ESP.getFreeHeap());
// Allocate memory and start DMA display // Allocate memory and start DMA display
if( not display->begin() ) { if( not display->begin() ) {
USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********");
USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap());
return; return;
} }
else { else {
USER_PRINTLN("MatrixPanel_I2S_DMA begin ok");
USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap());
delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle
_valid = true; _valid = true;
display->clearScreen(); // initially clear the screen buffer
USER_PRINTLN("MatrixPanel_I2S_DMA clear ok");
if (_ledBuffer) free(_ledBuffer); // should not happen
if (_ledsDirty) free(_ledsDirty); // should not happen
_ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits
if (_ledsDirty == nullptr) {
display->stopDMAoutput();
delete display; display = nullptr;
_valid = false;
USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for dirty bits!"));
USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap());
return; // fail is we cannot get memory for the buffer
}
setBitArray(_ledsDirty, _len, false); // reset dirty bits
if (mxconfig.double_buff == false) {
#if defined(CONFIG_IDF_TARGET_ESP32S3) && CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON))
if (psramFound()) {
_ledBuffer = (CRGB*) ps_calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK)
} else {
_ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK)
}
#else
_ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK)
#endif
}
} }
switch(bc.type) { switch(bc.type) {
@@ -686,28 +836,134 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
break; break;
} }
if (_valid) {
_panelWidth = fourScanPanel ? fourScanPanel->width() : display->width(); // cache width - it will never change
}
USER_PRINTLN("MatrixPanel_I2S_DMA started"); USER_PRINT(F("MatrixPanel_I2S_DMA "));
USER_PRINTF("%sstarted, width=%u, %u pixels.\n", _valid? "":"not ", _panelWidth, _len);
if (mxconfig.double_buff == true) USER_PRINTLN(F("MatrixPanel_I2S_DMA driver native double-buffering enabled."));
if (_ledBuffer != nullptr) USER_PRINTLN(F("MatrixPanel_I2S_DMA LEDS buffer enabled."));
if (_ledsDirty != nullptr) USER_PRINTLN(F("MatrixPanel_I2S_DMA LEDS dirty bit optimization enabled."));
if ((_ledBuffer != nullptr) || (_ledsDirty != nullptr)) {
USER_PRINT(F("MatrixPanel_I2S_DMA LEDS buffer uses "));
USER_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0));
USER_PRINTLN(F(" bytes."));
}
USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap());
} }
void BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) {
r = R(c); if (!_valid || pix >= _len) return;
g = G(c); // if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
b = B(c);
if(fourScanPanel != nullptr) { if (_ledBuffer) {
x = pix % fourScanPanel->width(); CRGB fastled_col = CRGB(c);
y = floor(pix / fourScanPanel->width()); if (_ledBuffer[pix] != fastled_col) {
fourScanPanel->drawPixelRGB888(x, y, r, g, b); _ledBuffer[pix] = fastled_col;
setBitInArray(_ledsDirty, pix, true); // flag pixel as "dirty"
}
} }
else { else {
x = pix % display->width(); if ((c == BLACK) && (getBitFromArray(_ledsDirty, pix) == false)) return; // ignore black if pixel is already black
y = floor(pix / display->width()); setBitInArray(_ledsDirty, pix, c != BLACK); // dirty = true means "color is not BLACK"
display->drawPixelRGB888(x, y, r, g, b);
#ifndef NO_CIE1931
c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction
#endif
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
if(fourScanPanel != nullptr) {
int width = _panelWidth;
int x = pix % width;
int y = pix / width;
fourScanPanel->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
} else {
int width = _panelWidth;
int x = pix % width;
int y = pix / width;
display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
}
} }
} }
uint32_t BusHub75Matrix::getPixelColor(uint16_t pix) const {
if (!_valid || pix >= _len) return BLACK;
if (_ledBuffer)
return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // scale8() is needed to mimic NeoPixelBus, which returns scaled-down colours
else
return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not
}
uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix) const {
if (!_valid || pix >= _len) return BLACK;
if (_ledBuffer)
return uint32_t(_ledBuffer[pix]) & 0x00FFFFFF;
else
return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not
}
void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) {
this->display->setBrightness(b); _bri = b;
// if (_bri > 238) _bri=238; // not strictly needed. Enable this line if you see glitches at highest brightness.
display->setBrightness(_bri);
}
void __attribute__((hot)) BusHub75Matrix::show(void) {
if (!_valid) return;
display->setBrightness(_bri);
if (_ledBuffer) {
// write out buffered LEDs
bool isFourScan = (fourScanPanel != nullptr);
//if (isFourScan) fourScanPanel->setRotation(0);
unsigned height = isFourScan ? fourScanPanel->height() : display->height();
unsigned width = _panelWidth;
//while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker.
size_t pix = 0; // running pixel index
for (int y=0; y<height; y++) for (int x=0; x<width; x++) {
if (getBitFromArray(_ledsDirty, pix) == true) { // only repaint the "dirty" pixels
uint32_t c = uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; // get RGB color, removing FastLED "alpha" component
#ifndef NO_CIE1931
c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction
#endif
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
if (isFourScan) fourScanPanel->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
else display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
}
pix ++;
}
setBitArray(_ledsDirty, _len, false); // buffer shown - reset all dirty bits
}
if(mxconfig.double_buff) {
display->flipDMABuffer(); // Show the back buffer, set current output buffer to the back (i.e. no longer being sent to LED panels)
// while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker.
display->clearScreen(); // Now clear the back-buffer
setBitArray(_ledsDirty, _len, false); // dislay buffer is blank - reset all dirty bits
}
}
void BusHub75Matrix::cleanup() {
if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black)
_valid = false;
_panelWidth = 0;
deallocatePins();
USER_PRINTLN("HUB75 output ended.");
//if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior
delete display;
display = nullptr;
fourScanPanel = nullptr;
if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr;
if (_ledsDirty != nullptr) free(_ledsDirty); _ledsDirty = nullptr;
} }
void BusHub75Matrix::deallocatePins() { void BusHub75Matrix::deallocatePins() {
@@ -766,7 +1022,7 @@ int BusManager::add(BusConfig &bc) {
busses[numBusses] = new BusHub75Matrix(bc); busses[numBusses] = new BusHub75Matrix(bc);
USER_PRINTLN("[BusHub75Matrix] "); USER_PRINTLN("[BusHub75Matrix] ");
#else #else
USER_PRINTLN("[unsupported! BusHub75Matrix] "); USER_PRINTLN("[unsupported! BusHub75Matrix - add flag -D WLED_ENABLE_HUB75MATRIX] ");
return -1; return -1;
#endif #endif
} else if (IS_DIGITAL(bc.type)) { } else if (IS_DIGITAL(bc.type)) {
@@ -808,7 +1064,7 @@ void BusManager::setStatusPixel(uint32_t c) {
} }
} }
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { void IRAM_ATTR __attribute__((hot)) BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) {
if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) {
// WLEDMM same bus as last time - no need to search again // WLEDMM same bus as last time - no need to search again
lastBus->setPixelColor(pix - laststart, c); lastBus->setPixelColor(pix - laststart, c);
@@ -836,7 +1092,7 @@ void BusManager::setBrightness(uint8_t b, bool immediate) {
} }
} }
void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { void __attribute__((cold)) BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
if (cct > 255) cct = 255; if (cct > 255) cct = 255;
if (cct >= 0) { if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255 //if white balance correction allowed, save as kelvin value instead of 0-255
@@ -845,7 +1101,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
Bus::setCCT(cct); Bus::setCCT(cct);
} }
uint32_t IRAM_ATTR BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR
if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) {
// WLEDMM same bus as last time - no need to search again // WLEDMM same bus as last time - no need to search again
return lastBus->getPixelColor(pix - laststart); return lastBus->getPixelColor(pix - laststart);
@@ -866,20 +1122,41 @@ uint32_t IRAM_ATTR BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM
return 0; return 0;
} }
bool BusManager::canAllShow() { uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColorRestored(uint_fast16_t pix) { // WLEDMM uses bus::getPixelColorRestored()
if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) {
// WLEDMM same bus as last time - no need to search again
return lastBus->getPixelColorRestored(pix - laststart);
}
for (uint_fast8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint_fast16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
else {
// WLEDMM remember last Bus we took
lastBus = b;
laststart = bstart;
lastend = bstart + b->getLength();
return b->getPixelColorRestored(pix - bstart);
}
}
return 0;
}
bool BusManager::canAllShow() const {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
if (!busses[i]->canShow()) return false; if (!busses[i]->canShow()) return false;
} }
return true; return true;
} }
Bus* BusManager::getBus(uint8_t busNr) { Bus* BusManager::getBus(uint8_t busNr) const {
if (busNr >= numBusses) return nullptr; if (busNr >= numBusses) return nullptr;
return busses[busNr]; return busses[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 BusManager::getTotalLength() { uint16_t BusManager::getTotalLength() const {
uint_fast16_t len = 0; uint_fast16_t len = 0;
for (uint_fast8_t i=0; i<numBusses; i++) len += busses[i]->getLength(); // WLEDMM use fast native types for (uint_fast8_t i=0; i<numBusses; i++) len += busses[i]->getLength(); // WLEDMM use fast native types
return len; return len;

View File

@@ -4,6 +4,7 @@
#ifdef WLED_ENABLE_HUB75MATRIX #ifdef WLED_ENABLE_HUB75MATRIX
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h> #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <ESP32-VirtualMatrixPanel-I2S-DMA.h> #include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
//extern volatile bool previousBufferFree; // experimental
#endif #endif
/* /*
* Class for addressing various light types * Class for addressing various light types
@@ -11,6 +12,27 @@
#include "const.h" #include "const.h"
#if !defined(FASTLED_VERSION) // only pull in FastLED if we don't have it yet
#define FASTLED_INTERNAL
#include <FastLED.h>
#endif
//color mangling macros
#if !defined(RGBW32)
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
#define R(c) (byte((c) >> 16))
#define G(c) (byte((c) >> 8))
#define B(c) (byte(c))
#define W(c) (byte((c) >> 24))
#endif
// WLEDMM bitarray utilities
void setBitInArray(uint8_t* byteArray, size_t position, bool value); // set bit
bool getBitFromArray(const uint8_t* byteArray, size_t position) __attribute__((pure)); // get bit value
size_t getBitArrayBytes(size_t num_bits) __attribute__((const)); // number of bytes needed for an array with num_bits bits
void setBitArray(uint8_t* byteArray, size_t numBits, bool value); // set all bits to same value
#define GET_BIT(var,bit) (((var)>>(bit))&0x01) #define GET_BIT(var,bit) (((var)>>(bit))&0x01)
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit))) #define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit)))) #define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
@@ -32,18 +54,18 @@ struct BusConfig {
uint8_t skipAmount; uint8_t skipAmount;
bool refreshReq; bool refreshReq;
uint8_t autoWhite; uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; // WLEDMM warning: this means that BusConfig cannot handle nore than 5 pins per bus!
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) { 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) {
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; count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz;
uint8_t nPins = 1; uint8_t nPins = 1; // default = only one pin (clockless LEDs like WS281x)
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 < (TYPE_NET_DDP_RGB + 16))) nPins = 4; // virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2; else if ((type > 47) && (type < 63)) nPins = 2; // (data + clock / SPI) busses - two pins
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (IS_PWM(type)) nPins = NUM_PWM_PINS(type); // PWM needs 1..5 pins
else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 1; // HUB75 does not use LED pins, but we need to preserve the "chain length" parameter
for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i]; for (uint8_t i = 0; i < min(unsigned(nPins), sizeof(pins)/sizeof(pins[0])); i++) pins[i] = ppins[i]; //softhack007 fix for potential array out-of-bounds access
} }
//validates start and length and extends total if needed //validates start and length and extends total if needed
@@ -112,35 +134,36 @@ class Bus {
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) const { return 0; }
virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; }; virtual uint32_t getPixelColorRestored(uint16_t pix) const { return restore_Color_Lossy(getPixelColor(pix), _bri); } // override in case your bus has a lossless buffer (HUB75, FastLED, Art-Net)
virtual void setBrightness(uint8_t b, bool immediate=false) { _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) const { return 0; }
virtual uint16_t getLength() { return _len; } virtual inline uint16_t getLength() const { return _len; }
virtual void setColorOrder() {} virtual void setColorOrder() {}
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; } virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; } virtual uint8_t skippedLeds() const { return 0; }
virtual uint16_t getFrequency() { return 0U; } virtual uint16_t getFrequency() const { return 0U; }
inline uint16_t getStart() { return _start; } inline uint16_t getStart() const { return _start; }
inline void setStart(uint16_t start) { _start = start; } inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() { return _type; } inline uint8_t getType() const { return _type; }
inline bool isOk() { return _valid; } inline bool isOk() const { return _valid; }
inline bool isOffRefreshRequired() { return _needsRefresh; } inline bool isOffRefreshRequired() const { return _needsRefresh; }
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; } //inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start+_len; } // WLEDMM not used, plus wrong - it does not consider skipped pixels
virtual uint16_t getMaxPixels() { return MAX_LEDS_PER_BUS; }; virtual uint16_t getMaxPixels() const { return MAX_LEDS_PER_BUS; }
virtual bool hasRGB() { virtual bool hasRGB() const {
if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; 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() const { 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 || type == TYPE_UCS8904) return true; // digital types with white channel if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904) 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() const {
if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA || if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA ||
_type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true; _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true;
return false; return false;
@@ -157,10 +180,21 @@ class Bus {
#endif #endif
} }
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; } inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
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; }
inline uint32_t restore_Color_Lossy(uint32_t c, uint8_t restoreBri) const { // shamelessly grabbed from upstream, who grabbed from NPB, who ..
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 slightly improves recovery / stops degradation on re-scale
}
}
return c;
}
bool reversed = false; bool reversed = false;
protected: protected:
@@ -175,7 +209,7 @@ class Bus {
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) const;
}; };
@@ -185,7 +219,7 @@ class BusDigital : public Bus {
inline void show(); inline void show();
bool canShow(); bool canShow() override;
void setBrightness(uint8_t b, bool immediate); void setBrightness(uint8_t b, bool immediate);
@@ -193,25 +227,25 @@ class BusDigital : public Bus {
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) const override;
uint8_t getColorOrder() { uint8_t getColorOrder() const {
return _colorOrder; return _colorOrder;
} }
uint16_t getLength() { uint16_t getLength() const override {
return _len - _skip; return _len - _skip;
} }
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray) const;
void setColorOrder(uint8_t colorOrder); void setColorOrder(uint8_t colorOrder);
uint8_t skippedLeds() { uint8_t skippedLeds() const override {
return _skip; return _skip;
} }
uint16_t getFrequency() { return _frequencykHz; } uint16_t getFrequency() const override { return _frequencykHz; }
void reinit(); void reinit();
@@ -239,13 +273,13 @@ class BusPwm : public Bus {
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
//does no index check //does no index check
uint32_t getPixelColor(uint16_t pix); uint32_t getPixelColor(uint16_t pix) const;
void show(); void show();
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray) const;
uint16_t getFrequency() { return _frequency; } uint16_t getFrequency() const override { return _frequency; }
void cleanup() { void cleanup() {
deallocatePins(); deallocatePins();
@@ -273,11 +307,12 @@ class BusOnOff : public Bus {
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) const;
uint32_t getPixelColorRestored(uint16_t pix) const override { return getPixelColor(pix);} // WLEDMM BusOnOff ignores brightness
void show(); void show();
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray) const;
void cleanup() { void cleanup() {
pinManager.deallocatePin(_pin, PinOwner::BusOnOff); pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
@@ -297,24 +332,25 @@ class BusNetwork : public Bus {
public: public:
BusNetwork(BusConfig &bc); BusNetwork(BusConfig &bc);
uint16_t getMaxPixels() override { return 4096; }; uint16_t getMaxPixels() const override { return 4096; };
bool hasRGB() { return true; } bool hasRGB() const { return true; }
bool hasWhite() { return _rgbw; } bool hasWhite() const { return _rgbw; }
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t __attribute__((pure)) getPixelColor(uint16_t pix); // WLEDMM attribute added uint32_t __attribute__((pure)) getPixelColor(uint16_t pix) const; // WLEDMM attribute added
uint32_t __attribute__((pure)) getPixelColorRestored(uint16_t pix) const override { return getPixelColor(pix);} // WLEDMM BusNetwork ignores brightness
void show(); void show();
bool canShow() { bool canShow() override {
// this should be a return value from UDP routine if it is still sending data out // this should be a return value from UDP routine if it is still sending data out
return !_broadcastLock; return !_broadcastLock;
} }
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray) const;
uint16_t getLength() { uint16_t getLength() const override {
return _len; return _len;
} }
@@ -338,36 +374,27 @@ class BusHub75Matrix : public Bus {
public: public:
BusHub75Matrix(BusConfig &bc); BusHub75Matrix(BusConfig &bc);
uint16_t getMaxPixels() override { return 4096; }; uint16_t getMaxPixels() const override { return MAX_LEDS; };
bool hasRGB() { return true; } bool hasRGB() const override { return true; }
bool hasWhite() { return false; } bool hasWhite() const override { return false; }
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override;
uint32_t getPixelColorRestored(uint16_t pix) const override; // lossless getPixelColor supported
void show() { void show(void) override;
if(mxconfig.double_buff) {
display->flipDMABuffer(); // Show the back buffer, set currently output buffer to the back (i.e. no longer being sent to LED panels)
display->clearScreen(); // Now clear the back-buffer
}
}
void setBrightness(uint8_t b, bool immediate); void setBrightness(uint8_t b, bool immediate) override;
uint8_t getPins(uint8_t* pinArray) { uint8_t getPins(uint8_t* pinArray) const override {
pinArray[0] = mxconfig.chain_length; pinArray[0] = mxconfig.chain_length;
return 1; return 1;
} // Fake value due to keep finaliseInit happy } // Fake value due to keep finaliseInit happy
void deallocatePins(); void deallocatePins();
void cleanup() { void cleanup(void) override;
deallocatePins();
fourScanPanel = nullptr;
// delete fourScanPanel;
delete display;
_valid = false;
}
~BusHub75Matrix() { ~BusHub75Matrix() {
cleanup(); cleanup();
@@ -377,8 +404,9 @@ class BusHub75Matrix : public Bus {
MatrixPanel_I2S_DMA *display = nullptr; MatrixPanel_I2S_DMA *display = nullptr;
VirtualMatrixPanel *fourScanPanel = nullptr; VirtualMatrixPanel *fourScanPanel = nullptr;
HUB75_I2S_CFG mxconfig; HUB75_I2S_CFG mxconfig;
uint8_t r, g, b, x, y; unsigned _panelWidth = 0;
CRGB *_ledBuffer = nullptr;
byte *_ledsDirty = nullptr;
}; };
#endif #endif
@@ -387,7 +415,7 @@ class BusManager {
BusManager() {}; BusManager() {};
//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) __attribute__((pure));
int add(BusConfig &bc); int add(BusConfig &bc);
@@ -405,13 +433,14 @@ class BusManager {
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t pix); // WLEDMM attribute added uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t pix); // WLEDMM attribute added
uint32_t __attribute__((pure)) getPixelColorRestored(uint_fast16_t pix); // WLEDMM
bool canAllShow(); bool canAllShow() const;
Bus* getBus(uint8_t busNr); Bus* getBus(uint8_t busNr) const;
//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() const;
inline void updateColorOrderMap(const ColorOrderMap &com) { inline void updateColorOrderMap(const ColorOrderMap &com) {
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
@@ -421,7 +450,7 @@ class BusManager {
return colorOrderMap; return colorOrderMap;
} }
inline uint8_t getNumBusses() { inline uint8_t getNumBusses() const {
return numBusses; return numBusses;
} }
@@ -434,7 +463,7 @@ class BusManager {
unsigned laststart = 0; unsigned laststart = 0;
unsigned lastend = 0; unsigned lastend = 0;
inline uint8_t getNumVirtualBusses() { inline uint8_t getNumVirtualBusses() const {
int j = 0; int j = 0;
for (int i=0; i<numBusses; i++) if (busses[i]->getType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++; for (int i=0; i<numBusses; i++) if (busses[i]->getType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++;
return j; return j;

View File

@@ -7,7 +7,7 @@
/* /*
* color blend function * color blend function
*/ */
IRAM_ATTR_YN uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) { IRAM_ATTR_YN __attribute__((hot)) uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) {
if(blend == 0) return color1; if(blend == 0) return color1;
if (color1 == color2) return color1; // WLEDMM shortcut if (color1 == color2) return color1; // WLEDMM shortcut
const uint_fast16_t blendmax = b16 ? 0xFFFF : 0xFF; const uint_fast16_t blendmax = b16 ? 0xFFFF : 0xFF;
@@ -71,7 +71,7 @@ IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM
* if using "video" method the resulting color will never become black unless it is already black * if using "video" method the resulting color will never become black unless it is already black
*/ */
IRAM_ATTR_YN uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) IRAM_ATTR_YN __attribute__((hot)) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
{ {
if (amount == 0) return 0; // WLEDMM shortcut if (amount == 0) return 0; // WLEDMM shortcut
@@ -297,7 +297,7 @@ static float maxf (float v, float w) // WLEDMM better use standard library fmax
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) // adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
// called from bus manager when color correction is enabled! // called from bus manager when color correction is enabled!
uint32_t IRAM_ATTR_YN colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) // WLEDMM: IRAM_ATTR_YN uint32_t __attribute__((hot)) IRAM_ATTR_YN colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) // WLEDMM: IRAM_ATTR_YN
{ {
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
static byte correctionRGB[4] = {0,0,0,0}; static byte correctionRGB[4] = {0,0,0,0};
@@ -406,13 +406,19 @@ static void calcInvGammaTable(float gamma)
gammaTinv[i] = (int)(powf((float)i / 255.0f, gammaInv) * 255.0f + 0.5f); gammaTinv[i] = (int)(powf((float)i / 255.0f, gammaInv) * 255.0f + 0.5f);
} }
} }
uint8_t unGamma8(uint8_t value) { uint8_t __attribute__((hot)) unGamma8(uint8_t value) {
//if (!gammaCorrectCol || (value == 0) || (value == 255)) return value; //if (!gammaCorrectCol || (value == 0) || (value == 255)) return value;
if ((value == 0) || (value == 255)) return value; if ((value == 0) || (value == 255)) return value;
if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return value; if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return value;
if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal); if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal);
return gammaTinv[value]; return gammaTinv[value];
} }
uint32_t __attribute__((hot)) unGamma24(uint32_t c) {
if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return c;
if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal);
return RGBW32(gammaTinv[R(c)], gammaTinv[G(c)], gammaTinv[B(c)], W(c));
}
// wleDMM end // wleDMM end
uint8_t gamma8_cal(uint8_t b, float gamma) uint8_t gamma8_cal(uint8_t b, float gamma)
@@ -432,13 +438,13 @@ void calcGammaTable(float gamma)
} }
// used for individual channel or brightness gamma correction // used for individual channel or brightness gamma correction
IRAM_ATTR_YN uint8_t gamma8(uint8_t b) // WLEDMM added IRAM_ATTR_YN IRAM_ATTR_YN __attribute__((hot)) uint8_t gamma8(uint8_t b) // WLEDMM added IRAM_ATTR_YN
{ {
return gammaT[b]; return gammaT[b];
} }
// used for color gamma correction // used for color gamma correction
uint32_t gamma32(uint32_t color) uint32_t __attribute__((hot)) gamma32(uint32_t color)
{ {
if (!gammaCorrectCol) return color; if (!gammaCorrectCol) return color;
uint8_t w = W(color); uint8_t w = W(color);

View File

@@ -245,7 +245,9 @@
#define TYPE_P9813 53 #define TYPE_P9813 53
#define TYPE_LPD6803 54 #define TYPE_LPD6803 54
// WLEDMM additional types
#define TYPE_HUB75MATRIX 100 // 100 - 110 #define TYPE_HUB75MATRIX 100 // 100 - 110
// WLEDMM caution - do not use bus types > 127
//Network types (master broadcast) (80-95) //Network types (master broadcast) (80-95)
#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus) #define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus)
@@ -257,6 +259,8 @@
#define IS_PWM(t) ((t) > 40 && (t) < 46) #define IS_PWM(t) ((t) > 40 && (t) < 46)
#define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only #define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only
#define IS_2PIN(t) ((t) > 47) #define IS_2PIN(t) ((t) > 47)
#define IS_VIRTUAL(t) ( ((t) <= TYPE_RESERVED) || (((t) >= TYPE_NET_DDP_RGB) && ((t) < (TYPE_NET_DDP_RGB + 16))) ) // WLEDMM 80..95 are network "virtual" busses
#define EXCLUDE_FROM_ABL(t) ( IS_VIRTUAL(t) || ( (t) >= (TYPE_HUB75MATRIX) && (t) < (TYPE_HUB75MATRIX + 10))) // WLEDMM do not apply auto-brightness-limiter on these bus types
//Color orders //Color orders
#define COL_ORDER_GRB 0 //GRB(w),defaut #define COL_ORDER_GRB 0 //GRB(w),defaut
@@ -352,6 +356,7 @@
#define ERR_LOW_SEG_MEM 34 // WLEDMM: low memory (segment data RAM) #define ERR_LOW_SEG_MEM 34 // WLEDMM: low memory (segment data RAM)
#define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws) #define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws)
#define ERR_LOW_AJAX_MEM 36 // WLEDMM: low memory (oappend) #define ERR_LOW_AJAX_MEM 36 // WLEDMM: low memory (oappend)
#define ERR_LOW_BUF 37 // WLEDMM: low memory (LED buffer from allocLEDs)
// Timer mode types // Timer mode types
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness
@@ -425,13 +430,15 @@
#endif #endif
#endif #endif
#ifdef WLED_USE_ETHERNET #ifndef E131_MAX_UNIVERSE_COUNT
#define E131_MAX_UNIVERSE_COUNT 20 #ifdef WLED_USE_ETHERNET
#else #define E131_MAX_UNIVERSE_COUNT 20
#ifdef ESP8266
#define E131_MAX_UNIVERSE_COUNT 9
#else #else
#define E131_MAX_UNIVERSE_COUNT 12 #ifdef ESP8266
#define E131_MAX_UNIVERSE_COUNT 9
#else
#define E131_MAX_UNIVERSE_COUNT 12
#endif
#endif #endif
#endif #endif
@@ -478,7 +485,9 @@
#endif #endif
//#define MIN_HEAP_SIZE (8k for AsyncWebServer) //#define MIN_HEAP_SIZE (8k for AsyncWebServer)
#if !defined(MIN_HEAP_SIZE)
#define MIN_HEAP_SIZE 8192 #define MIN_HEAP_SIZE 8192
#endif
// Maximum size of node map (list of other WLED instances) // Maximum size of node map (list of other WLED instances)
#ifdef ESP8266 #ifdef ESP8266

View File

@@ -27,7 +27,7 @@ var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false},
labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:true, labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:true,
css:true, hdays:false, fxdef:true} //WLEDMM segexp true as default css:true, hdays:false, fxdef:true, fxdef2:false} //WLEDMM segexp true as default, fxdef2 added
}; };
var hol = [ var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
@@ -696,7 +696,7 @@ function populateInfo(i)
if (i.ver.includes("0.14.0-b15.22")) vcn = "Lupo"; if (i.ver.includes("0.14.0-b15.22")) vcn = "Lupo";
if (i.ver.includes("0.14.1-b3")) vcn = "Fried Chicken"; // final line of "One Vision" by Queen if (i.ver.includes("0.14.1-b3")) vcn = "Fried Chicken"; // final line of "One Vision" by Queen
if (i.ver.includes("0.14.3-b")) vcn = "Fried Chicken"; if (i.ver.includes("0.14.3-b")) vcn = "Fried Chicken";
cn += `v${i.ver} &nbsp;<i>"${vcn}"</i><p>(WLEDMM_${i.ver} ${i.rel}.bin)</p><p><em>build ${i.vid}</em></p><table> cn += `v${i.ver} &nbsp;<i>"${vcn}"</i><p>(WLEDMM ${i.rel}.bin)</p><p><em>build ${i.vid}</em></p><table>
${urows} ${urows}
${urows===""?'':'<tr><td colspan=2><hr style="height:1px;border-width:0;color:SeaGreen;background-color:Seagreen"></td></tr>'} ${urows===""?'':'<tr><td colspan=2><hr style="height:1px;border-width:0;color:SeaGreen;background-color:Seagreen"></td></tr>'}
${i.opt&0x100?inforow("Net Print ☾","<button class=\"btn btn-xs\" onclick=\"requestJson({'netDebug':"+(i.opt&0x0080?"false":"true")+"});\"><i class=\"icons "+(i.opt&0x0080?"on":"off")+"\">&#xe08f;</i></button>"):''} ${i.opt&0x100?inforow("Net Print ☾","<button class=\"btn btn-xs\" onclick=\"requestJson({'netDebug':"+(i.opt&0x0080?"false":"true")+"});\"><i class=\"icons "+(i.opt&0x0080?"on":"off")+"\">&#xe08f;</i></button>"):''}
@@ -2008,6 +2008,9 @@ function readState(s,command=false)
case 36: case 36:
errstr = "Low Memory (oappend buffer)."; errstr = "Low Memory (oappend buffer).";
break; break;
case 37:
errstr = "no memory for LEDs buffer.";
break;
} }
showToast('Error ' + s.error + ": " + errstr, true); showToast('Error ' + s.error + ": " + errstr, true);
} }
@@ -2853,7 +2856,7 @@ function setFX(ind = null)
} else { } else {
d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true; d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true;
} }
var obj = {"seg": {"fx": parseInt(ind), "fxdef": cfg.comp.fxdef}}; // fxdef sets effect parameters to default values var obj = {"seg": {"fx": parseInt(ind), "fxdef": cfg.comp.fxdef, "fxdef2": cfg.comp.fxdef2}}; // fxdef sets effect parameters to default values; WLEDMM fxdef2 only set slider defaults
requestJson(obj); requestJson(obj);
} }

View File

@@ -250,7 +250,7 @@
if (s+c > sLC) sLC = s+c; //update total count if (s+c > sLC) sLC = s+c; //update total count
if(c>maxLC)maxLC=c; //max per output if(c>maxLC)maxLC=c; //max per output
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs if ((t<128) && ((t<80) || ((t>99)))) sPC+=c; //virtual out busses do not count towards physical LEDs //WLEDMM include HUB75 (>=100)
} // increase led count } // increase led count
continue; continue;
} }

View File

@@ -26,7 +26,8 @@
"segexp" : "Always expand first segment", "segexp" : "Always expand first segment",
"css": "Enable custom CSS", "css": "Enable custom CSS",
"hdays": "Enable custom Holidays list", "hdays": "Enable custom Holidays list",
"fxdef": "Use effect default parameters" "fxdef": "Use effect default parameters",
"fxdef2": "Don't use effect palette and segment parameters"
}, },
"theme":{ "theme":{
"alpha": { "alpha": {

View File

@@ -1135,7 +1135,7 @@ function setSegBri(s)
function setEffect(ind = 0) function setEffect(ind = 0)
{ {
tglFxDropdown(); tglFxDropdown();
var obj = {"seg": {"fx": parseInt(ind), "fxdef":true}}; // fxdef sets effect parameters to default values, TODO add client setting var obj = {"seg": {"fx": parseInt(ind), "fxdef":true, "fxdef2":false}}; // fxdef sets effect parameters to default values, TODO add client setting
requestJson(obj); requestJson(obj);
} }

View File

@@ -187,6 +187,9 @@ void DMXInput::updateInternal()
unsigned long now = millis(); unsigned long now = millis();
if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK)) { if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK)) {
if (!packet.err) { if (!packet.err) {
if(!connected) {
USER_PRINTLN("DMX Input - connected");
}
connected = true; connected = true;
identify = isIdentifyOn(); identify = isIdentifyOn();
if (!packet.is_rdm) { if (!packet.is_rdm) {
@@ -199,6 +202,9 @@ void DMXInput::updateInternal()
} }
} }
else { else {
if(connected) {
USER_PRINTLN("DMX Input - disconnected");
}
connected = false; connected = false;
} }
} }

View File

@@ -69,6 +69,7 @@ void calcGammaTable(float gamma);
uint8_t __attribute__((pure)) gamma8(uint8_t b); // WLEDMM: added attribute pure uint8_t __attribute__((pure)) gamma8(uint8_t b); // WLEDMM: added attribute pure
uint32_t __attribute__((pure)) gamma32(uint32_t); // WLEDMM: added attribute pure uint32_t __attribute__((pure)) gamma32(uint32_t); // WLEDMM: added attribute pure
uint8_t unGamma8(uint8_t value); // WLEDMM revert gamma correction uint8_t unGamma8(uint8_t value); // WLEDMM revert gamma correction
uint32_t unGamma24(uint32_t c); // WLEDMM for 24bit color (white left as-is)
//dmx_output.cpp //dmx_output.cpp
void initDMXOutput(); void initDMXOutput();
@@ -249,7 +250,7 @@ void refreshNodeList();
void sendSysInfoUDP(); void sendSysInfoUDP();
//network.cpp //network.cpp
int getSignalQuality(int rssi); int getSignalQuality(int rssi) __attribute__((const));
void WiFiEvent(WiFiEvent_t event); void WiFiEvent(WiFiEvent_t event);
//um_manager.cpp //um_manager.cpp
@@ -298,6 +299,7 @@ class Usermod {
virtual ~Usermod() { if (um_data) delete um_data; } virtual ~Usermod() { if (um_data) delete um_data; }
virtual void setup() = 0; // pure virtual, has to be overriden virtual void setup() = 0; // pure virtual, has to be overriden
virtual void loop() = 0; // pure virtual, has to be overriden virtual void loop() = 0; // pure virtual, has to be overriden
virtual void loop2() {} // WLEDMM called just before effects will be processed
virtual void handleOverlayDraw() {} // called after all effects have been processed, just before strip.show() virtual void handleOverlayDraw() {} // called after all effects have been processed, just before strip.show()
virtual bool handleButton(uint8_t b) { return false; } // button overrides are possible here virtual bool handleButton(uint8_t b) { return false; } // button overrides are possible here
virtual bool getUMData(um_data_t **data) { if (data) *data = nullptr; return false; }; // usermod data exchange [see examples for audio effects] virtual bool getUMData(um_data_t **data) { if (data) *data = nullptr; return false; }; // usermod data exchange [see examples for audio effects]
@@ -324,10 +326,11 @@ class Usermod {
class UsermodManager { class UsermodManager {
private: private:
Usermod* ums[WLED_MAX_USERMODS]; Usermod* ums[WLED_MAX_USERMODS];
byte numMods = 0; unsigned numMods = 0;
public: public:
void loop(); void loop();
void loop2(); // WLEDMM loop just before drawing effects (presets and everything already handled)
void handleOverlayDraw(); void handleOverlayDraw();
bool handleButton(uint8_t b); bool handleButton(uint8_t b);
bool getUMData(um_data_t **um_data, uint8_t mod_id = USERMOD_ID_RESERVED); // USERMOD_ID_RESERVED will poll all usermods bool getUMData(um_data_t **um_data, uint8_t mod_id = USERMOD_ID_RESERVED); // USERMOD_ID_RESERVED will poll all usermods
@@ -368,7 +371,7 @@ bool oappendi(int i); // append new number to temp buffer efficiently
void sappend(char stype, const char* key, int val); void sappend(char stype, const char* key, int val);
void sappends(char stype, const char* key, char* val); void sappends(char stype, const char* key, char* val);
void prepareHostname(char* hostname); void prepareHostname(char* hostname);
bool isAsterisksOnly(const char* str, byte maxLen); bool isAsterisksOnly(const char* str, byte maxLen) __attribute__((pure));
bool requestJSONBufferLock(uint8_t module=255); bool requestJSONBufferLock(uint8_t module=255);
void releaseJSONBufferLock(); void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
@@ -409,13 +412,14 @@ void clearEEPROM();
//wled_math.cpp //wled_math.cpp
#ifndef WLED_USE_REAL_MATH #ifndef WLED_USE_REAL_MATH
template <typename T> T atan_t(T x); template <typename T> T atan_t(T x);
float cos_t(float phi); float cos_t(float phi) __attribute__((const));
float sin_t(float x); float sin_t(float x) __attribute__((const));
float tan_t(float x); float tan_t(float x) __attribute__((const));
float acos_t(float x); float acos_t(float x);
float asin_t(float x); float asin_t(float x);
float floor_t(float x); float atan_t(float x) __attribute__((const));
float fmod_t(float num, float denom); float floor_t(float x) __attribute__((const));
float fmod_t(float num, float denom) __attribute__((const));
#else #else
#include <math.h> // WLEDMM use "float" variants #include <math.h> // WLEDMM use "float" variants
#define sin_t sinf #define sin_t sinf

View File

@@ -211,7 +211,7 @@ void sendImprovInfoResponse() {
//Use serverDescription if it has been changed from the default "WLED", else mDNS name //Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
char vString[32]; char vString[32];
snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b32.40/%i"),VERSION); snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b32.41/%i"),VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

@@ -302,7 +302,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
// end fix // end fix
if (getVal(elem["fx"], &fx, 0, last)) { //load effect ('r' random, '~' inc/dec, 0-255 exact value, 5~10r pick random between 5 & 10) if (getVal(elem["fx"], &fx, 0, last)) { //load effect ('r' random, '~' inc/dec, 0-255 exact value, 5~10r pick random between 5 & 10)
if (!presetId && currentPlaylist>=0) unloadPlaylist(); if (!presetId && currentPlaylist>=0) unloadPlaylist();
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")]); if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")], elem[F("fxdef2")]); // WLEDMM fxdef2 added
} }
//getVal also supports inc/decrementing and random //getVal also supports inc/decrementing and random
@@ -904,8 +904,8 @@ void serializeInfo(JsonObject root)
JsonObject leds = root.createNestedObject("leds"); JsonObject leds = root.createNestedObject("leds");
leds[F("count")] = strip.getLengthTotal(); leds[F("count")] = strip.getLengthTotal();
leds[F("countP")] = strip.getLengthPhysical(); //WLEDMM leds[F("countP")] = strip.getLengthPhysical2(); //WLEDMM - getLengthPhysical plus plysical busses not supporting ABL (i.e. HUB75)
leds[F("pwr")] = strip.currentMilliamps; leds[F("pwr")] = strip.currentMilliamps > 100 ? strip.currentMilliamps : 0; // WLEDMM show "not calculated" for HUB75, or when all LEDs are out
leds["fps"] = strip.getFps(); leds["fps"] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds[F("maxseg")] = strip.getMaxSegments(); leds[F("maxseg")] = strip.getMaxSegments();

View File

@@ -68,7 +68,7 @@ void toggleOnOff()
//scales the brightness with the briMultiplier factor //scales the brightness with the briMultiplier factor
IRAM_ATTR_YN byte scaledBri(byte in) // WLEDMM added IRAM_ATTR_YN IRAM_ATTR_YN __attribute__((hot)) byte scaledBri(byte in) // WLEDMM added IRAM_ATTR_YN
{ {
if (briMultiplier == 100) return(in); // WLEDMM shortcut if (briMultiplier == 100) return(in); // WLEDMM shortcut
uint_fast16_t val = ((uint_fast16_t)in*(uint_fast16_t)briMultiplier)/100; // WLEDMM uint_fast16_t val = ((uint_fast16_t)in*(uint_fast16_t)briMultiplier)/100; // WLEDMM

View File

@@ -89,7 +89,7 @@ const ethernet_settings ethernetBoards[] = {
// ESP32-ETHERNET-KIT-VE // ESP32-ETHERNET-KIT-VE
{ {
0, // eth_address, 1, // eth_address, WLED-MM: Changed from 0 to 1 based on not working with 0 on same devkit.
5, // eth_power, 5, // eth_power,
23, // eth_mdc, 23, // eth_mdc,
18, // eth_mdio, 18, // eth_mdio,

View File

@@ -113,13 +113,18 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#if defined(CONFIG_IDF_TARGET_ESP32S3) #if defined(CONFIG_IDF_TARGET_ESP32S3)
// ESP32-S3 // ESP32-S3
if (gpio > 18 && gpio < 21) return (F("USB (CDC) / JTAG")); if (gpio > 18 && gpio < 21) return (F("USB (CDC) or JTAG"));
#if !defined(BOARD_HAS_PSRAM) #if CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM)
if (gpio > 32 && gpio < 38) return (F("(optional) Octal Flash or PSRAM"));
#else
if (gpio > 32 && gpio < 38) return (F("(reserved) Octal PSRAM or Octal Flash")); if (gpio > 32 && gpio < 38) return (F("(reserved) Octal PSRAM or Octal Flash"));
#endif #endif
//if (gpio == 0 || gpio == 3 || gpio == 45 || gpio == 46) return (F("(strapping pin)")); //if (gpio == 0 || gpio == 3 || gpio == 45 || gpio == 46) return (F("(strapping pin)"));
#ifdef ARDUINO_TTGO_T7_S3
// experimental: a few special pins of the T7-S3 board
if (gpio == 2) return (F("(reserved) _VBAT voltage monitoring"));
if (gpio == 17) return (F("onboard LED"));
//if (gpio == 3) return (F("(cross-connected to pin 3-1)")); // WLEDMM experimental
//if (gpio == 12) return (F("(cross-connected to pin 12-1)")); // WLEDMM experimental
#endif
#elif defined(CONFIG_IDF_TARGET_ESP32S2) #elif defined(CONFIG_IDF_TARGET_ESP32S2)
// ESP32-S2 // ESP32-S2
@@ -129,7 +134,7 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in
#elif defined(CONFIG_IDF_TARGET_ESP32C3) #elif defined(CONFIG_IDF_TARGET_ESP32C3)
// ESP32-C3 // ESP32-C3
if (gpio > 17 && gpio < 20) return (F("USB (CDC) / JTAG")); if (gpio > 17 && gpio < 20) return (F("USB (CDC) or JTAG"));
//if (gpio == 2 || gpio == 8 || gpio == 9) return (F("(strapping pin)")); //if (gpio == 2 || gpio == 8 || gpio == 9) return (F("(strapping pin)"));
#else #else
@@ -723,19 +728,25 @@ bool PinManagerClass::joinWire(int8_t pinSDA, int8_t pinSCL) {
*/ */
// Check if supplied GPIO is ok to use // Check if supplied GPIO is ok to use
bool PinManagerClass::isPinOk(byte gpio, bool output) bool PinManagerClass::isPinOk(byte gpio, bool output) const
{ {
#ifdef ESP32 #ifdef ESP32
if (digitalPinIsValid(gpio)) { if (digitalPinIsValid(gpio)) {
#if defined(CONFIG_IDF_TARGET_ESP32C3) #if defined(CONFIG_IDF_TARGET_ESP32C3)
// strapping pins: 2, 8, & 9 // strapping pins: 2, 8, & 9
if (gpio > 11 && gpio < 18) return false; // 11-17 SPI FLASH if (gpio > 11 && gpio < 18) return false; // 11-17 SPI FLASH
#if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1
if (gpio > 17 && gpio < 20) return false; // 18-19 USB-JTAG if (gpio > 17 && gpio < 20) return false; // 18-19 USB-JTAG
#endif
#elif defined(CONFIG_IDF_TARGET_ESP32S3) #elif defined(CONFIG_IDF_TARGET_ESP32S3)
// 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board. // 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board.
#if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1
if (gpio > 18 && gpio < 21) return false; // 19 + 20 = USB-JTAG. Not recommended for other uses. if (gpio > 18 && gpio < 21) return false; // 19 + 20 = USB-JTAG. Not recommended for other uses.
#endif
if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH
//if (gpio > 32 && gpio < 38) return false; // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM // #if CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM)
// if (gpio > 32 && gpio < 38) return !psramFound(); // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM
// #endif
// 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board. // 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board.
#elif defined(CONFIG_IDF_TARGET_ESP32S2) #elif defined(CONFIG_IDF_TARGET_ESP32S2)
// strapping pins: 0, 45 & 46 // strapping pins: 0, 45 & 46
@@ -757,7 +768,7 @@ bool PinManagerClass::isPinOk(byte gpio, bool output)
return false; return false;
} }
PinOwner PinManagerClass::getPinOwner(byte gpio) { PinOwner PinManagerClass::getPinOwner(byte gpio) const {
if (gpio >= WLED_NUM_PINS) return PinOwner::None; // catch error case, to avoid array out-of-bounds access if (gpio >= WLED_NUM_PINS) return PinOwner::None; // catch error case, to avoid array out-of-bounds access
if (!isPinOk(gpio, false)) return PinOwner::None; if (!isPinOk(gpio, false)) return PinOwner::None;
return ownerTag[gpio]; return ownerTag[gpio];

View File

@@ -125,9 +125,9 @@ class PinManagerClass {
// will return true for reserved pins // will return true for reserved pins
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
// will return false for reserved pins // will return false for reserved pins
bool isPinOk(byte gpio, bool output = true); bool isPinOk(byte gpio, bool output = true) const;
PinOwner getPinOwner(byte gpio); PinOwner getPinOwner(byte gpio) const;
// WLEDMM begin // WLEDMM begin
String getOwnerText(PinOwner tag); // WLEDMM - return PIN owner tag as text String getOwnerText(PinOwner tag); // WLEDMM - return PIN owner tag as text

View File

@@ -1062,7 +1062,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all
if (fxModeChanged) seg.setMode(effectIn, req.indexOf(F("FXD="))>0); // apply defaults if FXD= is specified if (fxModeChanged) seg.setMode(effectIn, req.indexOf(F("FXD="))>0, req.indexOf(F("FXD2="))>0); // apply defaults if FXD= is specified
if (speedChanged) seg.speed = speedIn; if (speedChanged) seg.speed = speedIn;
if (intensityChanged) seg.intensity = intensityIn; if (intensityChanged) seg.intensity = intensityIn;
if (paletteChanged) seg.setPalette(paletteIn); if (paletteChanged) seg.setPalette(paletteIn);

View File

@@ -116,7 +116,7 @@ char* dayShortStr(uint8_t day);
/* low level functions to convert to and from system time */ /* low level functions to convert to and from system time */
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements void breakTime(time_t time, tmElements_t &tm); // break time_t into elements
time_t makeTime(tmElements_t &tm); // convert time elements into time_t time_t makeTime(tmElements_t &tm) __attribute__((pure)); // convert time elements into time_t
} // extern "C++" } // extern "C++"
#endif // __cplusplus #endif // __cplusplus

View File

@@ -4,49 +4,50 @@
*/ */
//Usermod Manager internals //Usermod Manager internals
void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); } void UsermodManager::setup() { for (unsigned i = 0; i < numMods; i++) ums[i]->setup(); }
void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); } void UsermodManager::connected() { for (unsigned i = 0; i < numMods; i++) ums[i]->connected(); }
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); } void UsermodManager::loop() { for (unsigned i = 0; i < numMods; i++) ums[i]->loop(); }
void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); } void UsermodManager::loop2() { for (unsigned i = 0; i < numMods; i++) ums[i]->loop2(); }
// void UsermodManager::appendConfigData() { for (byte i = 0; i < numMods; i++) ums[i]->appendConfigData(); } //WLEDMM not used void UsermodManager::handleOverlayDraw() { for (unsigned i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); }
// void UsermodManager::appendConfigData() { for (unsigned i = 0; i < numMods; i++) ums[i]->appendConfigData(); } //WLEDMM not used
bool UsermodManager::handleButton(uint8_t b) { bool UsermodManager::handleButton(uint8_t b) {
bool overrideIO = false; bool overrideIO = false;
for (byte i = 0; i < numMods; i++) { for (unsigned i = 0; i < numMods; i++) {
if (ums[i]->handleButton(b)) overrideIO = true; if (ums[i]->handleButton(b)) overrideIO = true;
} }
return overrideIO; return overrideIO;
} }
bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) { bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) {
for (byte i = 0; i < numMods; i++) { for (unsigned i = 0; i < numMods; i++) {
if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided
if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can provide data at one time) if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can provide data at one time)
} }
return false; return false;
} }
void UsermodManager::addToJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); } void UsermodManager::addToJsonState(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); }
void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); } void UsermodManager::addToJsonInfo(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); }
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); } void UsermodManager::readFromJsonState(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); } void UsermodManager::addToConfig(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
bool UsermodManager::readFromConfig(JsonObject& obj) { bool UsermodManager::readFromConfig(JsonObject& obj) {
bool allComplete = true; bool allComplete = true;
for (byte i = 0; i < numMods; i++) { for (unsigned i = 0; i < numMods; i++) {
if (!ums[i]->readFromConfig(obj)) allComplete = false; if (!ums[i]->readFromConfig(obj)) allComplete = false;
} }
return allComplete; return allComplete;
} }
void UsermodManager::onMqttConnect(bool sessionPresent) { for (byte i = 0; i < numMods; i++) ums[i]->onMqttConnect(sessionPresent); } void UsermodManager::onMqttConnect(bool sessionPresent) { for (unsigned i = 0; i < numMods; i++) ums[i]->onMqttConnect(sessionPresent); }
bool UsermodManager::onMqttMessage(char* topic, char* payload) { bool UsermodManager::onMqttMessage(char* topic, char* payload) {
for (byte i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true; for (unsigned i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true;
return false; return false;
} }
void UsermodManager::onUpdateBegin(bool init) { for (byte i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin void UsermodManager::onUpdateBegin(bool init) { for (unsigned i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin
void UsermodManager::onStateChange(uint8_t mode) { for (byte i = 0; i < numMods; i++) ums[i]->onStateChange(mode); } // notify usermods that WLED state changed void UsermodManager::onStateChange(uint8_t mode) { for (unsigned i = 0; i < numMods; i++) ums[i]->onStateChange(mode); } // notify usermods that WLED state changed
/* /*
* Enables usermods to lookup another Usermod. * Enables usermods to lookup another Usermod.
*/ */
Usermod* UsermodManager::lookup(uint16_t mod_id) { Usermod* UsermodManager::lookup(uint16_t mod_id) {
for (byte i = 0; i < numMods; i++) { for (unsigned i = 0; i < numMods; i++) {
if (ums[i]->getId() == mod_id) { if (ums[i]->getId() == mod_id) {
return ums[i]; return ums[i];
} }
@@ -57,7 +58,7 @@ Usermod* UsermodManager::lookup(uint16_t mod_id) {
//WLEDMM: used by Usermods in xml.cpp //WLEDMM: used by Usermods in xml.cpp
Usermod* UsermodManager::lookupName(const char *mod_name) { Usermod* UsermodManager::lookupName(const char *mod_name) {
//WLEDMM: hack to get the usermod object with the mod_name (better would be to store the usermod name in the class but that requires change to all usermods) //WLEDMM: hack to get the usermod object with the mod_name (better would be to store the usermod name in the class but that requires change to all usermods)
for (byte i = 0; i < numMods; i++) { for (unsigned i = 0; i < numMods; i++) {
// StaticJsonDocument <1024> docx; // StaticJsonDocument <1024> docx;
JsonObject um = doc.createNestedObject("um"); //WLEDMM reuse the global doc variable here JsonObject um = doc.createNestedObject("um"); //WLEDMM reuse the global doc variable here

View File

@@ -2,7 +2,9 @@
#include "wled.h" #include "wled.h"
#include "wled_ethernet.h" #include "wled_ethernet.h"
#include <Arduino.h> #include <Arduino.h>
#ifdef ARDUINO_ARCH_ESP32
#include "esp_ota_ops.h"
#endif
#warning WLED-MM GPL-v3. By installing WLED MM you implicitly accept the terms! #warning WLED-MM GPL-v3. By installing WLED MM you implicitly accept the terms!
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
@@ -211,6 +213,19 @@ void WLED::loop()
handlePresets(); handlePresets();
yield(); yield();
#if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH)
#ifdef WLED_DEBUG
unsigned long usermod2Millis = millis();
#endif
usermods.loop2();
#ifdef WLED_DEBUG
usermod2Millis = millis() - usermod2Millis;
avgUsermodMillis += usermod2Millis;
if (usermod2Millis > maxUsermodMillis) maxUsermodMillis = usermod2Millis;
#endif
yield();
#endif
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
unsigned long stripMillis = millis(); unsigned long stripMillis = millis();
#endif #endif
@@ -293,6 +308,7 @@ void WLED::loop()
delete busConfigs[i]; busConfigs[i] = nullptr; delete busConfigs[i]; busConfigs[i] = nullptr;
} }
strip.finalizeInit(); strip.finalizeInit();
busses.setBrightness(bri); // fix re-initialised bus' brightness #4005
loadLedmap = true; loadLedmap = true;
if (aligned) strip.makeAutoSegments(); if (aligned) strip.makeAutoSegments();
else strip.fixInvalidSegments(); else strip.fixInvalidSegments();
@@ -448,6 +464,9 @@ void WLED::setup()
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
pinMode(hardwareRX, INPUT_PULLDOWN); delay(1); // suppress noise in case RX pin is floating (at low noise energy) - see issue #3128 pinMode(hardwareRX, INPUT_PULLDOWN); delay(1); // suppress noise in case RX pin is floating (at low noise energy) - see issue #3128
#endif #endif
#ifdef WLED_BOOTUPDELAY
delay(WLED_BOOTUPDELAY); // delay to let voltage stabilize, helps with boot issues on some setups
#endif
Serial.begin(115200); Serial.begin(115200);
if (!Serial) delay(1000); // WLEDMM make sure that Serial has initalized if (!Serial) delay(1000); // WLEDMM make sure that Serial has initalized
@@ -498,7 +517,10 @@ void WLED::setup()
#ifdef WLED_RELEASE_NAME #ifdef WLED_RELEASE_NAME
USER_PRINTF(" WLEDMM_%s %s, build %s.\n", versionString, releaseString, TOSTRING(VERSION)); // WLEDMM specific USER_PRINTF(" WLEDMM_%s %s, build %s.\n", versionString, releaseString, TOSTRING(VERSION)); // WLEDMM specific
#endif #endif
#ifdef ARDUINO_ARCH_ESP32
const esp_partition_t *running_partition = esp_ota_get_running_partition();
USER_PRINTF("Running from: %s which is %u bytes and type %u subtype %u at address %x\n",running_partition->label,running_partition->size,running_partition->type,running_partition->subtype,running_partition->address);
#endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
DEBUG_PRINT(F("esp32 ")); DEBUG_PRINT(F("esp32 "));
DEBUG_PRINTLN(ESP.getSdkVersion()); DEBUG_PRINTLN(ESP.getSdkVersion());
@@ -586,9 +608,11 @@ void WLED::setup()
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
//psramInit(); //WLEDMM?? softhack007: not sure if explicit init is really needed ... lets disable it here and see if that works //psramInit(); //WLEDMM?? softhack007: not sure if explicit init is really needed ... lets disable it here and see if that works
#if defined(CONFIG_IDF_TARGET_ESP32S3) #if defined(CONFIG_IDF_TARGET_ESP32S3)
// S3: reserve GPIO 33-37 for "octal" PSRAM #if CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM)
managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} }; // S3: reserve GPIO 33-37 for "octal" PSRAM
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM); managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#endif
#elif defined(CONFIG_IDF_TARGET_ESP32S2) #elif defined(CONFIG_IDF_TARGET_ESP32S2)
// S2: reserve GPIO 26-32 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation) // S2: reserve GPIO 26-32 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation)
//managed_pin_type pins[] = { {26, true}, {27, true}, {28, true}, {29, true}, {30, true}, {31, true}, {32, true} }; //managed_pin_type pins[] = { {26, true}, {27, true}, {28, true}, {29, true}, {30, true}, {31, true}, {32, true} };
@@ -807,7 +831,11 @@ void WLED::setup()
USER_PRINTLN(F("\nGPIO\t| Assigned to\t\t| Info")); USER_PRINTLN(F("\nGPIO\t| Assigned to\t\t| Info"));
USER_PRINTLN(F("--------|-----------------------|------------")); USER_PRINTLN(F("--------|-----------------------|------------"));
for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3 for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3
#if defined(CONFIG_IDF_TARGET_ESP32S3)
if((pinManager.isPinOk(pinNr, false)) || (pinNr > 18 && pinNr < 21)) { // softhack007: list USB pins
#else
if(pinManager.isPinOk(pinNr, false)) { if(pinManager.isPinOk(pinNr, false)) {
#endif
//if ((!pinManager.isPinAllocated(pinNr)) && (pinManager.getPinSpecialText(pinNr).length() == 0)) continue; // un-comment to hide no-name,unused GPIO pins //if ((!pinManager.isPinAllocated(pinNr)) && (pinManager.getPinSpecialText(pinNr).length() == 0)) continue; // un-comment to hide no-name,unused GPIO pins
bool is_inOut = pinManager.isPinOk(pinNr, true); bool is_inOut = pinManager.isPinOk(pinNr, true);
#if 0 // for testing #if 0 // for testing
@@ -849,6 +877,7 @@ void WLED::setup()
USER_PRINTLN(F("\n")); USER_PRINTLN(F("\n"));
#endif #endif
USER_PRINT(F("Free heap ")); USER_PRINTLN(ESP.getFreeHeap());USER_PRINTLN();
USER_PRINTLN(F("WLED initialization done.\n")); USER_PRINTLN(F("WLED initialization done.\n"));
delay(50); delay(50);
// repeat Ada prompt // repeat Ada prompt
@@ -1217,7 +1246,7 @@ void WLED::handleConnection()
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// reconnect WiFi to clear stale allocations if heap gets too low // reconnect WiFi to clear stale allocations if heap gets too low
if ((!strip.isUpdating()) && (now - heapTime > 5000)) { // WLEDMM: updated with better logic for small heap available by block, not total. // WLEDMM trying to use a moment when the strip is idle if ((!strip.isUpdating()) && (now - heapTime > 5000)) { // WLEDMM: updated with better logic for small heap available by block, not total. // WLEDMM trying to use a moment when the strip is idle
#if defined(ARDUINO_ARCH_ESP32S2) #if defined(ARDUINO_ARCH_ESP32S2) || defined(WLED_ENABLE_HUB75MATRIX)
uint32_t heap = ESP.getFreeHeap(); // WLEDMM works better on -S2 uint32_t heap = ESP.getFreeHeap(); // WLEDMM works better on -S2
#else #else
uint32_t heap = heap_caps_get_largest_free_block(0x1800); // WLEDMM: This is a better metric for free heap. uint32_t heap = heap_caps_get_largest_free_block(0x1800); // WLEDMM: This is a better metric for free heap.

View File

@@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2407171 #define VERSION 2409280
// WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED.
#define _MoonModules_WLED_ #define _MoonModules_WLED_
@@ -928,9 +928,9 @@ public:
} }
// boot starts here // boot starts here
void setup(); void setup() __attribute__((used));
void loop(); void loop() __attribute__((used));
void reset(); void reset();
void beginStrip(); void beginStrip();

View File

@@ -61,6 +61,7 @@ void esp_heap_trace_free_hook(void* ptr)
unsigned long lastMillis = 0; //WLEDMM unsigned long lastMillis = 0; //WLEDMM
unsigned long loopCounter = 0; //WLEDMM unsigned long loopCounter = 0; //WLEDMM
void setup() __attribute__((used)); // needed for -flto
void setup() { void setup() {
#ifdef WLED_DEBUG_HEAP #ifdef WLED_DEBUG_HEAP
esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook); esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook);
@@ -68,6 +69,7 @@ void setup() {
WLED::instance().setup(); WLED::instance().setup();
} }
void loop() __attribute__((used)); // needed for -flto
void loop() { void loop() {
//WLEDMM show loops per second //WLEDMM show loops per second
loopCounter++; loopCounter++;

View File

@@ -1,4 +1,7 @@
#include "wled.h" #include "wled.h"
#ifdef ARDUINO_ARCH_ESP32
#include "esp_ota_ops.h"
#endif
/* /*
* Adalight and TPM2 handler * Adalight and TPM2 handler
@@ -119,6 +122,34 @@ void handleSerial()
} else if (next == 'v') { } else if (next == 'v') {
Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION); Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION);
} else if (next == '^') {
#ifdef ARDUINO_ARCH_ESP32
esp_err_t err;
const esp_partition_t *boot_partition = esp_ota_get_boot_partition();
const esp_partition_t *running_partition = esp_ota_get_running_partition();
USER_PRINTF("Running on %s and we should have booted from %s. This %s\n",running_partition->label,boot_partition->label,(String(running_partition->label) == String(boot_partition->label))?"is what we expect.":"means OTA messed up!");
if (String(running_partition->label) == String(boot_partition->label)) {
esp_partition_iterator_t new_boot_partition_iterator = NULL;
if (boot_partition->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0) {
new_boot_partition_iterator = esp_partition_find(ESP_PARTITION_TYPE_APP,ESP_PARTITION_SUBTYPE_APP_OTA_1,"app1");
} else {
new_boot_partition_iterator = esp_partition_find(ESP_PARTITION_TYPE_APP,ESP_PARTITION_SUBTYPE_APP_OTA_0,"app0");
}
const esp_partition_t* new_boot_partition = esp_partition_get(new_boot_partition_iterator);
err = esp_ota_set_boot_partition(new_boot_partition);
if (err == ESP_OK) {
USER_PRINTF("Switching boot partitions from %s to %s in 3 seconds!\n",boot_partition->label,new_boot_partition->label);
delay(3000);
esp_restart();
} else {
USER_PRINTF("Looks like the other app partition (%s) is invalid. Ignoring.\n",new_boot_partition->label);
}
} else {
USER_PRINTF("Looks like the other partion is invalid as we exepected %s but we booted failsafe to %s. Ignoring boot change.\n",boot_partition->label,running_partition->label);
}
#else
USER_PRINTLN("Boot partition switching is only available for ESP32 and newer boards.");
#endif
} else if (next == 'X') { } else if (next == 'X') {
forceReconnect = true; // WLEDMM - force reconnect via Serial forceReconnect = true; // WLEDMM - force reconnect via Serial
} else if (next == 0xB0) {updateBaudRate( 115200); } else if (next == 0xB0) {updateBaudRate( 115200);

View File

@@ -10,9 +10,11 @@ static volatile unsigned long wsLastLiveTime = 0; // WLEDMM
//uint8_t* wsFrameBuffer = nullptr; //uint8_t* wsFrameBuffer = nullptr;
#if !defined(ARDUINO_ARCH_ESP32) || defined(WLEDMM_FASTPATH) // WLEDMM #if !defined(ARDUINO_ARCH_ESP32) || defined(WLEDMM_FASTPATH) // WLEDMM
#define WS_LIVE_INTERVAL 120 #define WS_LIVE_INTERVAL_MAX 120
#define WS_LIVE_INTERVAL_MIN 25
#else #else
#define WS_LIVE_INTERVAL 80 #define WS_LIVE_INTERVAL_MAX 80
#define WS_LIVE_INTERVAL_MIN 40
#endif #endif
void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)
@@ -215,7 +217,11 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen
AsyncWebSocketBuffer wsBuf(bufSize); AsyncWebSocketBuffer wsBuf(bufSize);
if (!wsBuf) { if (!wsBuf) {
USER_PRINTLN(F("WS buffer allocation failed.")); static unsigned long last_err_time = 0;
if (millis() - last_err_time > 300) { // WLEDMM limit to 3 messages per second
USER_PRINTF("WS buffer allocation failed (!wsBuf %u bytes).\n", bufSize);
last_err_time = millis();
}
errorFlag = ERR_LOW_WS_MEM; errorFlag = ERR_LOW_WS_MEM;
return false; //out of memory return false; //out of memory
} }
@@ -247,6 +253,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
} }
#endif #endif
uint32_t c = restoreColorLossy(strip.getPixelColor(i), stripBrightness); // WLEDMM full bright preview - does _not_ recover ABL reductions uint32_t c = restoreColorLossy(strip.getPixelColor(i), stripBrightness); // WLEDMM full bright preview - does _not_ recover ABL reductions
//uint32_t c = strip.getPixelColorRestored(i);
// WLEDMM begin: preview with color gamma correction // WLEDMM begin: preview with color gamma correction
if (gammaCorrectPreview) { if (gammaCorrectPreview) {
uint8_t w = W(c); // not sure why, but it looks better if using "white" without corrections uint8_t w = W(c); // not sure why, but it looks better if using "white" without corrections
@@ -269,7 +276,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
void handleWs() void handleWs()
{ {
if ((millis() - wsLastLiveTime) > (unsigned long)(max((strip.getLengthTotal()/20), WS_LIVE_INTERVAL))) //WLEDMM dynamic nr of peek frames per second if ((millis() - wsLastLiveTime) > (unsigned long)(max(WS_LIVE_INTERVAL_MIN, min((strip.getLengthTotal()/80), WS_LIVE_INTERVAL_MAX)))) //WLEDMM dynamic nr of peek frames per second
{ {
#ifdef ESP8266 #ifdef ESP8266
ws.cleanupClients(3); ws.cleanupClients(3);