diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d09c8a605..1340da91c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,12 +2,7 @@ # [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6 ARG VARIANT="3" -FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} - -# [Option] Install Node.js -ARG INSTALL_NODE="true" -ARG NODE_VERSION="lts/*" -RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi +FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT} # [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. # COPY requirements.txt /tmp/pip-tmp/ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2a8e4712d..241acd79d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,10 +5,7 @@ "context": "..", "args": { // Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9 - "VARIANT": "3", - // Options - "INSTALL_NODE": "true", - "NODE_VERSION": "lts/*" + "VARIANT": "3" } }, @@ -54,7 +51,7 @@ // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "npm install", + "postCreateCommand": "bash -i -c 'nvm install && npm ci'", // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..2bac314f3 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,81 @@ +name: WLED Build + +# Only included into other workflows +on: + workflow_call: + +jobs: + + get_default_envs: + name: Gather Environments + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'pip' + - name: Install PlatformIO + run: pip install -r requirements.txt + - name: Get default environments + id: envs + run: | + echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT + outputs: + environments: ${{ steps.envs.outputs.environments }} + + + build: + name: Build Enviornments + runs-on: ubuntu-latest + needs: get_default_envs + strategy: + fail-fast: false + matrix: + environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} + steps: + - uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + - run: npm ci + - name: Cache PlatformIO + uses: actions/cache@v4 + with: + path: | + ~/.platformio/.cache + ~/.buildcache + build_output + key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }} + restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}- + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'pip' + - name: Install PlatformIO + run: pip install -r requirements.txt + - name: Build firmware + run: pio run -e ${{ matrix.environment }} + - uses: actions/upload-artifact@v4 + with: + name: firmware-${{ matrix.environment }} + path: | + build_output/release/*.bin + build_output/release/*_ESP02*.bin.gz + + + testCdata: + name: Test cdata.js + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + - run: npm ci + - run: npm test diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 000000000..138730058 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,40 @@ + +name: Deploy Nightly +on: + # This can be used to automatically publish nightlies at UTC nighttime + schedule: + - cron: '0 2 * * *' # run at 2 AM UTC + # This can be used to allow manually triggering nightlies from the web interface + workflow_dispatch: + +jobs: + wled_build: + uses: ./.github/workflows/build.yml + nightly: + name: Deploy nightly + runs-on: ubuntu-latest + needs: wled_build + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + merge-multiple: true + - name: Show Files + run: ls -la + - name: "✏️ Generate release changelog" + id: changelog + uses: janheinrichmerker/action-github-changelog-generator@v2.3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + sinceTag: v0.15.0 + - name: Update Nightly Release + uses: andelf/nightly-release@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: nightly + name: 'Nightly Release $$' + prerelease: true + body: ${{ steps.changelog.outputs.changelog }} + files: | + ./*.bin \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..27beec99c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: WLED Release CI + +on: + push: + tags: + - '*' + +jobs: + + wled_build: + uses: ./.github/workflows/build.yml + + release: + name: Create Release + runs-on: ubuntu-latest + needs: wled_build + steps: + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + - name: Create draft release + uses: softprops/action-gh-release@v1 + with: + draft: True + files: | + *.bin + *.bin.gz + diff --git a/.github/workflows/wled-ci.yml b/.github/workflows/wled-ci.yml index 1dcab26ab..3c862c185 100644 --- a/.github/workflows/wled-ci.yml +++ b/.github/workflows/wled-ci.yml @@ -1,94 +1,11 @@ name: WLED CI -on: [push, pull_request] +on: + push: + branches: + - '*' + pull_request: jobs: - - get_default_envs: - name: Gather Environments - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'pip' - - name: Install PlatformIO - run: pip install -r requirements.txt - - name: Get default environments - id: envs - run: | - echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT - outputs: - environments: ${{ steps.envs.outputs.environments }} - - - build: - name: Build Enviornments - runs-on: ubuntu-latest - needs: get_default_envs - strategy: - fail-fast: false - matrix: - environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} - steps: - - uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - cache: 'npm' - - run: npm ci - - name: Cache PlatformIO - uses: actions/cache@v4 - with: - path: | - ~/.platformio/.cache - ~/.buildcache - build_output - key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }} - restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}- - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'pip' - - name: Install PlatformIO - run: pip install -r requirements.txt - - name: Build firmware - run: pio run -e ${{ matrix.environment }} - - uses: actions/upload-artifact@v4 - with: - name: firmware-${{ matrix.environment }} - path: | - build_output/release/*.bin - build_output/release/*_ESP02*.bin.gz - release: - name: Create Release - runs-on: ubuntu-latest - needs: build - if: startsWith(github.ref, 'refs/tags/') - steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - - name: Create draft release - uses: softprops/action-gh-release@v1 - with: - draft: True - files: | - *.bin - *.bin.gz - - - testCdata: - name: Test cdata.js - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: '20.x' - cache: 'npm' - - run: npm ci - - run: npm test + wled_build: + uses: ./.github/workflows/build.yml diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..10fef252a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20.18 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06c221fca..670b5561d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ A good description helps us to review and understand your proposed changes. For ### Target branch for pull requests -Please make all PRs against the `0_15` branch. +Please make all PRs against the `main` branch. ### Updating your code While the PR is open - and under review by maintainers - you may be asked to modify your PR source code. @@ -105,4 +105,4 @@ Good: There is no hard character limit for a comment within a line, though as a rule of thumb consider wrapping after 120 characters. -Inline comments are OK if they describe that line only and are not exceedingly wide. \ No newline at end of file +Inline comments are OK if they describe that line only and are not exceedingly wide. diff --git a/boards/lolin_s3_mini.json b/boards/lolin_s3_mini.json new file mode 100644 index 000000000..7f55f0bde --- /dev/null +++ b/boards/lolin_s3_mini.json @@ -0,0 +1,47 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_qspi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_LOLIN_S3_MINI", + "-DARDUINO_USB_MODE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x303A", + "0x8167" + ] + ], + "mcu": "esp32s3", + "variant": "lolin_s3_mini" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "WEMOS LOLIN S3 Mini", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.wemos.cc/en/latest/s3/index.html", + "vendor": "WEMOS" +} + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0f73bff0c..0afeeaafd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,21 @@ { "name": "wled", - "version": "0.15.0-b7", + "version": "0.16.0-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wled", - "version": "0.15.0-b7", + "version": "0.16.0-dev", "license": "ISC", "dependencies": { "clean-css": "^5.3.3", "html-minifier-terser": "^7.2.0", "inliner": "^1.13.1", - "nodemon": "^3.0.2" + "nodemon": "^3.1.7" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@jridgewell/gen-mapping": { diff --git a/package.json b/package.json index 9d095c82c..68260982e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.15.0-b7", + "version": "0.16.0-alpha", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { @@ -27,5 +27,8 @@ "html-minifier-terser": "^7.2.0", "inliner": "^1.13.1", "nodemon": "^3.1.7" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 633654008..4d1594d84 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -19,8 +19,9 @@ def _create_dirs(dirs=["map", "release", "firmware"]): os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True) def create_release(source): - release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME") - if release_name: + release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME") + if release_name_def: + release_name = release_name_def.replace("\\\"", "") version = _get_cpp_define_value(env, "WLED_VERSION") release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin") release_gz_file = release_file + ".gz" diff --git a/platformio.ini b/platformio.ini index 6d4aa1dc1..5e0c01db7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ # ------------------------------------------------------------------------------ # CI/release binaries -default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover +default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32dev_V4, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover src_dir = ./wled00 data_dir = ./wled00/data @@ -176,6 +176,7 @@ lib_deps = extra_scripts = ${scripts_defaults.extra_scripts} [esp8266] +build_unflags = ${common.build_unflags} build_flags = -DESP8266 -DFP_IN_IROM @@ -242,6 +243,7 @@ lib_deps_compat = #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_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4 +build_unflags = ${common.build_unflags} build_flags = -g -DARDUINO_ARCH_ESP32 #-DCONFIG_LITTLEFS_FOR_IDF_3_2 @@ -263,6 +265,7 @@ lib_deps = AR_build_flags = -D USERMOD_AUDIOREACTIVE -D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster) AR_lib_deps = kosme/arduinoFFT @ 2.0.1 +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs [esp32_idf_V4] ;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5 @@ -270,21 +273,26 @@ AR_lib_deps = kosme/arduinoFFT @ 2.0.1 ;; ;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio. -platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) + +;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4 +build_unflags = ${common.build_unflags} build_flags = -g -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one -DARDUINO_ARCH_ESP32 -DESP32 -D CONFIG_ASYNC_TCP_USE_WDT=0 -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 + -D WLED_ENABLE_DMX_INPUT lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 + https://github.com/someweisguy/esp_dmx.git#47db25d ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs [esp32s2] ;; generic definitions for all ESP32-S2 boards -platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform = ${esp32_idf_V4.platform} +build_unflags = ${common.build_unflags} build_flags = -g -DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32S2 @@ -298,11 +306,12 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs [esp32c3] ;; generic definitions for all ESP32-C3 boards -platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform = ${esp32_idf_V4.platform} +build_unflags = ${common.build_unflags} build_flags = -g -DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32C3 @@ -315,11 +324,13 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs +board_build.flash_mode = qio [esp32s3] ;; generic definitions for all ESP32-S3 boards -platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform = ${esp32_idf_V4.platform} +build_unflags = ${common.build_unflags} build_flags = -g -DESP32 -DARDUINO_ARCH_ESP32 @@ -333,6 +344,7 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ${env.lib_deps} +board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs # ------------------------------------------------------------------------------ @@ -345,7 +357,7 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D lib_deps = ${esp8266.lib_deps} monitor_filters = esp8266_exception_decoder @@ -354,13 +366,13 @@ extends = env:nodemcuv2 ;; using platform version and build options from WLED 0.14.0 platform = ${esp8266.platform_compat} platform_packages = ${esp8266.platform_packages_compat} -build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP8266_compat #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D ;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9 [env:nodemcuv2_160] extends = env:nodemcuv2 board_build.f_cpu = 160000000L -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D -D USERMOD_AUDIOREACTIVE [env:esp8266_2m] @@ -369,7 +381,7 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_2m512k} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02 +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\" lib_deps = ${esp8266.lib_deps} [env:esp8266_2m_compat] @@ -377,12 +389,12 @@ extends = env:esp8266_2m ;; using platform version and build options from WLED 0.14.0 platform = ${esp8266.platform_compat} platform_packages = ${esp8266.platform_packages_compat} -build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP02_compat #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D [env:esp8266_2m_160] extends = env:esp8266_2m board_build.f_cpu = 160000000L -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02_160 +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\" -D USERMOD_AUDIOREACTIVE [env:esp01_1m_full] @@ -391,7 +403,7 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_1m128k} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM lib_deps = ${esp8266.lib_deps} @@ -400,12 +412,12 @@ extends = env:esp01_1m_full ;; using platform version and build options from WLED 0.14.0 platform = ${esp8266.platform_compat} platform_packages = ${esp8266.platform_packages_compat} -build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP01_compat -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D [env:esp01_1m_full_160] extends = env:esp01_1m_full board_build.f_cpu = 160000000L -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA -D USERMOD_AUDIOREACTIVE ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM @@ -414,19 +426,30 @@ board = esp32dev platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET +build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET ${esp32.AR_build_flags} lib_deps = ${esp32.lib_deps} ${esp32.AR_lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.default_partitions} +[env:esp32dev_V4] +board = esp32dev +platform = ${esp32_idf_V4.platform} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET + ${esp32.AR_build_flags} +lib_deps = ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} +monitor_filters = esp32_exception_decoder +board_build.partitions = ${esp32.default_partitions} +board_build.flash_mode = dio + [env:esp32dev_8M] board = esp32dev platform = ${esp32_idf_V4.platform} -platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_8M #-D WLED_DISABLE_BROWNOUT_DET +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET ${esp32.AR_build_flags} lib_deps = ${esp32_idf_V4.lib_deps} ${esp32.AR_lib_deps} @@ -440,9 +463,8 @@ board_upload.maximum_size = 8388608 [env:esp32dev_16M] board = esp32dev platform = ${esp32_idf_V4.platform} -platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_16M #-D WLED_DISABLE_BROWNOUT_DET +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET ${esp32.AR_build_flags} lib_deps = ${esp32_idf_V4.lib_deps} ${esp32.AR_lib_deps} @@ -458,7 +480,7 @@ board_build.flash_mode = dio ;platform = ${esp32.platform} ;platform_packages = ${esp32.platform_packages} ;build_unflags = ${common.build_unflags} -;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET +;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET ; ${esp32.AR_build_flags} ;lib_deps = ${esp32.lib_deps} ; ${esp32.AR_lib_deps} @@ -473,7 +495,7 @@ platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 +build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 ; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only ${esp32.AR_build_flags} lib_deps = ${esp32.lib_deps} @@ -483,13 +505,12 @@ board_build.partitions = ${esp32.default_partitions} [env:esp32_wrover] extends = esp32_idf_V4 platform = ${esp32_idf_V4.platform} -platform_packages = ${esp32_idf_V4.platform_packages} board = ttgo-t7-v14-mini32 board_build.f_flash = 80000000L board_build.flash_mode = qio board_build.partitions = ${esp32.extended_partitions} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_WROVER +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\" -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html -D DATA_PINS=25 ${esp32.AR_build_flags} @@ -499,11 +520,10 @@ lib_deps = ${esp32_idf_V4.lib_deps} [env:esp32c3dev] extends = esp32c3 platform = ${esp32c3.platform} -platform_packages = ${esp32c3.platform_packages} framework = arduino board = esp32-c3-devkitm-1 board_build.partitions = ${esp32.default_partitions} -build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3 +build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\" -D WLED_WATCHDOG_TIMEOUT=0 -DLOLIN_WIFI_FIX ; seems to work much better with this -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB @@ -517,10 +537,9 @@ lib_deps = ${esp32c3.lib_deps} board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB platform = ${esp32s3.platform} -platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_16MB_opi +build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\" -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") @@ -540,10 +559,9 @@ monitor_filters = esp32_exception_decoder board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB platform = ${esp32s3.platform} -platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_opi +build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\" -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") @@ -560,12 +578,11 @@ monitor_filters = esp32_exception_decoder ;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1 ;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi) platform = ${esp32s3.platform} -platform_packages = ${esp32s3.platform_packages} board = esp32s3camlcd ;; this is the only standard board with "opi_opi" board_build.arduino.memory_type = opi_opi upload_speed = 921600 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_WROOM-2 +build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\" -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip ;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") @@ -587,10 +604,9 @@ monitor_filters = esp32_exception_decoder ;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi) board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM platform = ${esp32s3.platform} -platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_4M_qspi +build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\" -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") -DBOARD_HAS_PSRAM -DLOLIN_WIFI_FIX ; seems to work much better with this @@ -605,13 +621,12 @@ monitor_filters = esp32_exception_decoder [env:lolin_s2_mini] platform = ${esp32s2.platform} -platform_packages = ${esp32s2.platform_packages} board = lolin_s2_mini board_build.partitions = ${esp32.default_partitions} board_build.flash_mode = qio board_build.f_flash = 80000000L build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2 +build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\" -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 diff --git a/platformio_override.sample.ini b/platformio_override.sample.ini index 8e5fdf003..19b8c273a 100644 --- a/platformio_override.sample.ini +++ b/platformio_override.sample.ini @@ -5,7 +5,7 @@ # Please visit documentation: https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = WLED_tasmota_1M # define as many as you need +default_envs = WLED_generic8266_1M, esp32dev_V4_dio80 # put the name(s) of your own build environment here. You can define as many as you need #---------- # SAMPLE @@ -28,8 +28,8 @@ lib_deps = ${esp8266.lib_deps} ; robtillaart/SHT85@~0.3.3 ; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug ; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library -; ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE ; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following +; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp8266.build_flags} @@ -37,7 +37,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above. ; ; Set a release name that may be used to distinguish required binary for flashing -; -D WLED_RELEASE_NAME=ESP32_MULTI_USREMODS +; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\" ; ; disable specific features ; -D WLED_DISABLE_OTA @@ -111,7 +111,6 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; ; Use 4 Line Display usermod with SPI display ; -D USERMOD_FOUR_LINE_DISPLAY -; -D USE_ALT_DISPlAY # mandatory ; -DFLD_SPI_DEFAULT ; -D FLD_TYPE=SSD1306_SPI64 ; -D FLD_PIN_CLOCKSPI=14 @@ -142,7 +141,8 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering) ; ; Use Audioreactive usermod and configure I2S microphone -; -D USERMOD_AUDIOREACTIVE +; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT +; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps} ; -D AUDIOPIN=-1 ; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM ; -D I2S_SDPIN=36 @@ -158,17 +158,22 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; -D USERMOD_POV_DISPLAY ; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16) ; -D STATUSLED=16 -; +; ; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name ; -D SERVERNAME="\"WLED\"" -; +; ; set the number of LEDs -; -D DEFAULT_LED_COUNT=30 +; -D PIXEL_COUNTS=30 ; or this for multiple outputs ; -D PIXEL_COUNTS=30,30 ; ; set the default LED type -; -D DEFAULT_LED_TYPE=22 # see const.h (TYPE_xxxx) +; -D LED_TYPES=22 # see const.h (TYPE_xxxx) +; or this for multiple outputs +; -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB +; +; set default color order of your led strip +; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB ; ; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs ; -D ABL_MILLIAMPS_DEFAULT=850 @@ -177,9 +182,6 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; enable IR by setting remote type ; -D IRTYPE=0 # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote ; -; set default color order of your led strip -; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB -; ; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues) ; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue # needed only for classic ESP32 rev.1 ; @@ -237,14 +239,13 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLE lib_deps = ${esp8266.lib_deps} [env:esp32dev_qio80] +extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options) board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET + ${esp32.AR_build_flags} ;; optional - includes USERMOD_AUDIOREACTIVE lib_deps = ${esp32.lib_deps} + ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32.default_partitions} board_build.f_flash = 80000000L board_build.flash_mode = qio @@ -252,26 +253,25 @@ board_build.flash_mode = qio ;; experimental ESP32 env using ESP-IDF V4.4.x ;; Warning: this build environment is not stable!! ;; please erase your device before installing. +extends = esp32_idf_V4 # based on newer "esp-idf V4" platform environment board = esp32dev -platform = ${esp32_idf_V4.platform} -platform_packages = ${esp32_idf_V4.platform_packages} -build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET + ${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE lib_deps = ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32_idf_V4.default_partitions} +board_build.partitions = ${esp32.default_partitions} ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions} board_build.f_flash = 80000000L board_build.flash_mode = dio [env:esp32s2_saola] +extends = esp32s2 board = esp32-s2-saola-1 platform = ${esp32s2.platform} platform_packages = ${esp32s2.platform_packages} framework = arduino -board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.flash_mode = qio upload_speed = 460800 -build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s2.build_flags} ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work -DARDUINO_USB_CDC_ON_BOOT=1 @@ -308,7 +308,7 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed. lib_deps = ${esp8266.lib_deps} [env:d1_mini_debug] @@ -363,36 +363,48 @@ board_upload.flash_size = 2MB board_upload.maximum_size = 2097152 [env:wemos_shield_esp32] +extends = esp32 ;; use default esp32 platform board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} upload_speed = 460800 -build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32.build_flags} + -D WLED_RELEASE_NAME=\"ESP32_wemos_shield\" -D DATA_PINS=16 -D RLYPIN=19 -D BTNPIN=17 -D IRPIN=18 - -D UWLED_USE_MY_CONFIG + -UWLED_USE_MY_CONFIG -D USERMOD_DALLASTEMPERATURE -D USERMOD_FOUR_LINE_DISPLAY -D TEMPERATURE_PIN=23 - -D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI - -D USERMOD_AUDIOREACTIVE + ${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE lib_deps = ${esp32.lib_deps} - OneWire@~2.3.5 - olikraus/U8g2 @ ^2.28.8 - https://github.com/blazoncek/arduinoFFT.git + OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE + olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY + ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE board_build.partitions = ${esp32.default_partitions} -[env:m5atom] -board = esp32dev -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39 +[env:esp32_pico-D4] +extends = esp32 ;; use default esp32 platform +board = pico32 ;; pico32-D4 is different from the standard esp32dev + ;; hardware details from https://github.com/srg74/WLED-ESP32-pico +build_flags = ${common.build_flags} ${esp32.build_flags} + -D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"' + -D WLED_DISABLE_ADALIGHT ;; no serial-to-USB chip on this board - better to disable serial protocols + -D DATA_PINS=2,18 ;; LED pins + -D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR + ${esp32.AR_build_flags} ;; include USERMOD_AUDIOREACTIVE + -D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default + ;; Audioreactive settings for on-board microphone (ICS-43432) + -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 + -D SR_SQUELCH=5 -D SR_GAIN=30 lib_deps = ${esp32.lib_deps} -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} + ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE board_build.partitions = ${esp32.default_partitions} +board_build.f_flash = 80000000L + +[env:m5atom] +extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options) +build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39 [env:sp501e] board = esp_wroom_02 @@ -415,7 +427,7 @@ platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_2m512k} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5 - -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 + -D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 lib_deps = ${esp8266.lib_deps} [env:Athom_15w_RGBCW] ;15w bulb @@ -425,7 +437,7 @@ platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_2m512k} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13 - -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT + -D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT lib_deps = ${esp8266.lib_deps} [env:Athom_3Pin_Controller] ;small controller with only data @@ -491,9 +503,8 @@ lib_deps = ${esp8266.lib_deps} # EleksTube-IPS # ------------------------------------------------------------------------------ [env:elekstube_ips] +extends = esp32 ;; use default esp32 platform board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} upload_speed = 921600 build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED -D USERMOD_RTC @@ -501,7 +512,7 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU -D DATA_PINS=12 -D RLYPIN=27 -D BTNPIN=34 - -D DEFAULT_LED_COUNT=6 + -D PIXEL_COUNTS=6 # Display config -D ST7789_DRIVER -D TFT_WIDTH=135 @@ -517,5 +528,15 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU monitor_filters = esp32_exception_decoder lib_deps = ${esp32.lib_deps} - TFT_eSPI @ ^2.3.70 -board_build.partitions = ${esp32.default_partitions} + TFT_eSPI @ 2.5.33 ;; this is the last version that compiles with the WLED default framework - newer versions require platform = espressif32 @ ^6.3.2 + +# ------------------------------------------------------------------------------ +# Usermod examples +# ------------------------------------------------------------------------------ + +# 433MHz RF remote example for esp32dev +[env:esp32dev_usermod_RF433] +extends = env:esp32dev +build_flags = ${env:esp32dev.build_flags} -D USERMOD_RF433 +lib_deps = ${env:esp32dev.lib_deps} + sui77/rc-switch @ 2.6.4 diff --git a/readme.md b/readme.md index 80256560a..8c9a08801 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ # Welcome to my project WLED! ✨ -A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102! +A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102! ## ⚙️ Features - WS2812FX library with more than 100 special effects @@ -21,7 +21,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control - Segments to set different effects and colors to user defined parts of the LED string - Settings page - configuration via the network - Access Point and station mode - automatic failsafe AP -- Up to 10 LED outputs per instance +- [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance - Support for RGBW strips - Up to 250 user presets to save and load colors/effects easily, supports cycling through them. - Presets can be used to automatically execute API calls diff --git a/tools/AutoCubeMap.xlsx b/tools/AutoCubeMap.xlsx new file mode 100644 index 000000000..b3f5cee2a Binary files /dev/null and b/tools/AutoCubeMap.xlsx differ diff --git a/tools/cdata.js b/tools/cdata.js index d65573a8e..c5d3c6aa5 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -101,6 +101,7 @@ function adoptVersionAndRepo(html) { async function minify(str, type = "plain") { const options = { collapseWhitespace: true, + conservativeCollapse: true, // preserve spaces in text collapseBooleanAttributes: true, collapseInlineTagWhitespace: true, minifyCSS: true, diff --git a/usermods/Analog_Clock/Analog_Clock.h b/usermods/Analog_Clock/Analog_Clock.h index 596f0acb3..9d82f7670 100644 --- a/usermods/Analog_Clock/Analog_Clock.h +++ b/usermods/Analog_Clock/Analog_Clock.h @@ -102,9 +102,9 @@ private: void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) { uint32_t ms = time.ms % 1000; - uint8_t b0 = (cos8(ms * 64 / 1000) - 128) * 2; + uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2; setPixelColor(secondLed, gamma32(scale32(secondColor, b0))); - uint8_t b1 = (sin8(ms * 64 / 1000) - 128) * 2; + uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2; setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1))); } diff --git a/usermods/Internal_Temperature_v2/usermod_internal_temperature.h b/usermods/Internal_Temperature_v2/usermod_internal_temperature.h index c24b4c628..6d4d4577c 100644 --- a/usermods/Internal_Temperature_v2/usermod_internal_temperature.h +++ b/usermods/Internal_Temperature_v2/usermod_internal_temperature.h @@ -50,7 +50,7 @@ public: #else // ESP32 ESP32S3 and ESP32C3 temperature = roundf(temperatureRead() * 10) / 10; #endif - + if(presetToActivate != 0){ // Check if temperature has exceeded the activation threshold if (temperature >= activationThreshold) { // Update the state flag if not already set @@ -58,7 +58,7 @@ public: isAboveThreshold = true; } // Check if a 'high temperature' preset is configured and it's not already active - if (presetToActivate != 0 && currentPreset != presetToActivate) { + if (currentPreset != presetToActivate) { // If a playlist is active, store it for reactivation later if (currentPlaylist > 0) { previousPlaylist = currentPlaylist; @@ -101,6 +101,7 @@ public: } } } + } #ifndef WLED_DISABLE_MQTT if (WLED_MQTT_CONNECTED) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index ad449fc83..9c463e0a1 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -75,7 +75,7 @@ static uint8_t soundAgc = 0; // Automagic gain control: 0 - n //static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency -static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay() +static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getFrameTime() static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData static unsigned long timeOfPeak = 0; // time of last sample peak detection. static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects @@ -536,8 +536,8 @@ static void detectSamplePeak(void) { #endif static void autoResetPeak(void) { - uint16_t MinShowDelay = MAX(50, strip.getMinShowDelay()); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC - if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed. + uint16_t peakDelay = max(uint16_t(50), strip.getFrameTime()); + if (millis() - timeOfPeak > peakDelay) { // Auto-reset of samplePeak after at least one complete frame has passed. samplePeak = false; if (audioSyncEnabled == 0) udpSamplePeak = false; // this is normally reset by transmitAudioData } diff --git a/usermods/deep_sleep/readme.md b/usermods/deep_sleep/readme.md new file mode 100644 index 000000000..006aa31fd --- /dev/null +++ b/usermods/deep_sleep/readme.md @@ -0,0 +1,84 @@ +# Deep Sleep usermod + +This usermod unleashes the low power capabilities of th ESP: when you power off your LEDs (using the UI power button or a macro) the ESP will be put into deep sleep mode, reducing power consumption to a minimum. +During deep sleep the ESP is shut down completely: no WiFi, no CPU, no outputs. The only way to wake it up is to use an external signal or a button. Once it wakes from deep sleep it reboots so ***make sure to use a boot-up preset.*** + +# A word of warning + +When you disable the WLED option 'Turn LEDs on after power up/reset' and 'DelaySleep' is set to zero the ESP will go into deep sleep directly after power-up and only start WLED after it has been woken up. +If the ESP can not be awoken from deep sleep due to a wrong configuration it has to be factory reset, disabling sleep at power-up. There is no other way to wake it up. + +# Power Consumption in deep sleep + +The current drawn by the ESP in deep sleep mode depends on the type and is in the range of 5uA-20uA (as in micro Amperes): +- ESP32: 10uA +- ESP32 S3: 8uA +- ESP32 S2: 20uA +- ESP32 C3: 5uA +- ESP8266: 20uA (not supported in this usermod) + +However, there is usually additional components on a controller that increase the value: +- Power LED: the power LED on a ESP board draws 500uA - 1mA +- LDO: the voltage regulator also draws idle current. Depending on the type used this can be around 50uA up to 10mA (LM1117). Special low power LDOs with very low idle currents do exist +- Digital LEDs: WS2812 for example draw a current of about 1mA per LED. To make good use of this usermod it is required to power them off using MOSFETs or a Relay + +For lowest power consumption, remove the Power LED and make sure your board does not use an LM1117. On a ESP32 C3 Supermini with the power LED removed (no other modifications) powered through the 5V pin I measured a current draw of 50uA in deep sleep. + +# Useable GPIOs + +The GPIOs that can be used to wake the ESP from deep sleep are limited. Only pins connected to the internal RTC unit can be used: + +- ESP32: GPIO 0, 2, 4, 12-15, 25-39 +- ESP32 S3: GPIO 0-21 +- ESP32 S2: GPIO 0-21 +- ESP32 C3: GPIO 0-5 +- ESP8266 is not supported in this usermod + +You can however use the selected wake-up pin normally in WLED, it only gets activated as a wake-up pin when your LEDs are powered down. + +# Limitations + +To keep this usermod simple and easy to use, it is a very basic implementation of the low-power capabilities provided by the ESP. If you need more advanced control you are welcome to implement your own version based on this usermod. + +## Usermod installation + +Use `#define USERMOD_DEEP_SLEEP` in wled.h or `-D USERMOD_DEEP_SLEEP` in your platformio.ini. Settings can be changed in the usermod config UI. + +### Define Settings + +There are five parameters you can set: + +- GPIO: the pin to use for wake-up +- WakeWhen High/Low: the pin state that triggers the wake-up +- Pull-up/down disable: enable or disable the internal pullup resistors during sleep (does not affect normal use while running) +- Wake after: if set larger than 0, ESP will automatically wake-up after this many seconds (Turn LEDs on after power up/reset is overriden, it will always turn on) +- Delay sleep: if set larger than 0, ESP will not go to sleep for this many seconds after you power it off. Timer is reset when switched back on during this time. + +To override the default settings, place the `#define` in wled.h or add `-D DEEPSLEEP_xxx` to your platformio_override.ini build flags + +* `DEEPSLEEP_WAKEUPPIN x` - define the pin to be used for wake-up, see list of useable pins above. The pin can be used normally as a button pin in WLED. +* `DEEPSLEEP_WAKEWHENHIGH` - if defined, wakes up when pin goes high (default is low) +* `DEEPSLEEP_DISABLEPULL` - if defined, internal pullup/pulldown is disabled in deep sleep (default is ebnabled) +* `DEEPSLEEP_WAKEUPINTERVAL` - number of seconds after which a wake-up happens automatically, sooner if button is pressed. 0 = never. accuracy is about 2% +* `DEEPSLEEP_DELAY` - delay between power-off and sleep + +example for env build flags: + `-D USERMOD_DEEP_SLEEP` + `-D DEEPSLEEP_WAKEUPPIN=4` + `-D DEEPSLEEP_DISABLEPULL=0` ;enable pull-up/down resistors by default + `-D DEEPSLEEP_WAKEUPINTERVAL=43200` ;wake up after 12 hours (or when button is pressed) + +### Hardware Setup + +To wake from deep-sleep an external trigger signal on the configured GPIO is required. When using timed-only wake-up, use a GPIO that has an on-board pull-up resistor (GPIO0 on most boards). When using push-buttons it is highly recommended to use an external pull-up resistor: not all IO's on all devices have properly working internal resistors. + +Using sensors like PIR, IR, touch sensors or any other sensor with a digital output can be used instead of a button. + +now go on and save some power +@dedehai + +## Change log +2024-09 +* Initial version +2024-10 +* Changed from #define configuration to UI configuration \ No newline at end of file diff --git a/usermods/deep_sleep/usermod_deep_sleep.h b/usermods/deep_sleep/usermod_deep_sleep.h new file mode 100644 index 000000000..7f4efd5ca --- /dev/null +++ b/usermods/deep_sleep/usermod_deep_sleep.h @@ -0,0 +1,227 @@ +#pragma once + +#include "wled.h" +#include "driver/rtc_io.h" + +#ifdef ESP8266 +#error The "Deep Sleep" usermod does not support ESP8266 +#endif + +#ifndef DEEPSLEEP_WAKEUPPIN +#define DEEPSLEEP_WAKEUPPIN 0 +#endif +#ifndef DEEPSLEEP_WAKEWHENHIGH +#define DEEPSLEEP_WAKEWHENHIGH 0 +#endif +#ifndef DEEPSLEEP_DISABLEPULL +#define DEEPSLEEP_DISABLEPULL 1 +#endif +#ifndef DEEPSLEEP_WAKEUPINTERVAL +#define DEEPSLEEP_WAKEUPINTERVAL 0 +#endif +#ifndef DEEPSLEEP_DELAY +#define DEEPSLEEP_DELAY 1 +#endif + +RTC_DATA_ATTR bool powerup = true; // variable in RTC data persists on a reboot + +class DeepSleepUsermod : public Usermod { + + private: + + bool enabled = true; + bool initDone = false; + uint8_t wakeupPin = DEEPSLEEP_WAKEUPPIN; + uint8_t wakeWhenHigh = DEEPSLEEP_WAKEWHENHIGH; // wake up when pin goes high if 1, triggers on low if 0 + bool noPull = true; // use pullup/pulldown resistor + int wakeupAfter = DEEPSLEEP_WAKEUPINTERVAL; // in seconds, <=0: button only + int sleepDelay = DEEPSLEEP_DELAY; // in seconds, 0 = immediate + int delaycounter = 5; // delay deep sleep at bootup until preset settings are applied + uint32_t lastLoopTime = 0; + // string that are used multiple time (this will save some flash memory) + static const char _name[]; + static const char _enabled[]; + + bool pin_is_valid(uint8_t wakePin) { + #ifdef CONFIG_IDF_TARGET_ESP32 //ESP32: GPIOs 0,2,4, 12-15, 25-39 can be used for wake-up + if (wakePin == 0 || wakePin == 2 || wakePin == 4 || (wakePin >= 12 && wakePin <= 15) || (wakePin >= 25 && wakePin <= 27) || (wakePin >= 32 && wakePin <= 39)) { + return true; + } + #endif + #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2) //ESP32 S3 & S3: GPIOs 0-21 can be used for wake-up + if (wakePin <= 21) { + return true; + } + #endif + #ifdef CONFIG_IDF_TARGET_ESP32C3 // ESP32 C3: GPIOs 0-5 can be used for wake-up + if (wakePin <= 5) { + return true; + } + #endif + DEBUG_PRINTLN(F("Error: unsupported deep sleep wake-up pin")); + return false; + } + + public: + + inline void enable(bool enable) { enabled = enable; } // Enable/Disable the usermod + inline bool isEnabled() { return enabled; } //Get usermod enabled/disabled state + + // setup is called at boot (or in this case after every exit of sleep mode) + void setup() { + //TODO: if the de-init of RTC pins is required to do it could be done here + //rtc_gpio_deinit(wakeupPin); + initDone = true; + } + + void loop() { + if (!enabled || !offMode) { // disabled or LEDs are on + lastLoopTime = 0; // reset timer + return; + } + + if (sleepDelay > 0) { + if(lastLoopTime == 0) lastLoopTime = millis(); // initialize + if (millis() - lastLoopTime < sleepDelay * 1000) { + return; // wait until delay is over + } + } + + if(powerup == false && delaycounter) { // delay sleep in case a preset is being loaded and turnOnAtBoot is disabled (handleIO() does enable offMode temporarily in this case) + delaycounter--; + if(delaycounter == 2 && offMode) { // force turn on, no matter the settings (device is bricked if user set sleepDelay=0, no bootup preset and turnOnAtBoot=false) + if (briS == 0) bri = 10; // turn on at low brightness + else bri = briS; + strip.setBrightness(bri); // needed to make handleIO() not turn off LEDs (really? does not help in bootup preset) + offMode = false; + applyPresetWithFallback(0, CALL_MODE_INIT, FX_MODE_STATIC, 0); // try to apply preset 0, fallback to static + if (rlyPin >= 0) { + digitalWrite(rlyPin, (rlyMde ? HIGH : LOW)); // turn relay on TODO: this should be done by wled, what function to call? + } + } + return; + } + + DEBUG_PRINTLN(F("DeepSleep UM: entering deep sleep...")); + powerup = false; // turn leds on in all subsequent bootups (overrides Turn LEDs on after power up/reset' at reboot) + if(!pin_is_valid(wakeupPin)) return; + esp_err_t halerror = ESP_OK; + pinMode(wakeupPin, INPUT); // make sure GPIO is input with pullup/pulldown disabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); //disable all wake-up sources (just in case) + + if(wakeupAfter) + esp_sleep_enable_timer_wakeup((uint64_t)wakeupAfter * (uint64_t)1e6); //sleep for x seconds + + #if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3 + if(noPull) + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING); + else { // enable pullup/pulldown resistor + if(wakeWhenHigh) + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY); + else + gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY); + } + if(wakeWhenHigh) + halerror = esp_deep_sleep_enable_gpio_wakeup(1<(0 = never)');")); + oappend(SET_F("addInfo('DeepSleep:delaySleep',1,'seconds (0 = sleep at powerup)');")); // first string is suffix, second string is prefix + } + + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() { + return USERMOD_ID_DEEP_SLEEP; + } + +}; + +// add more strings here to reduce flash memory usage +const char DeepSleepUsermod::_name[] PROGMEM = "DeepSleep"; +const char DeepSleepUsermod::_enabled[] PROGMEM = "enabled"; \ No newline at end of file diff --git a/usermods/rgb-rotary-encoder/readme.md b/usermods/rgb-rotary-encoder/readme.md index ba5aad4df..653179179 100644 --- a/usermods/rgb-rotary-encoder/readme.md +++ b/usermods/rgb-rotary-encoder/readme.md @@ -9,7 +9,7 @@ The actual / original code that controls the LED modes is from Adam Zeloof. I ta It was quite a bit more work than I hoped, but I got there eventually :) ## Requirements -* "ESP Rotary" by Lennart Hennigs, v1.5.0 or higher: https://github.com/LennartHennigs/ESPRotary +* "ESP Rotary" by Lennart Hennigs, v2.1.1 or higher: https://github.com/LennartHennigs/ESPRotary ## Usermod installation Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D RGB_ROTARY_ENCODER`. @@ -20,7 +20,7 @@ ESP32: extends = env:esp32dev build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D RGB_ROTARY_ENCODER lib_deps = ${esp32.lib_deps} - lennarthennigs/ESP Rotary@^1.5.0 + lennarthennigs/ESP Rotary@^2.1.1 ``` ESP8266 / D1 Mini: @@ -29,7 +29,7 @@ ESP8266 / D1 Mini: extends = env:d1_mini build_flags = ${common.build_flags_esp8266} -D RGB_ROTARY_ENCODER lib_deps = ${esp8266.lib_deps} - lennarthennigs/ESP Rotary@^1.5.0 + lennarthennigs/ESP Rotary@^2.1.1 ``` ## How to connect the board to your ESP diff --git a/usermods/seven_segment_display_reloaded/readme.md b/usermods/seven_segment_display_reloaded/readme.md index a3398c3e5..94788df7e 100644 --- a/usermods/seven_segment_display_reloaded/readme.md +++ b/usermods/seven_segment_display_reloaded/readme.md @@ -9,7 +9,7 @@ Very loosely based on the existing usermod "seven segment display". Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`. -For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions. +For the auto brightness option, the usermod SN_Photoresistor or BH1750_V2 has to be installed as well. See SN_Photoresistor/readme.md or BH1750_V2/readme.md for instructions. ## Settings All settings can be controlled via the usermod settings page. @@ -28,10 +28,10 @@ Enables the blinking colon(s) if they are defined Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`) ### enable-auto-brightness -Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed. +Enables the auto brightness feature. Can be used only when the usermods SN_Photoresistor or BH1750_V2 are installed. ### auto-brightness-min / auto-brightness-max -The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here. +The lux value calculated from usermod SN_Photoresistor or BH1750_V2 will be mapped to the values defined here. The mapping, 0 - 1000 lux, will be mapped to auto-brightness-min and auto-brightness-max WLED current protection will override the calculated value if it is too high. diff --git a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h index 1436f8fc4..72f4c2dd6 100644 --- a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h +++ b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h @@ -97,6 +97,11 @@ private: #else void* ptr = nullptr; #endif +#ifdef USERMOD_BH1750 + Usermod_BH1750* bh1750 = nullptr; +#else + void* bh1750 = nullptr; +#endif void _overlaySevenSegmentDraw() { int displayMaskLen = static_cast(umSSDRDisplayMask.length()); @@ -387,6 +392,9 @@ public: #ifdef USERMOD_SN_PHOTORESISTOR ptr = (Usermod_SN_Photoresistor*) UsermodManager::lookup(USERMOD_ID_SN_PHOTORESISTOR); #endif + #ifdef USERMOD_BH1750 + bh1750 = (Usermod_BH1750*) UsermodManager::lookup(USERMOD_ID_BH1750); + #endif DEBUG_PRINTLN(F("Setup done")); } @@ -410,6 +418,20 @@ public: umSSDRLastRefresh = millis(); } #endif + #ifdef USERMOD_BH1750 + if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) { + if (bh1750 != nullptr) { + float lux = bh1750->getIlluminance(); + uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax); + if (bri != brightness) { + DEBUG_PRINTF("Adjusting brightness based on lux value: %.2f lx, new brightness: %d\n", lux, brightness); + bri = brightness; + stateUpdated(1); + } + } + umSSDRLastRefresh = millis(); + } + #endif } void handleOverlayDraw() { diff --git a/usermods/smartnest/usermod_smartnest.h b/usermods/smartnest/usermod_smartnest.h index 92d524c88..9d21ef2e7 100644 --- a/usermods/smartnest/usermod_smartnest.h +++ b/usermods/smartnest/usermod_smartnest.h @@ -49,7 +49,7 @@ private: void setColor(int r, int g, int b) { - strip.setColor(0, r, g, b); + strip.getMainSegment().setColor(0, RGBW32(r, g, b, 0)); stateUpdated(CALL_MODE_DIRECT_CHANGE); char msg[18] {}; sprintf(msg, "rgb(%d,%d,%d)", r, g, b); diff --git a/usermods/usermod_v2_RF433/readme.md b/usermods/usermod_v2_RF433/readme.md new file mode 100644 index 000000000..43919f11b --- /dev/null +++ b/usermods/usermod_v2_RF433/readme.md @@ -0,0 +1,18 @@ +# RF433 remote usermod + +Usermod for controlling WLED using a generic 433 / 315MHz remote and simple 3-pin receiver +See for compatibility details + +## Build + +- Create a `platformio_override.ini` file at the root of the wled source directory if not already present +- Copy the `433MHz RF remote example for esp32dev` section from `platformio_override.sample.ini` into it +- Duplicate/adjust for other boards + +## Usage + +- Connect receiver to a free pin +- Set pin in Config->Usermods +- Info pane will show the last received button code +- Upload the remote433.json sample file in this folder to the ESP with the file editor at [http://\[wled-ip\]/edit](http://ip/edit) +- Edit as necessary, the key is the button number retrieved from the info pane, and the "cmd" can be either an [HTTP API](https://kno.wled.ge/interfaces/http-api/) or a [JSON API](https://kno.wled.ge/interfaces/json-api/) command. \ No newline at end of file diff --git a/usermods/usermod_v2_RF433/remote433.json b/usermods/usermod_v2_RF433/remote433.json new file mode 100644 index 000000000..d5d930a81 --- /dev/null +++ b/usermods/usermod_v2_RF433/remote433.json @@ -0,0 +1,34 @@ +{ + "13985576": { + "cmnt": "Toggle Power using HTTP API", + "cmd": "T=2" + }, + "3670817": { + "cmnt": "Force Power ON using HTTP API", + "cmd": "T=1" + }, + "13985572": { + "cmnt": "Set brightness to 200 using JSON API", + "cmd": {"bri":200} + }, + "3670818": { + "cmnt": "Run Preset 1 using JSON API", + "cmd": {"ps":1} + }, + "13985570": { + "cmnt": "Increase brightness by 40 using HTTP API", + "cmd": "A=~40" + }, + "13985569": { + "cmnt": "Decrease brightness by 40 using HTTP API", + "cmd": "A=~-40" + }, + "7608836": { + "cmnt": "Start 1min timer using JSON API", + "cmd": {"nl":{"on":true,"dur":1,"mode":0}} + }, + "7608840": { + "cmnt": "Select random effect on all segments using JSON API", + "cmd": {"seg":{"fx":"r"}} + } +} \ No newline at end of file diff --git a/usermods/usermod_v2_RF433/usermod_v2_RF433.h b/usermods/usermod_v2_RF433/usermod_v2_RF433.h new file mode 100644 index 000000000..ebaf433f1 --- /dev/null +++ b/usermods/usermod_v2_RF433/usermod_v2_RF433.h @@ -0,0 +1,183 @@ +#pragma once + +#include "wled.h" +#include "Arduino.h" +#include + +#define RF433_BUSWAIT_TIMEOUT 24 + +class RF433Usermod : public Usermod +{ +private: + RCSwitch mySwitch = RCSwitch(); + unsigned long lastCommand = 0; + unsigned long lastTime = 0; + + bool modEnabled = true; + int8_t receivePin = -1; + + static const char _modName[]; + static const char _modEnabled[]; + static const char _receivePin[]; + + bool initDone = false; + +public: + + void setup() + { + mySwitch.disableReceive(); + if (modEnabled) + { + mySwitch.enableReceive(receivePin); + } + initDone = true; + } + + /* + * connected() is called every time the WiFi is (re)connected + * Use it to initialize network interfaces + */ + void connected() + { + } + + void loop() + { + if (!modEnabled || strip.isUpdating()) + return; + + if (mySwitch.available()) + { + unsigned long receivedCommand = mySwitch.getReceivedValue(); + mySwitch.resetAvailable(); + + // Discard duplicates, limit long press repeat + if (lastCommand == receivedCommand && millis() - lastTime < 800) + return; + + lastCommand = receivedCommand; + lastTime = millis(); + + DEBUG_PRINT(F("RF433 Receive: ")); + DEBUG_PRINTLN(receivedCommand); + + if(!remoteJson433(receivedCommand)) + DEBUG_PRINTLN(F("RF433: unknown button")); + } + } + + // Add last received button to info pane + void addToJsonInfo(JsonObject &root) + { + if (!initDone) + return; // prevent crash on boot applyPreset() + JsonObject user = root["u"]; + if (user.isNull()) + user = root.createNestedObject("u"); + + JsonArray switchArr = user.createNestedArray("RF433 Last Received"); // name + switchArr.add(lastCommand); + } + + void addToConfig(JsonObject &root) + { + JsonObject top = root.createNestedObject(FPSTR(_modName)); // usermodname + top[FPSTR(_modEnabled)] = modEnabled; + JsonArray pinArray = top.createNestedArray("pin"); + pinArray.add(receivePin); + + DEBUG_PRINTLN(F(" config saved.")); + } + + bool readFromConfig(JsonObject &root) + { + JsonObject top = root[FPSTR(_modName)]; + if (top.isNull()) + { + DEBUG_PRINT(FPSTR(_modName)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } + getJsonValue(top[FPSTR(_modEnabled)], modEnabled); + getJsonValue(top["pin"][0], receivePin); + + DEBUG_PRINTLN(F("config (re)loaded.")); + + // Redo init on update + if(initDone) + setup(); + + return true; + } + + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() + { + return USERMOD_ID_RF433; + } + + // this function follows the same principle as decodeIRJson() / remoteJson() + bool remoteJson433(int button) + { + char objKey[14]; + bool parsed = false; + + if (!requestJSONBufferLock(22)) return false; + + sprintf_P(objKey, PSTR("\"%d\":"), button); + + unsigned long start = millis(); + while (strip.isUpdating() && millis()-start < RF433_BUSWAIT_TIMEOUT) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches + + // attempt to read command from remote.json + readObjectFromFile(PSTR("/remote433.json"), objKey, pDoc); + JsonObject fdo = pDoc->as(); + if (fdo.isNull()) { + // the received button does not exist + releaseJSONBufferLock(); + return parsed; + } + + String cmdStr = fdo["cmd"].as(); + JsonObject jsonCmdObj = fdo["cmd"]; //object + + if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is() + { + // HTTP API command + String apireq = "win"; apireq += '&'; // reduce flash string usage + if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix + if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) { + char tmp[10]; + sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId()); + cmdStr += tmp; + } + fdo.clear(); // clear JSON buffer (it is no longer needed) + handleSet(nullptr, cmdStr, false); // no stateUpdated() call here + stateUpdated(CALL_MODE_BUTTON); + parsed = true; + } else { + // command is JSON object + if (jsonCmdObj[F("psave")].isNull()) + deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET); + else { + uint8_t psave = jsonCmdObj[F("psave")].as(); + char pname[33]; + sprintf_P(pname, PSTR("IR Preset %d"), psave); + fdo.clear(); + if (psave > 0 && psave < 251) savePreset(psave, pname, fdo); + } + parsed = true; + } + releaseJSONBufferLock(); + return parsed; + } +}; + +const char RF433Usermod::_modName[] PROGMEM = "RF433 Remote"; +const char RF433Usermod::_modEnabled[] PROGMEM = "Enabled"; +const char RF433Usermod::_receivePin[] PROGMEM = "RX Pin"; + diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio–override.sample.ini b/usermods/usermod_v2_four_line_display_ALT/platformio_override.sample.ini similarity index 59% rename from usermods/usermod_v2_rotary_encoder_ui_ALT/platformio–override.sample.ini rename to usermods/usermod_v2_four_line_display_ALT/platformio_override.sample.ini index 6b32c71fb..e59637453 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio–override.sample.ini +++ b/usermods/usermod_v2_four_line_display_ALT/platformio_override.sample.ini @@ -7,11 +7,12 @@ platform = ${esp32.platform} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp32} - -D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY - -D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19 -upload_speed = 460800 + -D USERMOD_FOUR_LINE_DISPLAY + -D FLD_TYPE=SH1106 + -D I2CSCLPIN=27 + -D I2CSDAPIN=26 + lib_deps = ${esp32.lib_deps} U8g2@~2.34.4 Wire - diff --git a/usermods/usermod_v2_four_line_display_ALT/readme.md b/usermods/usermod_v2_four_line_display_ALT/readme.md index a8f386dac..39bb5d28e 100644 --- a/usermods/usermod_v2_four_line_display_ALT/readme.md +++ b/usermods/usermod_v2_four_line_display_ALT/readme.md @@ -1,16 +1,8 @@ # I2C/SPI 4 Line Display Usermod ALT -Thank you to the authors of the original version of these usermods. It would not have been possible without them! -"usermod_v2_four_line_display" -"usermod_v2_rotary_encoder_ui" +This usermod could be used in compination with `usermod_v2_rotary_encoder_ui_ALT`. -The core of these usermods are a copy of the originals. The main changes are to the FourLineDisplay usermod. -The display usermod UI has been completely changed. - - -The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod. -Without the display, it functions identical to the original. -The original "usermod_v2_auto_save" will not work with the display just yet. +## Functionalities Press the encoder to cycle through the options: * Brightness @@ -18,26 +10,18 @@ Press the encoder to cycle through the options: * Intensity * Palette * Effect -* Main Color (only if display is used) -* Saturation (only if display is used) +* Main Color +* Saturation -Press and hold the encoder to display Network Info. If AP is active, it will display AP, SSID and password +Press and hold the encoder to display Network Info. If AP is active, it will display the AP, SSID and Password -Also shows if the timer is enabled +Also shows if the timer is enabled. [See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI) ## Installation -Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions. - -Copy the example `platformio_override.sample.ini` from the usermod_v2_rotary_encoder_ui_ALT folder to the root directory of your particular build and rename it to `platformio_override.ini`. - -This file should be placed in the same directory as `platformio.ini`. - -Then, to activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, - or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file - +Copy the example `platformio_override.sample.ini` to the root directory of your particular build. ## Configuration diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio_override.sample.ini b/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio_override.sample.ini new file mode 100644 index 000000000..8a88fd6b5 --- /dev/null +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio_override.sample.ini @@ -0,0 +1,14 @@ +[platformio] +default_envs = esp32dev + +[env:esp32dev] +board = esp32dev +platform = ${esp32.platform} +build_unflags = ${common.build_unflags} +build_flags = + ${common.build_flags_esp32} + -D USERMOD_ROTARY_ENCODER_UI + -D USERMOD_ROTARY_ENCODER_GPIO=INPUT + -D ENCODER_DT_PIN=21 + -D ENCODER_CLK_PIN=23 + -D ENCODER_SW_PIN=0 diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md b/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md index 10db879fb..c46e87663 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md @@ -1,16 +1,8 @@ # Rotary Encoder UI Usermod ALT -Thank you to the authors of the original version of these usermods. It would not have been possible without them! -"usermod_v2_four_line_display" -"usermod_v2_rotary_encoder_ui" +This usermod supports the UI of the `usermod_v2_rotary_encoder_ui_ALT`. -The core of these usermods are a copy of the originals. The main changes are to the FourLineDisplay usermod. -The display usermod UI has been completely changed. - - -The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod. -Without the display, it functions identical to the original. -The original "usermod_v2_auto_save" will not work with the display just yet. +## Functionalities Press the encoder to cycle through the options: * Brightness @@ -21,8 +13,7 @@ Press the encoder to cycle through the options: * Main Color (only if display is used) * Saturation (only if display is used) -Press and hold the encoder to display Network Info - if AP is active, it will display the AP, SSID and Password +Press and hold the encoder to display Network Info. If AP is active, it will display the AP, SSID and Password Also shows if the timer is enabled. @@ -30,9 +21,7 @@ Also shows if the timer is enabled. ## Installation -Copy the example `platformio_override.sample.ini` to the root directory of your particular build and rename it to `platformio_override.ini`. - -To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, or add `-D USE_ALT_DISPlAY` to your `platformio_override.ini` file +Copy the example `platformio_override.sample.ini` to the root directory of your particular build. ### Define Your Options @@ -40,7 +29,6 @@ To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE * `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details) -* `USE_ALT_DISPlAY` - Mandatory to use Four Line Display * `ENCODER_DT_PIN` - defaults to 18 * `ENCODER_CLK_PIN` - defaults to 5 * `ENCODER_SW_PIN` - defaults to 19 @@ -50,7 +38,7 @@ To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE ### PlatformIO requirements -Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`. +No special requirements. ## Change Log diff --git a/usermods/word-clock-matrix/usermod_word_clock_matrix.h b/usermods/word-clock-matrix/usermod_word_clock_matrix.h index 506c1275e..82499c0ce 100644 --- a/usermods/word-clock-matrix/usermod_word_clock_matrix.h +++ b/usermods/word-clock-matrix/usermod_word_clock_matrix.h @@ -31,14 +31,14 @@ public: //strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); //select first two segments (background color + FX settable) - WS2812FX::Segment &seg = strip.getSegment(0); + Segment &seg = strip.getSegment(0); seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF))); strip.getSegment(0).setOption(0, false); strip.getSegment(0).setOption(2, false); //other segments are text for (int i = 1; i < 10; i++) { - WS2812FX::Segment &seg = strip.getSegment(i); + Segment &seg = strip.getSegment(i); seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF))); strip.getSegment(i).setOption(0, true); strip.setBrightness(64); @@ -80,61 +80,61 @@ public: void displayTime(byte hour, byte minute) { bool isToHour = false; //true if minute > 30 - strip.setSegment(0, 0, 64); // background - strip.setSegment(1, 0, 2); //It is + strip.getSegment(0).setGeometry(0, 64); // background + strip.getSegment(1).setGeometry(0, 2); //It is - strip.setSegment(2, 0, 0); - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //past - strip.setSegment(6, 0, 0); //to - strip.setSegment(8, 0, 0); //disable o'clock + strip.getSegment(2).setGeometry(0, 0); + strip.getSegment(3).setGeometry(0, 0); //disable minutes + strip.getSegment(4).setGeometry(0, 0); //past + strip.getSegment(6).setGeometry(0, 0); //to + strip.getSegment(8).setGeometry(0, 0); //disable o'clock if (hour < 24) //valid time, display { if (minute == 30) { - strip.setSegment(2, 3, 6); //half - strip.setSegment(3, 0, 0); //minutes + strip.getSegment(2).setGeometry(3, 6); //half + strip.getSegment(3).setGeometry(0, 0); //minutes } else if (minute == 15 || minute == 45) { - strip.setSegment(3, 0, 0); //minutes + strip.getSegment(3).setGeometry(0, 0); //minutes } else if (minute == 10) { - //strip.setSegment(5, 6, 8); //ten + //strip.getSegment(5).setGeometry(6, 8); //ten } else if (minute == 5) { - //strip.setSegment(5, 16, 18); //five + //strip.getSegment(5).setGeometry(16, 18); //five } else if (minute == 0) { - strip.setSegment(3, 0, 0); //minutes + strip.getSegment(3).setGeometry(0, 0); //minutes //hourChime(); } else { - strip.setSegment(3, 18, 22); //minutes + strip.getSegment(3).setGeometry(18, 22); //minutes } //past or to? if (minute == 0) { //full hour - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //disable past - strip.setSegment(6, 0, 0); //disable to - strip.setSegment(8, 60, 64); //o'clock + strip.getSegment(3).setGeometry(0, 0); //disable minutes + strip.getSegment(4).setGeometry(0, 0); //disable past + strip.getSegment(6).setGeometry(0, 0); //disable to + strip.getSegment(8).setGeometry(60, 64); //o'clock } else if (minute > 34) { - //strip.setSegment(6, 22, 24); //to + //strip.getSegment(6).setGeometry(22, 24); //to //minute = 60 - minute; isToHour = true; } else { - //strip.setSegment(4, 24, 27); //past + //strip.getSegment(4).setGeometry(24, 27); //past //isToHour = false; } } @@ -143,68 +143,68 @@ public: if (minute <= 4) { - strip.setSegment(3, 0, 0); //nothing - strip.setSegment(5, 0, 0); //nothing - strip.setSegment(6, 0, 0); //nothing - strip.setSegment(8, 60, 64); //o'clock + strip.getSegment(3).setGeometry(0, 0); //nothing + strip.getSegment(5).setGeometry(0, 0); //nothing + strip.getSegment(6).setGeometry(0, 0); //nothing + strip.getSegment(8).setGeometry(60, 64); //o'clock } else if (minute <= 9) { - strip.setSegment(5, 16, 18); // five past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(16, 18); // five past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 14) { - strip.setSegment(5, 6, 8); // ten past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(6, 8); // ten past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 19) { - strip.setSegment(5, 8, 12); // quarter past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(8, 12); // quarter past + strip.getSegment(3).setGeometry(0, 0); //minutes + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 24) { - strip.setSegment(5, 12, 16); // twenty past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(12, 16); // twenty past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 29) { - strip.setSegment(5, 12, 18); // twenty-five past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(12, 18); // twenty-five past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 34) { - strip.setSegment(5, 3, 6); // half past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(3, 6); // half past + strip.getSegment(3).setGeometry(0, 0); //minutes + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 39) { - strip.setSegment(5, 12, 18); // twenty-five to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(12, 18); // twenty-five to + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 44) { - strip.setSegment(5, 12, 16); // twenty to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(12, 16); // twenty to + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 49) { - strip.setSegment(5, 8, 12); // quarter to - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(8, 12); // quarter to + strip.getSegment(3).setGeometry(0, 0); //minutes + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 54) { - strip.setSegment(5, 6, 8); // ten to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(6, 8); // ten to + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 59) { - strip.setSegment(5, 16, 18); // five to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(16, 18); // five to + strip.getSegment(6).setGeometry(22, 24); //to } //hours @@ -220,45 +220,45 @@ public: switch (hour) { case 1: - strip.setSegment(7, 27, 29); + strip.getSegment(7).setGeometry(27, 29); break; //one case 2: - strip.setSegment(7, 35, 37); + strip.getSegment(7).setGeometry(35, 37); break; //two case 3: - strip.setSegment(7, 29, 32); + strip.getSegment(7).setGeometry(29, 32); break; //three case 4: - strip.setSegment(7, 32, 35); + strip.getSegment(7).setGeometry(32, 35); break; //four case 5: - strip.setSegment(7, 37, 40); + strip.getSegment(7).setGeometry(37, 40); break; //five case 6: - strip.setSegment(7, 43, 45); + strip.getSegment(7).setGeometry(43, 45); break; //six case 7: - strip.setSegment(7, 40, 43); + strip.getSegment(7).setGeometry(40, 43); break; //seven case 8: - strip.setSegment(7, 45, 48); + strip.getSegment(7).setGeometry(45, 48); break; //eight case 9: - strip.setSegment(7, 48, 50); + strip.getSegment(7).setGeometry(48, 50); break; //nine case 10: - strip.setSegment(7, 54, 56); + strip.getSegment(7).setGeometry(54, 56); break; //ten case 11: - strip.setSegment(7, 50, 54); + strip.getSegment(7).setGeometry(50, 54); break; //eleven case 12: - strip.setSegment(7, 56, 60); + strip.getSegment(7).setGeometry(56, 60); break; //twelve } selectWordSegments(true); - applyMacro(1); + applyPreset(1); } void timeOfDay() diff --git a/usermods/word-clock-matrix/word-clock-matrix.cpp b/usermods/word-clock-matrix/word-clock-matrix.cpp deleted file mode 100644 index 67c5b1e47..000000000 --- a/usermods/word-clock-matrix/word-clock-matrix.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include "wled.h" -/* - * This v1 usermod file allows you to add own functionality to WLED more easily - * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h) - * If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE) - * - * Consider the v2 usermod API if you need a more advanced feature set! - */ - - -uint8_t minuteLast = 99; -int dayBrightness = 128; -int nightBrightness = 16; - -//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) - -//gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() -{ -saveMacro(14, "A=128", false); -saveMacro(15, "A=64", false); -saveMacro(16, "A=16", false); - -saveMacro(1, "&FX=0&R=255&G=255&B=255", false); - -//strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); - - //select first two segments (background color + FX settable) - Segment &seg = strip.getSegment(0); - seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF))); - strip.getSegment(0).setOption(0, false); - strip.getSegment(0).setOption(2, false); - //other segments are text - for (int i = 1; i < 10; i++) - { - Segment &seg = strip.getSegment(i); - seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF))); - strip.getSegment(i).setOption(0, true); - strip.setBrightness(128); - } -} - -//gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() -{ -} - -void selectWordSegments(bool state) -{ - for (int i = 1; i < 10; i++) - { - //Segment &seg = strip.getSegment(i); - strip.getSegment(i).setOption(0, state); - // strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); - //seg.mode = 12; - //seg.palette = 1; - //strip.setBrightness(255); - } - strip.getSegment(0).setOption(0, !state); -} - -void hourChime() -{ - //strip.resetSegments(); - selectWordSegments(true); - colorUpdated(CALL_MODE_FX_CHANGED); - //savePreset(255); - selectWordSegments(false); - //strip.getSegment(0).setOption(0, true); - strip.getSegment(0).setOption(2, true); - applyPreset(12); - colorUpdated(CALL_MODE_FX_CHANGED); -} - -void displayTime(byte hour, byte minute) -{ - bool isToHour = false; //true if minute > 30 - strip.setSegment(0, 0, 64); // background - strip.setSegment(1, 0, 2); //It is - - strip.setSegment(2, 0, 0); - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //past - strip.setSegment(6, 0, 0); //to - strip.setSegment(8, 0, 0); //disable o'clock - - if (hour < 24) //valid time, display - { - if (minute == 30) - { - strip.setSegment(2, 3, 6); //half - strip.setSegment(3, 0, 0); //minutes - } - else if (minute == 15 || minute == 45) - { - strip.setSegment(3, 0, 0); //minutes - } - else if (minute == 10) - { - //strip.setSegment(5, 6, 8); //ten - } - else if (minute == 5) - { - //strip.setSegment(5, 16, 18); //five - } - else if (minute == 0) - { - strip.setSegment(3, 0, 0); //minutes - //hourChime(); - } - else - { - strip.setSegment(3, 18, 22); //minutes - } - - //past or to? - if (minute == 0) - { //full hour - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //disable past - strip.setSegment(6, 0, 0); //disable to - strip.setSegment(8, 60, 64); //o'clock - } - else if (minute > 34) - { - //strip.setSegment(6, 22, 24); //to - //minute = 60 - minute; - isToHour = true; - } - else - { - //strip.setSegment(4, 24, 27); //past - //isToHour = false; - } - } - else - { //temperature display - } - - //byte minuteRem = minute %10; - - if (minute <= 4) - { - strip.setSegment(3, 0, 0); //nothing - strip.setSegment(5, 0, 0); //nothing - strip.setSegment(6, 0, 0); //nothing - strip.setSegment(8, 60, 64); //o'clock - } - else if (minute <= 9) - { - strip.setSegment(5, 16, 18); // five past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 14) - { - strip.setSegment(5, 6, 8); // ten past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 19) - { - strip.setSegment(5, 8, 12); // quarter past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 24) - { - strip.setSegment(5, 12, 16); // twenty past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 29) - { - strip.setSegment(5, 12, 18); // twenty-five past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 34) - { - strip.setSegment(5, 3, 6); // half past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 39) - { - strip.setSegment(5, 12, 18); // twenty-five to - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 44) - { - strip.setSegment(5, 12, 16); // twenty to - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 49) - { - strip.setSegment(5, 8, 12); // quarter to - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 54) - { - strip.setSegment(5, 6, 8); // ten to - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 59) - { - strip.setSegment(5, 16, 18); // five to - strip.setSegment(6, 22, 24); //to - } - - //hours - if (hour > 23) - return; - if (isToHour) - hour++; - if (hour > 12) - hour -= 12; - if (hour == 0) - hour = 12; - - switch (hour) - { - case 1: - strip.setSegment(7, 27, 29); - break; //one - case 2: - strip.setSegment(7, 35, 37); - break; //two - case 3: - strip.setSegment(7, 29, 32); - break; //three - case 4: - strip.setSegment(7, 32, 35); - break; //four - case 5: - strip.setSegment(7, 37, 40); - break; //five - case 6: - strip.setSegment(7, 43, 45); - break; //six - case 7: - strip.setSegment(7, 40, 43); - break; //seven - case 8: - strip.setSegment(7, 45, 48); - break; //eight - case 9: - strip.setSegment(7, 48, 50); - break; //nine - case 10: - strip.setSegment(7, 54, 56); - break; //ten - case 11: - strip.setSegment(7, 50, 54); - break; //eleven - case 12: - strip.setSegment(7, 56, 60); - break; //twelve - } - -selectWordSegments(true); -applyMacro(1); -} - -void timeOfDay() { -// NOT USED: use timed macros instead - //Used to set brightness dependant of time of day - lights dimmed at night - - //monday to thursday and sunday - - if ((weekday(localTime) == 6) | (weekday(localTime) == 7)) { - if (hour(localTime) > 0 | hour(localTime) < 8) { - strip.setBrightness(nightBrightness); - } - else { - strip.setBrightness(dayBrightness); - } - } - else { - if (hour(localTime) < 6 | hour(localTime) >= 22) { - strip.setBrightness(nightBrightness); - } - else { - strip.setBrightness(dayBrightness); - } - } -} - -//loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() -{ - if (minute(localTime) != minuteLast) - { - updateLocalTime(); - //timeOfDay(); - minuteLast = minute(localTime); - displayTime(hour(localTime), minute(localTime)); - if (minute(localTime) == 0){ - hourChime(); - } - if (minute(localTime) == 1){ - //turn off background segment; - strip.getSegment(0).setOption(2, false); - //applyPreset(255); - } - } -} diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 2f24f745a..1459c4a0f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -14,18 +14,60 @@ #include "FX.h" #include "fcn_declare.h" -#define IBN 5100 + ////////////// + // DEV INFO // + ////////////// +/* + information for FX metadata strings: https://kno.wled.ge/interfaces/json-api/#effect-metadata + + Audio Reactive: use the following code to pass usermod variables to effect + + uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment + bool samplePeak = false; + float FFT_MajorPeak = 1.0; + uint8_t *fftResult = nullptr; + float *fftBin = nullptr; + um_data_t *um_data; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + volumeSmth = *(float*) um_data->u_data[0]; + volumeRaw = *(float*) um_data->u_data[1]; + fftResult = (uint8_t*) um_data->u_data[2]; + samplePeak = *(uint8_t*) um_data->u_data[3]; + FFT_MajorPeak = *(float*) um_data->u_data[4]; + my_magnitude = *(float*) um_data->u_data[5]; + maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element + binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element + fftBin = (float*) um_data->u_data[8]; + } else { + // add support for no audio data + um_data = simulateSound(SEGMENT.soundSim); + } +*/ + + +#define IBN 5100 // paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined) #define PALETTE_SOLID_WRAP (strip.paletteBlend == 1 || strip.paletteBlend == 3) #define PALETTE_MOVING_WRAP !(strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0)) #define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16)) +// a few constants needed for AudioReactive effects +// for 22Khz sampling +#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion) +#define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY) +// for 20Khz sampling +//#define MAX_FREQUENCY 10240 +//#define MAX_FREQ_LOG10 4.0103f +// for 10Khz sampling +//#define MAX_FREQUENCY 5120 +//#define MAX_FREQ_LOG10 3.71f + // effect utility functions uint8_t sin_gap(uint16_t in) { if (in & 0x100) return 0; - return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0 + return sin8_t(in + 192); // correct phase shift of sine so that it starts and stops at 0 } uint16_t triwave16(uint16_t in) { @@ -104,7 +146,7 @@ uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { uint32_t color = on ? color1 : color2; if (color == color1 && do_palette) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } else SEGMENT.fill(color); @@ -155,7 +197,7 @@ static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!; * if (bool rev == true) then LEDs are turned off in reverse order */ uint16_t color_wipe(bool rev, bool useRandomColors) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; uint32_t perc = strip.now % cycleTime; unsigned prog = (perc * 65535) / cycleTime; @@ -169,7 +211,7 @@ uint16_t color_wipe(bool rev, bool useRandomColors) { if (useRandomColors) { if (SEGENV.call == 0) { - SEGENV.aux0 = random8(); + SEGENV.aux0 = hw_random8(); SEGENV.step = 3; } if (SEGENV.step == 1) { //if flag set, change to new random color @@ -200,7 +242,7 @@ uint16_t color_wipe(bool rev, bool useRandomColors) { } else { SEGMENT.setPixelColor(index, back? col0 : col1); - if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem)); + if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, uint8_t(rem))); } } return FRAMETIME; @@ -261,7 +303,7 @@ uint16_t mode_random_color(void) { } if (SEGENV.call == 0) { - SEGENV.aux0 = random8(); + SEGENV.aux0 = hw_random8(); SEGENV.step = 2; } if (it != SEGENV.step) //new color @@ -271,7 +313,7 @@ uint16_t mode_random_color(void) { SEGENV.step = it; } - SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), uint8_t(fade))); return FRAMETIME; } static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01"; @@ -286,25 +328,25 @@ uint16_t mode_dynamic(void) { if(SEGENV.call == 0) { //SEGMENT.fill(BLACK); - for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); + for (unsigned i = 0; i < SEGLEN; i++) SEGENV.data[i] = hw_random8(); } uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15; uint32_t it = strip.now / cycleTime; if (it != SEGENV.step && SEGMENT.speed != 0) //new color { - for (int i = 0; i < SEGLEN; i++) { - if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); // random color index + for (unsigned i = 0; i < SEGLEN; i++) { + if (hw_random8() <= SEGMENT.intensity) SEGENV.data[i] = hw_random8(); // random color index } SEGENV.step = it; } if (SEGMENT.check1) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]), 16); } } else { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i])); } } @@ -335,11 +377,11 @@ uint16_t mode_breath(void) { counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048 if (counter < 16384) { if (counter > 8192) counter = 8192 - (counter - 8192); - var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170 + var = sin16_t(counter) / 103; //close to parabolic in range 0-8192, max val. 23170 } - unsigned lum = 30 + var; - for (int i = 0; i < SEGLEN; i++) { + uint8_t lum = 30 + var; + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -353,9 +395,9 @@ static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01"; */ uint16_t mode_fade(void) { unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10)); - unsigned lum = triwave16(counter) >> 8; + uint8_t lum = triwave16(counter) >> 8; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -368,7 +410,7 @@ static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!;01"; * Scan mode parent function */ uint16_t scan(bool dual) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; uint32_t perc = strip.now % cycleTime; int prog = (perc * 65535) / cycleTime; @@ -421,7 +463,7 @@ uint16_t mode_rainbow(void) { counter = counter >> 8; if (SEGMENT.intensity < 128){ - SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,128-SEGMENT.intensity)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,uint8_t(128-SEGMENT.intensity))); } else { SEGMENT.fill(SEGMENT.color_wheel(counter)); } @@ -438,7 +480,7 @@ uint16_t mode_rainbow_cycle(void) { unsigned counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { //intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16) uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter; SEGMENT.setPixelColor(i, SEGMENT.color_wheel(index)); @@ -458,7 +500,7 @@ static uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) uint32_t it = strip.now / cycleTime; bool usePalette = color1 == SEGCOLOR(0); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { uint32_t col = color2; if (usePalette) color1 = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0); if (theatre) { @@ -505,7 +547,7 @@ static uint16_t running_base(bool saw, bool dual=false) { unsigned x_scale = SEGMENT.intensity >> 2; uint32_t counter = (strip.now * SEGMENT.speed) >> 9; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned a = i*x_scale - counter; if (saw) { a &= 0xFF; @@ -517,13 +559,13 @@ static uint16_t running_base(bool saw, bool dual=false) { } a = 255 - a; } - uint8_t s = dual ? sin_gap(a) : sin8(a); + uint8_t s = dual ? sin_gap(a) : sin8_t(a); uint32_t ca = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s); if (dual) { unsigned b = (SEGLEN-1-i)*x_scale - counter; uint8_t t = sin_gap(b); uint32_t cb = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t); - ca = color_blend(ca, cb, 127); + ca = color_blend(ca, cb, uint8_t(127)); } SEGMENT.setPixelColor(i, ca); } @@ -575,7 +617,7 @@ uint16_t mode_twinkle(void) { if (SEGENV.aux0 >= maxOn) { SEGENV.aux0 = 0; - SEGENV.aux1 = random16(); //new seed for our PRNG + SEGENV.aux1 = hw_random(); //new seed for our PRNG } SEGENV.aux0++; SEGENV.step = it; @@ -600,45 +642,39 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; * Dissolve function */ uint16_t dissolve(uint32_t color) { - unsigned dataSize = (SEGLEN+7) >> 3; //1 bit per LED + unsigned dataSize = sizeof(uint32_t) * SEGLEN; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t* pixels = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - memset(SEGMENT.data, 0xFF, dataSize); // start by fading pixels up + for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = SEGCOLOR(1); SEGENV.aux0 = 1; } - for (int j = 0; j <= SEGLEN / 15; j++) { - if (random8() <= SEGMENT.intensity) { + for (unsigned j = 0; j <= SEGLEN / 15; j++) { + if (hw_random8() <= SEGMENT.intensity) { for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times - unsigned i = random16(SEGLEN); - unsigned index = i >> 3; - unsigned bitNum = i & 0x07; - bool fadeUp = bitRead(SEGENV.data[index], bitNum); + unsigned i = hw_random16(SEGLEN); if (SEGENV.aux0) { //dissolve to primary/palette - if (fadeUp) { - if (color == SEGCOLOR(0)) { - SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); - } else { - SEGMENT.setPixelColor(i, color); - } - bitWrite(SEGENV.data[index], bitNum, false); + if (pixels[i] == SEGCOLOR(1)) { + pixels[i] = color == SEGCOLOR(0) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : color; break; //only spawn 1 new pixel per frame per 50 LEDs } } else { //dissolve to secondary - if (!fadeUp) { - SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; - bitWrite(SEGENV.data[index], bitNum, true); + if (pixels[i] != SEGCOLOR(1)) { + pixels[i] = SEGCOLOR(1); + break; } } } } } + // fix for #4401 + for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, pixels[i]); if (SEGENV.step > (255 - SEGMENT.speed) + 15U) { SEGENV.aux0 = !SEGENV.aux0; SEGENV.step = 0; - memset(SEGMENT.data, (SEGENV.aux0 ? 0xFF : 0), dataSize); // switch fading } else { SEGENV.step++; } @@ -651,7 +687,7 @@ uint16_t dissolve(uint32_t color) { * Blink several LEDs on and then off */ uint16_t mode_dissolve(void) { - return dissolve(SEGMENT.check1 ? SEGMENT.color_wheel(random8()) : SEGCOLOR(0)); + return dissolve(SEGMENT.check1 ? SEGMENT.color_wheel(hw_random8()) : SEGCOLOR(0)); } static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed,,,,Random;!,!;!"; @@ -660,7 +696,7 @@ static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Diss * Blink several LEDs on and then off in random colors */ uint16_t mode_dissolve_random(void) { - return dissolve(SEGMENT.color_wheel(random8())); + return dissolve(SEGMENT.color_wheel(hw_random8())); } static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat speed,Dissolve speed;,!;!"; @@ -670,14 +706,14 @@ static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_sparkle(void) { - if (!SEGMENT.check2) for(int i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for(unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2; uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { - SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index + SEGENV.aux0 = hw_random16(SEGLEN); // aux0 stores the random led index SEGENV.step = it; } @@ -692,13 +728,13 @@ static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!; * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_flash_sparkle(void) { - if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } if (strip.now - SEGENV.aux0 > SEGENV.step) { - if(random8((255-SEGMENT.intensity) >> 4) == 0) { - SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); //flash + if(hw_random8((255-SEGMENT.intensity) >> 4) == 0) { + SEGMENT.setPixelColor(hw_random16(SEGLEN), SEGCOLOR(1)); //flash } SEGENV.step = strip.now; SEGENV.aux0 = 255-SEGMENT.speed; @@ -713,14 +749,15 @@ static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!,,,,, * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_hyper_sparkle(void) { - if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } if (strip.now - SEGENV.aux0 > SEGENV.step) { - if (random8((255-SEGMENT.intensity) >> 4) == 0) { - for (int i = 0; i < max(1, SEGLEN/3); i++) { - SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); + if (hw_random8((255-SEGMENT.intensity) >> 4) == 0) { + int len = max(1, (int)SEGLEN/3); + for (int i = 0; i < len; i++) { + SEGMENT.setPixelColor(hw_random16(SEGLEN), SEGCOLOR(1)); } } SEGENV.step = strip.now; @@ -735,7 +772,7 @@ static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!,,,,,Over * Strobe effect with different strobe count and pause, controlled by speed. */ uint16_t mode_multi_strobe(void) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } @@ -766,7 +803,7 @@ static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!; */ uint16_t mode_android(void) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } @@ -980,11 +1017,11 @@ static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2, * Emulates a traffic light. */ uint16_t mode_traffic_light(void) { - if (SEGLEN == 1) return mode_static(); - for (int i=0; i < SEGLEN; i++) + if (SEGLEN <= 1) return mode_static(); + for (unsigned i=0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; - for (int i = 0; i < SEGLEN-2 ; i+=3) + for (unsigned i = 0; i < SEGLEN-2 ; i+=3) { switch (SEGENV.aux0) { @@ -1013,10 +1050,10 @@ static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US st */ #define FLASH_COUNT 4 uint16_t mode_chase_flash(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } @@ -1043,7 +1080,7 @@ static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!"; * Prim flashes running, followed by random color. */ uint16_t mode_chase_flash_random(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); for (int i = 0; i < SEGENV.aux1; i++) { @@ -1090,7 +1127,7 @@ static const char _data_FX_MODE_RUNNING_COLOR[] PROGMEM = "Chase 2@!,Width;!,!;! uint16_t mode_running_random(void) { uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); uint32_t it = strip.now / cycleTime; - if (SEGENV.call == 0) SEGENV.aux0 = random16(); // random seed for PRNG on start + if (SEGENV.call == 0) SEGENV.aux0 = hw_random(); // random seed for PRNG on start unsigned zoneSize = ((255-SEGMENT.intensity) >> 4) +1; uint16_t PRNG16 = SEGENV.aux0; @@ -1125,7 +1162,7 @@ static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;; * K.I.T.T. */ uint16_t mode_larson_scanner(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); const unsigned speed = FRAMETIME * map(SEGMENT.speed, 0, 255, 96, 2); // map into useful range const unsigned pixels = SEGLEN / speed; // how many pixels to advance per frame @@ -1183,7 +1220,7 @@ static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!, * Firing comets from one end. "Lighthouse" */ uint16_t mode_comet(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned counter = (strip.now * ((SEGMENT.speed >>2) +1)) & 0xFFFF; unsigned index = (counter * SEGLEN) >> 16; if (SEGENV.call == 0) SEGENV.aux0 = index; @@ -1211,9 +1248,9 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!" * Fireworks function. */ uint16_t mode_fireworks() { - if (SEGLEN == 1) return mode_static(); - const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); - const uint16_t height = SEGMENT.virtualHeight(); + if (SEGLEN <= 1) return mode_static(); + const uint16_t width = SEGMENT.is2D() ? SEG_W : SEGLEN; + const uint16_t height = SEG_H; if (SEGENV.call == 0) { SEGENV.aux0 = UINT16_MAX; @@ -1235,11 +1272,11 @@ uint16_t mode_fireworks() { } for (int i=0; i> 1)) == 0) { - uint16_t index = random16(width*height); + if (hw_random8(129 - (SEGMENT.intensity >> 1)) == 0) { + uint16_t index = hw_random16(width*height); x = index % width; y = index / width; - uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); + uint32_t col = SEGMENT.color_from_palette(hw_random8(), false, false, 0); if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, col); else SEGMENT.setPixelColor(index, col); SEGENV.aux1 = SEGENV.aux0; // old spark @@ -1253,9 +1290,9 @@ static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!; //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h uint16_t mode_rain() { - if (SEGLEN == 1) return mode_static(); - const unsigned width = SEGMENT.virtualWidth(); - const unsigned height = SEGMENT.virtualHeight(); + if (SEGLEN <= 1) return mode_static(); + const unsigned width = SEG_W; + const unsigned height = SEG_H; SEGENV.step += FRAMETIME; if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) { SEGENV.step = 1; @@ -1269,7 +1306,7 @@ uint16_t mode_rain() { } else { //shift all leds left uint32_t ctemp = SEGMENT.getPixelColor(0); - for (int i = 0; i < SEGLEN - 1; i++) { + for (unsigned i = 0; i < SEGLEN - 1; i++) { SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); } SEGMENT.setPixelColor(SEGLEN -1, ctemp); // wrap around @@ -1300,8 +1337,8 @@ uint16_t mode_fire_flicker(void) { byte b = (SEGCOLOR(0) ); byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); - for (int i = 0; i < SEGLEN; i++) { - byte flicker = random8(lum); + for (unsigned i = 0; i < SEGLEN; i++) { + byte flicker = hw_random8(lum); if (SEGMENT.palette == 0) { SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); } else { @@ -1319,7 +1356,7 @@ static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;0 * Gradient run base function */ uint16_t gradient_base(bool loading) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); uint16_t pp = (counter * SEGLEN) >> 16; if (SEGENV.call == 0) pp = 0; @@ -1329,14 +1366,14 @@ uint16_t gradient_base(bool loading) { int p1 = pp-SEGLEN; int p2 = pp+SEGLEN; - for (int i = 0; i < SEGLEN; i++) { + for (int i = 0; i < (int)SEGLEN; i++) { if (loading) { val = abs(((i>pp) ? p2:pp) - i); } else { val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); } val = (brd > val) ? (val * 255) / brd : 255; - SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), uint8_t(val))); } return FRAMETIME; @@ -1360,16 +1397,19 @@ uint16_t mode_loading(void) { } static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16"; - -//American Police Light with all LEDs Red and Blue -uint16_t police_base(uint32_t color1, uint32_t color2) { - if (SEGLEN == 1) return mode_static(); +/* + * Two dots running + */ +uint16_t mode_two_dots() { + if (SEGLEN <= 1) return mode_static(); unsigned delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay); unsigned offset = it % SEGLEN; - unsigned width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip if (!width) width = 1; + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2)); + const uint32_t color1 = SEGCOLOR(0); + const uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? color1 : SEGCOLOR(1); for (unsigned i = 0; i < width; i++) { unsigned indexR = (offset + i) % SEGLEN; unsigned indexB = (offset + i + (SEGLEN>>1)) % SEGLEN; @@ -1378,23 +1418,6 @@ uint16_t police_base(uint32_t color1, uint32_t color2) { } return FRAMETIME; } - - -//Police Lights Red and Blue -//uint16_t mode_police() -//{ -// SEGMENT.fill(SEGCOLOR(1)); -// return police_base(RED, BLUE); -//} -//static const char _data_FX_MODE_POLICE[] PROGMEM = "Police@!,Width;,Bg;0"; - - -//Police Lights with custom colors -uint16_t mode_two_dots() { - if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2)); - uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); - return police_base(SEGCOLOR(0), color2); -} static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size,,,,,Overlay;1,2,Bg;!"; @@ -1414,7 +1437,7 @@ typedef struct Flasher { uint16_t mode_fairy() { //set every pixel to a 'random' color from palette (using seed so it doesn't change between frames) uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0)); } @@ -1441,16 +1464,16 @@ uint16_t mode_fairy() { if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1)); for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) { - unsigned stateTime = now16 - flashers[f].stateStart; + unsigned stateTime = uint16_t(now16 - flashers[f].stateStart); //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 10) { flashers[f].stateOn = !flashers[f].stateOn; if (flashers[f].stateOn) { - flashers[f].stateDur = 12 + random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms + flashers[f].stateDur = 12 + hw_random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms } else { - flashers[f].stateDur = 20 + random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms + flashers[f].stateDur = 20 + hw_random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms } - //flashers[f].stateDur = 51 + random8(2 + ((255 - SEGMENT.speed) >> 1)); + //flashers[f].stateDur = 51 + hw_random8(2 + ((255 - SEGMENT.speed) >> 1)); flashers[f].stateStart = now16; if (stateTime < 255) { flashers[f].stateStart -= 255 -stateTime; //start early to get correct bri @@ -1470,7 +1493,7 @@ uint16_t mode_fairy() { unsigned globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) { - unsigned bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255; + uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255; PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number unsigned flasherPos = f*flasherDistance; SEGMENT.setPixelColor(flasherPos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), bri)); @@ -1499,29 +1522,29 @@ uint16_t mode_fairytwinkle() { unsigned riseFallTime = 400 + (255-SEGMENT.speed)*3; unsigned maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1); - for (int f = 0; f < SEGLEN; f++) { - unsigned stateTime = now16 - flashers[f].stateStart; + for (unsigned f = 0; f < SEGLEN; f++) { + uint16_t stateTime = now16 - flashers[f].stateStart; //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 100) { flashers[f].stateOn = !flashers[f].stateOn; bool init = !flashers[f].stateDur; if (flashers[f].stateOn) { - flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1; + flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + hw_random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1; } else { - flashers[f].stateDur = riseFallTime/100 + random8(3 + ((255 - SEGMENT.speed) >> 6)) +1; + flashers[f].stateDur = riseFallTime/100 + hw_random8(3 + ((255 - SEGMENT.speed) >> 6)) +1; } flashers[f].stateStart = now16; stateTime = 0; if (init) { flashers[f].stateStart -= riseFallTime; //start lit - flashers[f].stateDur = riseFallTime/100 + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker + flashers[f].stateDur = riseFallTime/100 + hw_random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker stateTime = riseFallTime; } } if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state unsigned fadeprog = 255 - ((stateTime * 255) / riseFallTime); - unsigned flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog); + uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog); unsigned lastR = PRNG16; unsigned diff = 0; while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough @@ -1544,7 +1567,7 @@ uint16_t tricolor_chase(uint32_t color1, uint32_t color2) { unsigned width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour unsigned index = it % (width*3); - for (int i = 0; i < SEGLEN; i++, index++) { + for (unsigned i = 0; i < SEGLEN; i++, index++) { if (index > (width*3)-1) index = 0; uint32_t color = color1; @@ -1582,13 +1605,13 @@ uint16_t mode_icu(void) { SEGMENT.setPixelColor(dest + SEGLEN/space, col); if(SEGENV.aux0 == dest) { // pause between eye movements - if(random8(6) == 0) { // blink once in a while + if(hw_random8(6) == 0) { // blink once in a while SEGMENT.setPixelColor(dest, SEGCOLOR(1)); SEGMENT.setPixelColor(dest + SEGLEN/space, SEGCOLOR(1)); return 200; } - SEGENV.aux0 = random16(SEGLEN-SEGLEN/space); - return 1000 + random16(2000); + SEGENV.aux0 = hw_random16(SEGLEN-SEGLEN/space); + return 1000 + hw_random16(2000); } if(SEGENV.aux0 > SEGENV.step) { @@ -1617,7 +1640,7 @@ uint16_t mode_tricolor_wipe(void) { unsigned ledIndex = (prog * SEGLEN * 3) >> 16; unsigned ledOffset = ledIndex; - for (int i = 0; i < SEGLEN; i++) + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); } @@ -1718,7 +1741,7 @@ uint16_t mode_multi_comet(void) { } comets[i]++; } else { - if(!random16(SEGLEN)) { + if(!hw_random16(SEGLEN)) { comets[i] = 0; } } @@ -1798,24 +1821,24 @@ uint16_t mode_oscillate(void) { // if the counter has increased, move the oscillator by the random step if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); - if((oscillators[i].dir == -1) && (oscillators[i].pos <= 0)) { + if((oscillators[i].dir == -1) && (oscillators[i].pos > SEGLEN << 1)) { // use integer overflow oscillators[i].pos = 0; oscillators[i].dir = 1; // make bigger steps for faster speeds - oscillators[i].speed = SEGMENT.speed > 100 ? random8(2, 4):random8(1, 3); + oscillators[i].speed = SEGMENT.speed > 100 ? hw_random8(2, 4):hw_random8(1, 3); } if((oscillators[i].dir == 1) && (oscillators[i].pos >= (SEGLEN - 1))) { oscillators[i].pos = SEGLEN - 1; oscillators[i].dir = -1; - oscillators[i].speed = SEGMENT.speed > 100 ? random8(2, 4):random8(1, 3); + oscillators[i].speed = SEGMENT.speed > 100 ? hw_random8(2, 4):hw_random8(1, 3); } } for (unsigned i = 0; i < SEGLEN; i++) { uint32_t color = BLACK; for (unsigned j = 0; j < numOscillators; j++) { - if(i >= (unsigned)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { - color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); + if((int)i >= (int)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { + color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), uint8_t(128)); } } SEGMENT.setPixelColor(i, color); @@ -1829,14 +1852,14 @@ static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate"; //TODO uint16_t mode_lightning(void) { - if (SEGLEN == 1) return mode_static(); - unsigned ledstart = random16(SEGLEN); // Determine starting location of flash - unsigned ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) - uint8_t bri = 255/random8(1, 3); + if (SEGLEN <= 1) return mode_static(); + unsigned ledstart = hw_random16(SEGLEN); // Determine starting location of flash + unsigned ledlen = 1 + hw_random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) + uint8_t bri = 255/hw_random8(1, 3); if (SEGENV.aux1 == 0) //init, leader flash { - SEGENV.aux1 = random8(4, 4 + SEGMENT.intensity/20); //number of flashes + SEGENV.aux1 = hw_random8(4, 4 + SEGMENT.intensity/20); //number of flashes SEGENV.aux1 *= 2; bri = 52; //leader has lower brightness @@ -1853,15 +1876,15 @@ uint16_t mode_lightning(void) { SEGENV.aux1--; SEGENV.step = strip.now; - //return random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds + //return hw_random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds } else { if (strip.now - SEGENV.step > SEGENV.aux0) { SEGENV.aux1--; if (SEGENV.aux1 < 2) SEGENV.aux1 = 0; - SEGENV.aux0 = (50 + random8(100)); //delay between flashes + SEGENV.aux0 = (50 + hw_random8(100)); //delay between flashes if (SEGENV.aux1 == 2) { - SEGENV.aux0 = (random8(255 - SEGMENT.speed) * 100); // delay between strikes + SEGENV.aux0 = (hw_random8(255 - SEGMENT.speed) * 100); // delay between strikes } SEGENV.step = strip.now; } @@ -1870,60 +1893,84 @@ uint16_t mode_lightning(void) { } static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!,,,,,Overlay;!,!;!"; - -// Pride2015 -// Animated, ever-changing rainbows. -// by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 -uint16_t mode_pride_2015(void) { +// combined function from original pride and colorwaves +uint16_t mode_colorwaves_pride_base(bool isPride2015) { unsigned duration = 10 + SEGMENT.speed; unsigned sPseudotime = SEGENV.step; unsigned sHue16 = SEGENV.aux0; - uint8_t sat8 = beatsin88( 87, 220, 250); - uint8_t brightdepth = beatsin88( 341, 96, 224); - unsigned brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); - unsigned msmultiplier = beatsin88(147, 23, 60); + uint8_t sat8 = isPride2015 ? beatsin88_t(87, 220, 250) : 255; + unsigned brightdepth = beatsin88_t(341, 96, 224); + unsigned brightnessthetainc16 = beatsin88_t(203, (25 * 256), (40 * 256)); + unsigned msmultiplier = beatsin88_t(147, 23, 60); - unsigned hue16 = sHue16;//gHue * 256; - unsigned hueinc16 = beatsin88(113, 1, 3000); + unsigned hue16 = sHue16; + unsigned hueinc16 = isPride2015 ? beatsin88_t(113, 1, 3000) : + beatsin88_t(113, 60, 300) * SEGMENT.intensity * 10 / 255; sPseudotime += duration * msmultiplier; - sHue16 += duration * beatsin88( 400, 5,9); + sHue16 += duration * beatsin88_t(400, 5, 9); unsigned brightnesstheta16 = sPseudotime; - for (unsigned i = 0 ; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { hue16 += hueinc16; - uint8_t hue8 = hue16 >> 8; + uint8_t hue8; - brightnesstheta16 += brightnessthetainc16; - unsigned b16 = sin16( brightnesstheta16 ) + 32768; + if (isPride2015) { + hue8 = hue16 >> 8; + } else { + unsigned h16_128 = hue16 >> 7; + hue8 = (h16_128 & 0x100) ? (255 - (h16_128 >> 1)) : (h16_128 >> 1); + } + brightnesstheta16 += brightnessthetainc16; + unsigned b16 = sin16_t(brightnesstheta16) + 32768; unsigned bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; bri8 += (255 - brightdepth); - CRGB newcolor = CHSV(hue8, sat8, bri8); - SEGMENT.blendPixelColor(i, newcolor, 64); + if (isPride2015) { + CRGB newcolor = CHSV(hue8, sat8, bri8); + SEGMENT.blendPixelColor(i, newcolor, 64); + } else { + SEGMENT.blendPixelColor(i, SEGMENT.color_from_palette(hue8, false, PALETTE_SOLID_WRAP, 0, bri8), 128); + } } + SEGENV.step = sPseudotime; SEGENV.aux0 = sHue16; return FRAMETIME; } + +// Pride2015 +// Animated, ever-changing rainbows. +// by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 +uint16_t mode_pride_2015(void) { + return mode_colorwaves_pride_base(true); +} static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;"; +// ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb +// This function draws color waves with an ever-changing, +// widely-varying set of parameters, using a color palette. +uint16_t mode_colorwaves() { + return mode_colorwaves_pride_base(false); +} +static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!;;pal=26"; + //eight colored dots, weaving in and out of sync with each other uint16_t mode_juggle(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4)); CRGB fastled_col; byte dothue = 0; for (int i = 0; i < 8; i++) { - int index = 0 + beatsin88((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); + int index = 0 + beatsin88_t((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); fastled_col = CRGB(SEGMENT.getPixelColor(index)); - fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255); + fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):CRGB(ColorFromPalette(SEGPALETTE, dothue, 255)); SEGMENT.setPixelColor(index, fastled_col); dothue += 32; } @@ -1942,8 +1989,8 @@ uint16_t mode_palette() { constexpr mathType maxAngle = 0x8000; constexpr mathType staticRotationScale = 256; constexpr mathType animatedRotationScale = 1; - constexpr int16_t (*sinFunction)(uint16_t) = &sin16; - constexpr int16_t (*cosFunction)(uint16_t) = &cos16; + constexpr int16_t (*sinFunction)(uint16_t) = &sin16_t; + constexpr int16_t (*cosFunction)(uint16_t) = &cos16_t; #else using mathType = float; using wideMathType = float; @@ -1956,8 +2003,8 @@ uint16_t mode_palette() { constexpr float (*cosFunction)(float) = &cos_t; #endif const bool isMatrix = strip.isMatrix; - const int cols = SEGMENT.virtualWidth(); - const int rows = isMatrix ? SEGMENT.virtualHeight() : strip.getActiveSegmentsNum(); + const int cols = SEG_W; + const int rows = isMatrix ? SEG_H : strip.getActiveSegmentsNum(); const int inputShift = SEGMENT.speed; const int inputSize = SEGMENT.intensity; @@ -1966,7 +2013,7 @@ uint16_t mode_palette() { const bool inputAnimateRotation = SEGMENT.check2; const bool inputAssumeSquare = SEGMENT.check3; - const angleType theta = (!inputAnimateRotation) ? (inputRotation * maxAngle / staticRotationScale) : (((strip.now * ((inputRotation >> 4) +1)) & 0xFFFF) * animatedRotationScale); + const angleType theta = (!inputAnimateRotation) ? ((inputRotation + 128) * maxAngle / staticRotationScale) : (((strip.now * ((inputRotation >> 4) +1)) & 0xFFFF) * animatedRotationScale); const mathType sinTheta = sinFunction(theta); const mathType cosTheta = cosFunction(theta); @@ -1985,7 +2032,7 @@ uint16_t mode_palette() { // So the rectangle needs to have exactly the right size. That size depends on the rotation. // This scale computation here only considers one dimension. You can think of it like the rectangle is always scaled so that // the left and right most points always match the left and right side of the display. - const mathType scale = std::abs(sinTheta) + (std::abs(cosTheta) * maxYOut / maxXOut); + const mathType scale = std::abs(sinTheta) + (std::abs(cosTheta) * maxYOut / maxXOut); // 2D simulation: // If we are dealing with a 1D setup, we assume that each segment represents one line on a 2-dimensional display. // The function is called once per segments, so we need to handle one line at a time. @@ -2003,7 +2050,7 @@ uint16_t mode_palette() { const mathType sourceX = xtSinTheta + ytCosTheta + centerX; // The computation was scaled just right so that the result should always be in range [0, maxXOut], but enforce this anyway // to account for imprecision. Then scale it so that the range is [0, 255], which we can use with the palette. - int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * 255) / (sInt16Scale * maxXOut); + int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * wideMathType(255)) / (sInt16Scale * maxXOut); // inputSize determines by how much we want to scale the palette: // values < 128 display a fraction of a palette, // values > 128 display multiple palettes. @@ -2016,8 +2063,8 @@ uint16_t mode_palette() { colorIndex = ((inputSize - 112) * colorIndex) / 16; } // Finally, shift the palette a bit. - const int paletteOffset = (!inputAnimateShift) ? (inputShift-128) : (((strip.now * ((inputShift >> 3) +1)) & 0xFFFF) >> 8); - colorIndex += paletteOffset; + const int paletteOffset = (!inputAnimateShift) ? (inputShift) : (((strip.now * ((inputShift >> 3) +1)) & 0xFFFF) >> 8); + colorIndex -= paletteOffset; const uint32_t color = SEGMENT.color_wheel((uint8_t)colorIndex); if (isMatrix) { SEGMENT.setPixelColorXY(x, y, color); @@ -2028,7 +2075,7 @@ uint16_t mode_palette() { } return FRAMETIME; } -static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation,,,Animate Shift,Animate Rotation,Anamorphic;;!;12;c1=128,c2=128,c3=128,o1=1,o2=1,o3=0"; +static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation,,,Animate Shift,Animate Rotation,Anamorphic;;!;12;ix=112,c1=0,o1=1,o2=0,o3=1"; // WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active @@ -2060,7 +2107,7 @@ static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used // in step 3 above) (Effect Intensity = Sparking). uint16_t mode_fire_2012() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); const unsigned strips = SEGMENT.nrOfVStrips(); if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed byte* heat = SEGENV.data; @@ -2070,11 +2117,11 @@ uint16_t mode_fire_2012() { struct virtualStrip { static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) { - const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels + const uint8_t ignition = MAX(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels // Step 1. Cool down every cell a little - for (int i = 0; i < SEGLEN; i++) { - uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random8(4); + for (unsigned i = 0; i < SEGLEN; i++) { + uint8_t cool = (it != SEGENV.step) ? hw_random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : hw_random8(4); uint8_t minTemp = (i> 8; - unsigned h16_128 = hue16 >> 7; - if ( h16_128 & 0x100) { - hue8 = 255 - (h16_128 >> 1); - } else { - hue8 = h16_128 >> 1; - } - - brightnesstheta16 += brightnessthetainc16; - unsigned b16 = sin16(brightnesstheta16) + 32768; - - unsigned bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; - uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; - bri8 += (255 - brightdepth); - - SEGMENT.blendPixelColor(i, SEGMENT.color_from_palette(hue8, false, PALETTE_SOLID_WRAP, 0, bri8), 128); // 50/50 mix - } - SEGENV.step = sPseudotime; - SEGENV.aux0 = sHue16; - - return FRAMETIME; -} -static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!"; - +static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,2D Blur,Boost;;!;1;pal=35,sx=64,ix=160,m12=1,c2=128"; // bars // colored stripes pulsing at a defined Beats-Per-Minute (BPM) uint16_t mode_bpm() { uint32_t stp = (strip.now / 20) & 0xFF; - uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for (int i = 0; i < SEGLEN; i++) { + uint8_t beat = beatsin8_t(SEGMENT.speed, 64, 255); + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10))); } @@ -2179,12 +2179,12 @@ static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64"; uint16_t mode_fillnoise8() { - if (SEGENV.call == 0) SEGENV.step = random16(12345); - for (int i = 0; i < SEGLEN; i++) { + if (SEGENV.call == 0) SEGENV.step = hw_random(); + for (unsigned i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } - SEGENV.step += beatsin8(SEGMENT.speed, 1, 6); //10,1,4 + SEGENV.step += beatsin8_t(SEGMENT.speed, 1, 6); //10,1,4 return FRAMETIME; } @@ -2195,72 +2195,72 @@ uint16_t mode_noise16_1() { unsigned scale = 320; // the "zoom factor" for the noise SEGENV.step += (1 + SEGMENT.speed/16); - for (int i = 0; i < SEGLEN; i++) { - unsigned shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm + for (unsigned i = 0; i < SEGLEN; i++) { + unsigned shift_x = beatsin8_t(11); // the x position of the noise field swings @ 17 bpm unsigned shift_y = SEGENV.step/42; // the y position becomes slowly incremented unsigned real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm unsigned real_y = (i + shift_y) * scale; // the y position becomes slowly incremented uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented unsigned noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down - unsigned index = sin8(noise * 3); // map LED color based on noise data + unsigned index = sin8_t(noise * 3); // map LED color based on noise data SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!;!;!"; +static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!;!;!;;pal=20"; uint16_t mode_noise16_2() { unsigned scale = 1000; // the "zoom factor" for the noise SEGENV.step += (1 + (SEGMENT.speed >> 1)); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned shift_x = SEGENV.step >> 6; // x as a function of time uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field unsigned noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down - unsigned index = sin8(noise * 3); // map led color based on noise data + unsigned index = sin8_t(noise * 3); // map led color based on noise data SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); } return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!"; +static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!;;pal=43"; uint16_t mode_noise16_3() { unsigned scale = 800; // the "zoom factor" for the noise SEGENV.step += (1 + SEGMENT.speed); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned shift_x = 4223; // no movement along x and y unsigned shift_y = 1234; uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions uint32_t real_z = SEGENV.step*8; unsigned noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down - unsigned index = sin8(noise * 3); // map led color based on noise data + unsigned index = sin8_t(noise * 3); // map led color based on noise data SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); } return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!"; +static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!;;pal=35"; //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino uint16_t mode_noise16_4() { uint32_t stp = (strip.now * SEGMENT.speed) >> 7; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { int index = inoise16(uint32_t(i) << 12, stp); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!"; +static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!;;pal=26"; //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e @@ -2268,46 +2268,45 @@ uint16_t mode_colortwinkle() { unsigned dataSize = (SEGLEN+7) >> 3; //1 bit per LED if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - CRGB fastled_col, prev; + CRGBW col, prev; fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness(); fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness(); - for (int i = 0; i < SEGLEN; i++) { - fastled_col = SEGMENT.getPixelColor(i); - prev = fastled_col; + for (unsigned i = 0; i < SEGLEN; i++) { + CRGBW cur = SEGMENT.getPixelColor(i); + prev = cur; unsigned index = i >> 3; unsigned bitNum = i & 0x07; bool fadeUp = bitRead(SEGENV.data[index], bitNum); if (fadeUp) { - CRGB incrementalColor = fastled_col; - incrementalColor.nscale8_video(fadeUpAmount); - fastled_col += incrementalColor; + CRGBW incrementalColor = color_fade(cur, fadeUpAmount, true); + col = color_add(cur, incrementalColor); - if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { + if (col.r == 255 || col.g == 255 || col.b == 255) { bitWrite(SEGENV.data[index], bitNum, false); } - SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - if (SEGMENT.getPixelColor(i) == RGBW32(prev.r, prev.g, prev.b, 0)) { //fix "stuck" pixels - fastled_col += fastled_col; - SEGMENT.setPixelColor(i, fastled_col); + if (cur == prev) { //fix "stuck" pixels + color_add(col, col); + SEGMENT.setPixelColor(i, col); } - } else { - fastled_col.nscale8(255 - fadeDownAmount); - SEGMENT.setPixelColor(i, fastled_col); + else SEGMENT.setPixelColor(i, col); + } + else { + col = color_fade(cur, 255 - fadeDownAmount); + SEGMENT.setPixelColor(i, col); } } for (unsigned j = 0; j <= SEGLEN / 50; j++) { - if (random8() <= SEGMENT.intensity) { + if (hw_random8() <= SEGMENT.intensity) { for (unsigned times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times - int i = random16(SEGLEN); + int i = hw_random16(SEGLEN); if (SEGMENT.getPixelColor(i) == 0) { - fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND); unsigned index = i >> 3; unsigned bitNum = i & 0x07; bitWrite(SEGENV.data[index], bitNum, true); - SEGMENT.setPixelColor(i, fastled_col); + SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, hw_random8(), 64, NOBLEND)); break; //only spawn 1 new pixel per frame per 50 LEDs } } @@ -2321,13 +2320,13 @@ static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade spe //Calm effect, like a lake at night uint16_t mode_lake() { unsigned sp = SEGMENT.speed/10; - int wave1 = beatsin8(sp +2, -64,64); - int wave2 = beatsin8(sp +1, -64,64); - int wave3 = beatsin8(sp +2, 0,80); + int wave1 = beatsin8_t(sp +2, -64,64); + int wave2 = beatsin8_t(sp +1, -64,64); + int wave3 = beatsin8_t(sp +2, 0,80); - for (int i = 0; i < SEGLEN; i++) + for (unsigned i = 0; i < SEGLEN; i++) { - int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2; + int index = cos8_t((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2; uint8_t lum = (index > wave3) ? index - wave3 : 0; SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, 0, lum)); } @@ -2337,99 +2336,78 @@ uint16_t mode_lake() { static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!"; -// meteor effect +// meteor effect & meteor smooth (merged by @dedehai) // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain uint16_t mode_meteor() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed - + const bool meteorSmooth = SEGMENT.check3; byte* trail = SEGENV.data; const unsigned meteorSize = 1 + SEGLEN / 20; // 5% - unsigned counter = strip.now * ((SEGMENT.speed >> 2) +8); - uint16_t in = counter * SEGLEN >> 16; - - const int max = SEGMENT.palette==5 ? 239 : 255; // "* Colors only" palette blends end with start - // fade all leds to colors[1] in LEDs one step - for (int i = 0; i < SEGLEN; i++) { - if (random8() <= 255 - SEGMENT.intensity) { - int meteorTrailDecay = 128 + random8(127); - trail[i] = scale8(trail[i], meteorTrailDecay); - int index = trail[i]; - int idx = 255; - int bri = SEGMENT.palette==35 || SEGMENT.palette==36 ? 255 : trail[i]; - if (!SEGMENT.check1) { - idx = 0; - index = map(i,0,SEGLEN,0,max); - bri = trail[i]; - } - uint32_t col = SEGMENT.color_from_palette(index, false, false, idx, bri); // full brightness for Fire - SEGMENT.setPixelColor(i, col); - } + uint16_t meteorstart; + if(meteorSmooth) meteorstart = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1); + else { + unsigned counter = strip.now * ((SEGMENT.speed >> 2) + 8); + meteorstart = (counter * SEGLEN) >> 16; } - // draw meteor - for (unsigned j = 0; j < meteorSize; j++) { - int index = (in + j) % SEGLEN; - int idx = 255; - int i = trail[index] = max; - if (!SEGMENT.check1) { - i = map(index,0,SEGLEN,0,max); - idx = 0; - } - uint32_t col = SEGMENT.color_from_palette(i, false, false, idx, 255); // full brightness - SEGMENT.setPixelColor(index, col); - } - - return FRAMETIME; -} -static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient;!;!;1"; - - -// smooth meteor effect -// send a meteor from begining to to the end of the strip with a trail that randomly decays. -// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain -uint16_t mode_meteor_smooth() { - if (SEGLEN == 1) return mode_static(); - if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed - - byte* trail = SEGENV.data; - - const unsigned meteorSize = 1+ SEGLEN / 20; // 5% - uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1); - const int max = SEGMENT.palette==5 || !SEGMENT.check1 ? 240 : 255; // fade all leds to colors[1] in LEDs one step for (unsigned i = 0; i < SEGLEN; i++) { - if (/*trail[i] != 0 &&*/ random8() <= 255 - SEGMENT.intensity) { - int change = trail[i] + 4 - random8(24); //change each time between -20 and +4 - trail[i] = constrain(change, 0, max); - uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255); + uint32_t col; + if (hw_random8() <= 255 - SEGMENT.intensity) { + if(meteorSmooth) { + int change = trail[i] + 4 - hw_random8(24); //change each time between -20 and +4 + trail[i] = constrain(change, 0, max); + col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255); + } + else { + trail[i] = scale8(trail[i], 128 + hw_random8(127)); + int index = trail[i]; + int idx = 255; + int bri = SEGMENT.palette==35 || SEGMENT.palette==36 ? 255 : trail[i]; + if (!SEGMENT.check1) { + idx = 0; + index = map(i,0,SEGLEN,0,max); + bri = trail[i]; + } + col = SEGMENT.color_from_palette(index, false, false, idx, bri); // full brightness for Fire + } SEGMENT.setPixelColor(i, col); } } // draw meteor for (unsigned j = 0; j < meteorSize; j++) { - unsigned index = in + j; - if (index >= SEGLEN) { - index -= SEGLEN; + unsigned index = (meteorstart + j) % SEGLEN; + if(meteorSmooth) { + trail[index] = max; + uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255); + SEGMENT.setPixelColor(index, col); + } + else{ + int idx = 255; + int i = trail[index] = max; + if (!SEGMENT.check1) { + i = map(index,0,SEGLEN,0,max); + idx = 0; + } + uint32_t col = SEGMENT.color_from_palette(i, false, false, idx, 255); // full brightness + SEGMENT.setPixelColor(index, col); } - trail[index] = max; - uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255); - SEGMENT.setPixelColor(index, col); } SEGENV.step += SEGMENT.speed +1; return FRAMETIME; } -static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail,,,,Gradient;;!;1"; +static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient,,Smooth;;!;1"; //Railway Crossing / Christmas Fairy lights uint16_t mode_railway() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned dur = (256 - SEGMENT.speed) * 40; uint16_t rampdur = (dur * SEGMENT.intensity) >> 8; if (SEGENV.step > dur) @@ -2445,7 +2423,7 @@ uint16_t mode_railway() { if (p0 < 255) pos = p0; } if (SEGENV.aux0) pos = 255 - pos; - for (int i = 0; i < SEGLEN; i += 2) + for (unsigned i = 0; i < SEGLEN; i += 2) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255)); // do not use color 1 or 2, always use palette if (i < SEGLEN -1) @@ -2456,7 +2434,7 @@ uint16_t mode_railway() { SEGENV.step += FRAMETIME; return FRAMETIME; } -static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;1,2;!"; +static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;1,2;!;;pal=3"; //Water ripple @@ -2475,8 +2453,8 @@ typedef struct Ripple { #else #define MAX_RIPPLES 100 #endif -static uint16_t ripple_base() { - unsigned maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 +static uint16_t ripple_base(uint8_t blurAmount = 0) { + unsigned maxRipples = min(1 + (int)(SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 unsigned dataSize = sizeof(ripple) * maxRipples; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2500,7 +2478,7 @@ static uint16_t ripple_base() { propI /= 2; unsigned cx = rippleorigin >> 8; unsigned cy = rippleorigin & 0xFF; - unsigned mag = scale8(sin8((propF>>2)), amp); + unsigned mag = scale8(sin8_t((propF>>2)), amp); if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true); } else #endif @@ -2508,7 +2486,7 @@ static uint16_t ripple_base() { int left = rippleorigin - propI -1; int right = rippleorigin + propI +2; for (int v = 0; v < 4; v++) { - unsigned mag = scale8(cubicwave8((propF>>2) + v * 64), amp); + uint8_t mag = scale8(cubicwave8((propF>>2) + v * 64), amp); SEGMENT.setPixelColor(left + v, color_blend(SEGMENT.getPixelColor(left + v), col, mag)); // TODO SEGMENT.setPixelColor(right - v, color_blend(SEGMENT.getPixelColor(right - v), col, mag)); // TODO } @@ -2516,42 +2494,45 @@ static uint16_t ripple_base() { ripplestate += rippledecay; ripples[i].state = (ripplestate > 254) ? 0 : ripplestate; } else {//randomly create new wave - if (random16(IBN + 10000) <= (SEGMENT.intensity >> (SEGMENT.is2D()*3))) { + if (hw_random16(IBN + 10000) <= (SEGMENT.intensity >> (SEGMENT.is2D()*3))) { ripples[i].state = 1; - ripples[i].pos = SEGMENT.is2D() ? ((random8(SEGENV.virtualWidth())<<8) | (random8(SEGENV.virtualHeight()))) : random16(SEGLEN); - ripples[i].color = random8(); //color + ripples[i].pos = SEGMENT.is2D() ? ((hw_random8(SEG_W)<<8) | (hw_random8(SEG_H))) : hw_random16(SEGLEN); + ripples[i].color = hw_random8(); //color } } } - + SEGMENT.blur(blurAmount); return FRAMETIME; } #undef MAX_RIPPLES uint16_t mode_ripple(void) { - if (SEGLEN == 1) return mode_static(); - if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); - else SEGMENT.fade_out(250); - return ripple_base(); + if (SEGLEN <= 1) return mode_static(); + if(SEGMENT.custom1 || SEGMENT.check2) // blur or overlay + SEGMENT.fade_out(250); + else + SEGMENT.fill(SEGCOLOR(1)); + + return ripple_base(SEGMENT.custom1>>1); } -static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay;,!;!;12"; +static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,Blur,,,,Overlay;,!;!;12;c1=0"; uint16_t mode_ripple_rainbow(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); if (SEGENV.call ==0) { - SEGENV.aux0 = random8(); - SEGENV.aux1 = random8(); + SEGENV.aux0 = hw_random8(); + SEGENV.aux1 = hw_random8(); } if (SEGENV.aux0 == SEGENV.aux1) { - SEGENV.aux1 = random8(); + SEGENV.aux1 = hw_random8(); } else if (SEGENV.aux1 > SEGENV.aux0) { SEGENV.aux0++; } else { SEGENV.aux0--; } - SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,235)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,uint8_t(235))); return ripple_base(); } static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!;12"; @@ -2565,11 +2546,11 @@ static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) unsigned ticks = ms / SEGENV.aux0; - unsigned fastcycle8 = ticks; - unsigned slowcycle16 = (ticks >> 8) + salt; - slowcycle16 += sin8(slowcycle16); + unsigned fastcycle8 = uint8_t(ticks); + uint16_t slowcycle16 = (ticks >> 8) + salt; + slowcycle16 += sin8_t(slowcycle16); slowcycle16 = (slowcycle16 * 2053) + 1384; - unsigned slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); + uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); // Overall twinkle density. // 0 (NONE lit) to 8 (ALL lit at once). @@ -2646,7 +2627,7 @@ static uint16_t twinklefox_base(bool cat) unsigned backgroundBrightness = bg.getAverageLight(); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number unsigned myclockoffset16= PRNG16; // use that number as clock offset @@ -2666,15 +2647,15 @@ static uint16_t twinklefox_base(bool cat) if (deltabright >= 32 || (!bg)) { // If the new pixel is significantly brighter than the background color, // use the new color. - SEGMENT.setPixelColor(i, c.red, c.green, c.blue); + SEGMENT.setPixelColor(i, c); } else if (deltabright > 0) { // If the new pixel is just slightly brighter than the background color, // mix a blend of the new color and the background color - SEGMENT.setPixelColor(i, color_blend(RGBW32(bg.r,bg.g,bg.b,0), RGBW32(c.r,c.g,c.b,0), deltabright * 8)); + SEGMENT.setPixelColor(i, color_blend(RGBW32(bg.r,bg.g,bg.b,0), RGBW32(c.r,c.g,c.b,0), uint8_t(deltabright * 8))); } else { // if the new pixel is not at all brighter than the background color, // just use the background color. - SEGMENT.setPixelColor(i, bg.r, bg.g, bg.b); + SEGMENT.setPixelColor(i, bg); } } return FRAMETIME; @@ -2717,9 +2698,9 @@ uint16_t mode_halloween_eyes() uint32_t blinkEndTime; }; - if (SEGLEN == 1) return mode_static(); - const unsigned maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN; - const unsigned HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5); + if (SEGLEN <= 1) return mode_static(); + const unsigned maxWidth = strip.isMatrix ? SEG_W : SEGLEN; + const unsigned HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEG_W>>4: SEGLEN>>5); const unsigned HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2; unsigned eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short @@ -2740,10 +2721,10 @@ uint16_t mode_halloween_eyes() // - select a duration // - immediately switch to eyes on state. - data.startPos = random16(0, maxWidth - eyeLength - 1); - data.color = random8(); - if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices - duration = 128u + random16(SEGMENT.intensity*64u); + data.startPos = hw_random16(0, maxWidth - eyeLength - 1); + data.color = hw_random8(); + if (strip.isMatrix) SEGMENT.offset = hw_random16(SEG_H-1); // a hack: reuse offset since it is not used in matrices + duration = 128u + hw_random16(SEGMENT.intensity*64u); data.duration = duration; data.state = eyeState::on; [[fallthrough]]; @@ -2766,15 +2747,15 @@ uint16_t mode_halloween_eyes() const uint32_t eyeColor = SEGMENT.color_from_palette(data.color, false, false, 0); uint32_t c = eyeColor; if (fadeInAnimationState < 256u) { - c = color_blend(backgroundColor, eyeColor, fadeInAnimationState); + c = color_blend(backgroundColor, eyeColor, uint8_t(fadeInAnimationState)); } else if (elapsedTime > minimumOnTimeBegin) { const uint32_t remainingTime = (elapsedTime >= duration) ? 0u : (duration - elapsedTime); if (remainingTime > minimumOnTimeEnd) { - if (random8() < 4u) + if (hw_random8() < 4u) { c = backgroundColor; data.state = eyeState::blink; - data.blinkEndTime = strip.now + random8(8, 128); + data.blinkEndTime = strip.now + hw_random8(8, 128); } } } @@ -2808,7 +2789,7 @@ uint16_t mode_halloween_eyes() // - immediately switch to eyes-off state const unsigned eyeOffTimeBase = SEGMENT.speed*128u; - duration = eyeOffTimeBase + random16(eyeOffTimeBase); + duration = eyeOffTimeBase + hw_random16(eyeOffTimeBase); data.duration = duration; data.state = eyeState::off; [[fallthrough]]; @@ -2860,7 +2841,7 @@ uint16_t mode_static_pattern() bool drawingLit = true; unsigned cnt = 0; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, (drawingLit) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); cnt++; if (cnt >= ((drawingLit) ? lit : unlit)) { @@ -2880,7 +2861,7 @@ uint16_t mode_tri_static_pattern() unsigned currSeg = 0; unsigned currSegCount = 0; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if ( currSeg % 3 == 0 ) { SEGMENT.setPixelColor(i, SEGCOLOR(0)); } else if( currSeg % 3 == 1) { @@ -2902,7 +2883,7 @@ static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tr static uint16_t spots_base(uint16_t threshold) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); unsigned maxZones = SEGLEN >> 2; @@ -2919,7 +2900,7 @@ static uint16_t spots_base(uint16_t threshold) if (wave > threshold) { unsigned index = 0 + pos + i; unsigned s = (wave - threshold)*255 / (0xFFFF - threshold); - SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); + SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255-s))); } } } @@ -2958,7 +2939,7 @@ typedef struct Ball { * Bouncing Balls Effect */ uint16_t mode_bouncing_balls(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); //allocate segment data const unsigned strips = SEGMENT.nrOfVStrips(); // adapt for 2D const size_t maxNumBalls = 16; @@ -2998,7 +2979,7 @@ uint16_t mode_bouncing_balls(void) { balls[i].lastBounceTime = time; if (balls[i].impactVelocity < 0.015f) { - float impactVelocityStart = sqrtf(-2.0f * gravity) * random8(5,11)/10.0f; // randomize impact velocity + float impactVelocityStart = sqrtf(-2.0f * gravity) * hw_random8(5,11)/10.0f; // randomize impact velocity balls[i].impactVelocity = impactVelocityStart; } } else if (balls[i].height > 1.0f) { @@ -3061,10 +3042,10 @@ static uint16_t rolling_balls(void) { SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // start clean for (unsigned i = 0; i < maxNumBalls; i++) { balls[i].lastBounceUpdate = strip.now; - balls[i].velocity = 20.0f * float(random16(1000, 10000))/10000.0f; // number from 1 to 10 - if (random8()<128) balls[i].velocity = -balls[i].velocity; // 50% chance of reverse direction - balls[i].height = (float(random16(0, 10000)) / 10000.0f); // from 0. to 1. - balls[i].mass = (float(random16(1000, 10000)) / 10000.0f); // from .1 to 1. + balls[i].velocity = 20.0f * float(hw_random16(1000, 10000))/10000.0f; // number from 1 to 10 + if (hw_random8()<128) balls[i].velocity = -balls[i].velocity; // 50% chance of reverse direction + balls[i].height = (float(hw_random16(0, 10000)) / 10000.0f); // from 0. to 1. + balls[i].mass = (float(hw_random16(1000, 10000)) / 10000.0f); // from .1 to 1. } } @@ -3080,7 +3061,7 @@ static uint16_t rolling_balls(void) { float thisHeight = balls[i].height + balls[i].velocity * timeSinceLastUpdate; // this method keeps higher resolution // test if intensity level was increased and some balls are way off the track then put them back if (thisHeight < -0.5f || thisHeight > 1.5f) { - thisHeight = balls[i].height = (float(random16(0, 10000)) / 10000.0f); // from 0. to 1. + thisHeight = balls[i].height = (float(hw_random16(0, 10000)) / 10000.0f); // from 0. to 1. balls[i].lastBounceUpdate = strip.now; } // check if reached ends of the strip @@ -3136,9 +3117,9 @@ static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of b * Sinelon stolen from FASTLED examples */ static uint16_t sinelon_base(bool dual, bool rainbow=false) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); SEGMENT.fade_out(SEGMENT.intensity); - unsigned pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); + unsigned pos = beatsin16_t(SEGMENT.speed/10,0,SEGLEN-1); if (SEGENV.call == 0) SEGENV.aux0 = pos; uint32_t color1 = SEGMENT.color_from_palette(pos, true, false, 0); uint32_t color2 = SEGCOLOR(2); @@ -3190,7 +3171,7 @@ static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow@!,T // utility function that will add random glitter to SEGMENT void glitter_base(uint8_t intensity, uint32_t col = ULTRAWHITE) { - if (intensity > random8()) SEGMENT.setPixelColor(random16(SEGLEN), col); + if (intensity > hw_random8()) SEGMENT.setPixelColor(hw_random16(SEGLEN), col); } //Glitter with palette background, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 @@ -3213,7 +3194,7 @@ uint16_t mode_glitter() glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE); return FRAMETIME; } -static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;,,Glitter color;!;;pal=0,m12=0"; //pixels +static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;,,Glitter color;!;;pal=11,m12=0"; //pixels //Solid colour background with glitter (can be replaced by Glitter) @@ -3241,7 +3222,7 @@ typedef struct Spark { * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h */ uint16_t mode_popcorn(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); //allocate segment data unsigned strips = SEGMENT.nrOfVStrips(); unsigned usablePopcorns = maxNumPopcorn; @@ -3267,18 +3248,18 @@ uint16_t mode_popcorn(void) { popcorn[i].pos += popcorn[i].vel; popcorn[i].vel += gravity; } else { // if kernel is inactive, randomly pop it - if (random8() < 2) { // POP!!! + if (hw_random8() < 2) { // POP!!! popcorn[i].pos = 0.01f; - unsigned peakHeight = 128 + random8(128); //0-255 + unsigned peakHeight = 128 + hw_random8(128); //0-255 peakHeight = (peakHeight * (SEGLEN -1)) >> 8; popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight); if (SEGMENT.palette) { - popcorn[i].colIndex = random8(); + popcorn[i].colIndex = hw_random8(); } else { - byte col = random8(0, NUM_COLORS); + byte col = hw_random8(0, NUM_COLORS); if (!SEGCOLOR(2) || !SEGCOLOR(col)) col = 0; popcorn[i].colIndex = col; } @@ -3310,7 +3291,7 @@ uint16_t candle(bool multi) { if (multi && SEGLEN > 1) { //allocate segment data - unsigned dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266) + unsigned dataSize = max(1, (int)SEGLEN -1) *3; //max. 1365 pixels (ESP8266) if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed } @@ -3340,7 +3321,7 @@ uint16_t candle(bool multi) s = SEGENV.data[d]; s_target = SEGENV.data[d+1]; fadeStep = SEGENV.data[d+2]; } if (fadeStep == 0) { //init vals - s = 128; s_target = 130 + random8(4); fadeStep = 1; + s = 128; s_target = 130 + hw_random8(4); fadeStep = 1; } bool newTarget = false; @@ -3353,8 +3334,8 @@ uint16_t candle(bool multi) } if (newTarget) { - s_target = random8(rndval) + random8(rndval); //between 0 and rndval*2 -2 = 252 - if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + random8(rndval); + s_target = hw_random8(rndval) + hw_random8(rndval); //between 0 and rndval*2 -2 = 252 + if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + hw_random8(rndval); unsigned offset = (255 - valrange); s_target += offset; @@ -3365,12 +3346,12 @@ uint16_t candle(bool multi) } if (i > 0) { - SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), uint8_t(s))); SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep; } else { - for (int j = 0; j < SEGLEN; j++) { - SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); + for (unsigned j = 0; j < SEGLEN; j++) { + SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), uint8_t(s))); } SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep; @@ -3416,7 +3397,7 @@ typedef struct particle { } star; uint16_t mode_starburst(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 unsigned segs = strip.getActiveSegmentsNum(); if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs @@ -3440,19 +3421,19 @@ uint16_t mode_starburst(void) { for (unsigned j = 0; j < numStars; j++) { // speed to adjust chance of a burst, max is nearly always. - if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) + if (hw_random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) { // Pick a random color and location. - unsigned startPos = random16(SEGLEN-1); - float multiplier = (float)(random8())/255.0f * 1.0f; + unsigned startPos = hw_random16(SEGLEN-1); + float multiplier = (float)(hw_random8())/255.0f * 1.0f; - stars[j].color = CRGB(SEGMENT.color_wheel(random8())); + stars[j].color = CRGB(SEGMENT.color_wheel(hw_random8())); stars[j].pos = startPos; - stars[j].vel = maxSpeed * (float)(random8())/255.0f * multiplier; + stars[j].vel = maxSpeed * (float)(hw_random8())/255.0f * multiplier; stars[j].birth = it; stars[j].last = it; // more fragments means larger burst effect - int num = random8(3,6 + (SEGMENT.intensity >> 5)); + int num = hw_random8(3,6 + (SEGMENT.intensity >> 5)); for (int i=0; i < STARBURST_MAX_FRAG; i++) { if (i < num) stars[j].fragment[i] = startPos; @@ -3488,7 +3469,7 @@ uint16_t mode_starburst(void) { float age = it-stars[j].birth; if (age < particleIgnition) { - c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), 254.5f*((age / particleIgnition)))); + c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), uint8_t(254.5f*((age / particleIgnition))))); } else { // Figure out how much to fade and shrink the star based on // its age relative to its lifetime @@ -3499,8 +3480,7 @@ uint16_t mode_starburst(void) { } else { age -= particleIgnition; fade = (age / particleFadeTime); // Fading star - byte f = 254.5f*fade; - c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), f)); + c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), uint8_t(254.5f*fade))); } } @@ -3512,13 +3492,13 @@ uint16_t mode_starburst(void) { if (stars[j].fragment[i] > 0) { float loc = stars[j].fragment[i]; if (mirrored) loc -= (loc-stars[j].pos)*2; - int start = loc - particleSize; - int end = loc + particleSize; + unsigned start = loc - particleSize; + unsigned end = loc + particleSize; if (start < 0) start = 0; if (start == end) end++; if (end > SEGLEN) end = SEGLEN; - for (int p = start; p < end; p++) { - SEGMENT.setPixelColor(p, c.r, c.g, c.b); + for (unsigned p = start; p < end; p++) { + SEGMENT.setPixelColor(p, c); } } } @@ -3536,9 +3516,9 @@ static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chanc */ uint16_t mode_exploding_fireworks(void) { - if (SEGLEN == 1) return mode_static(); - const int cols = SEGMENT.is2D() ? SEGMENT.virtualWidth() : 1; - const int rows = SEGMENT.is2D() ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + if (SEGLEN <= 1) return mode_static(); + const int cols = SEGMENT.is2D() ? SEG_W : 1; + const int rows = SEGMENT.is2D() ? SEG_H : SEGLEN; //allocate segment data unsigned maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 @@ -3547,7 +3527,7 @@ uint16_t mode_exploding_fireworks(void) if (segs <= (strip.getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg - unsigned numSparks = min(2 + ((rows*cols) >> 1), maxSparks); + unsigned numSparks = min(5 + ((rows*cols) >> 1), maxSparks); unsigned dataSize = sizeof(spark) * numSparks; if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed float *dying_gravity = reinterpret_cast(SEGENV.data + dataSize); @@ -3569,11 +3549,11 @@ uint16_t mode_exploding_fireworks(void) if (SEGENV.aux0 < 2) { //FLARE if (SEGENV.aux0 == 0) { //init flare flare->pos = 0; - flare->posX = SEGMENT.is2D() ? random16(2,cols-3) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D - unsigned peakHeight = 75 + random8(180); //0-255 + flare->posX = SEGMENT.is2D() ? hw_random16(2,cols-3) : (SEGMENT.intensity > hw_random8()); // will enable random firing side on 1D + unsigned peakHeight = 75 + hw_random8(180); //0-255 peakHeight = (peakHeight * (rows -1)) >> 8; flare->vel = sqrtf(-2.0f * gravity * peakHeight); - flare->velX = SEGMENT.is2D() ? (random8(9)-4)/64.0f : 0; // no X velocity on 1D + flare->velX = SEGMENT.is2D() ? (hw_random8(9)-4)/64.0f : 0; // no X velocity on 1D flare->col = 255; //brightness SEGENV.aux0 = 1; } @@ -3601,20 +3581,21 @@ uint16_t mode_exploding_fireworks(void) * Explosion happens where the flare ended. * Size is proportional to the height. */ - unsigned nSparks = flare->pos + random8(4); - nSparks = constrain(nSparks, 4, numSparks); + unsigned nSparks = flare->pos + hw_random8(4); + nSparks = std::max(nSparks, 4U); // This is not a standard constrain; numSparks is not guaranteed to be at least 4 + nSparks = std::min(nSparks, numSparks); // initialize sparks if (SEGENV.aux0 == 2) { for (unsigned i = 1; i < nSparks; i++) { sparks[i].pos = flare->pos; sparks[i].posX = flare->posX; - sparks[i].vel = (float(random16(20001)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 + sparks[i].vel = (float(hw_random16(20001)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips - sparks[i].velX = SEGMENT.is2D() ? (float(random16(20001)) / 10000.0f) - 1.0f : 0; // from -1 to 1 + sparks[i].velX = SEGMENT.is2D() ? (float(hw_random16(20001)) / 10000.0f) - 1.0f : 0; // from -1 to 1 sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright //sparks[i].col = constrain(sparks[i].col, 0, 345); - sparks[i].colIndex = random8(); + sparks[i].colIndex = hw_random8(); sparks[i].vel *= flare->pos/rows; // proportional to height sparks[i].velX *= SEGMENT.is2D() ? flare->posX/cols : 0; // proportional to width sparks[i].vel *= -gravity *50; @@ -3636,23 +3617,23 @@ uint16_t mode_exploding_fireworks(void) if (SEGMENT.is2D() && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue; unsigned prog = sparks[i].col; uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0); - CRGB c = CRGB::Black; //HeatColor(sparks[i].col); + CRGBW c = BLACK; //HeatColor(sparks[i].col); if (prog > 300) { //fade from white to spark color - c = CRGB(color_blend(spColor, WHITE, (prog - 300)*5)); + c = color_blend(spColor, WHITE, uint8_t((prog - 300)*5)); } else if (prog > 45) { //fade from spark color to black - c = CRGB(color_blend(BLACK, spColor, prog - 45)); + c = color_blend(BLACK, spColor, uint8_t(prog - 45)); unsigned cooling = (300 - prog) >> 5; c.g = qsub8(c.g, cooling); c.b = qsub8(c.b, cooling * 2); } - if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c.red, c.green, c.blue); - else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c.red, c.green, c.blue); + if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c); + else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c); } } if (SEGMENT.check3) SEGMENT.blur(16); *dying_gravity *= .8f; // as sparks burn out they fall slower } else { - SEGENV.aux0 = 6 + random8(10); //wait for this many frames + SEGENV.aux0 = 6 + hw_random8(10); //wait for this many frames } } else { SEGENV.aux0--; @@ -3673,7 +3654,7 @@ static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gr */ uint16_t mode_drip(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); //allocate segment data unsigned strips = SEGMENT.nrOfVStrips(); const int maxNumDrops = 4; @@ -3689,7 +3670,7 @@ uint16_t mode_drip(void) unsigned numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 float gravity = -0.0005f - (SEGMENT.speed/50000.0f); - gravity *= max(1, SEGLEN-1); + gravity *= max(1, (int)SEGLEN-1); int sourcedrop = 12; for (unsigned j=0;j255) drops[j].col=255; - SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col)); + SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,SEGCOLOR(0),uint8_t(drops[j].col))); drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling - if (random8() < drops[j].col/10) { // random drop + if (hw_random8() < drops[j].col/10) { // random drop drops[j].colIndex=2; //fall drops[j].col=255; } @@ -3719,12 +3700,12 @@ uint16_t mode_drip(void) drops[j].vel += gravity; // gravity is negative for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - unsigned pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally - SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling + unsigned pos = constrain(unsigned(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally + SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),uint8_t(drops[j].col/i))); //spread pixel with fade while falling } if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(SEGCOLOR(0),BLACK,drops[j].col)); + SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(SEGCOLOR(0),BLACK,uint8_t(drops[j].col))); } } else { // we hit bottom if (drops[j].colIndex > 2) { // already hit once, so back to forming @@ -3769,7 +3750,7 @@ typedef struct Tetris { } tetris; uint16_t mode_tetrix(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned strips = SEGMENT.nrOfVStrips(); // allow running on virtual strips (columns in 2D segment) unsigned dataSize = sizeof(tetris); if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed @@ -3793,17 +3774,17 @@ uint16_t mode_tetrix(void) { // speed calculation: a single brick should reach bottom of strip in X seconds // if the speed is set to 1 this should take 5s and at 255 it should take 0.25s // as this is dependant on SEGLEN it should be taken into account and the fact that effect runs every FRAMETIME s - int speed = SEGMENT.speed ? SEGMENT.speed : random8(1,255); + int speed = SEGMENT.speed ? SEGMENT.speed : hw_random8(1,255); speed = map(speed, 1, 255, 5000, 250); // time taken for full (SEGLEN) drop drop->speed = float(SEGLEN * FRAMETIME) / float(speed); // set speed drop->pos = SEGLEN; // start at end of segment (no need to subtract 1) - if (!SEGMENT.check1) drop->col = random8(0,15)<<4; // limit color choices so there is enough HUE gap + if (!SEGMENT.check1) drop->col = hw_random8(0,15)<<4; // limit color choices so there is enough HUE gap drop->step = 1; // drop state (0 init, 1 forming, 2 falling) - drop->brick = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick + drop->brick = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : hw_random8(1,5)) * (1+(SEGLEN>>6)); // size of brick } if (drop->step == 1) { // forming - if (random8()>>6) { // random drop + if (hw_random8()>>6) { // random drop drop->step = 2; // fall } } @@ -3812,8 +3793,8 @@ uint16_t mode_tetrix(void) { if (drop->pos > drop->stack) { // fall until top of stack drop->pos -= drop->speed; // may add gravity as: speed += gravity if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack; - for (int i = int(drop->pos); i < SEGLEN; i++) { - uint32_t col = ipos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); + for (unsigned i = unsigned(drop->pos); i < SEGLEN; i++) { + uint32_t col = i < unsigned(drop->pos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col); } } else { // we hit bottom @@ -3827,7 +3808,7 @@ uint16_t mode_tetrix(void) { drop->brick = 0; // reset brick size (no more growing) if (drop->step > strip.now) { // allow fading of virtual strip - for (int i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend + for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend } else { drop->stack = 0; // reset brick stack size drop->step = 0; // proceed with next brick @@ -3852,15 +3833,15 @@ static const char _data_FX_MODE_TETRIX[] PROGMEM = "Tetrix@!,Width,,,,One color; uint16_t mode_plasma(void) { // initialize phases on start if (SEGENV.call == 0) { - SEGENV.aux0 = random8(0,2); // add a bit of randomness + SEGENV.aux0 = hw_random8(0,2); // add a bit of randomness } - unsigned thisPhase = beatsin8(6+SEGENV.aux0,-64,64); - unsigned thatPhase = beatsin8(7+SEGENV.aux0,-64,64); + unsigned thisPhase = beatsin8_t(6+SEGENV.aux0,-64,64); + unsigned thatPhase = beatsin8_t(7+SEGENV.aux0,-64,64); for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows: unsigned colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. - + cos8((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. - unsigned thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1)))); + + cos8_t((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. + unsigned thisBright = qsub8(colorIndex, beatsin8_t(7,0, (128 - (SEGMENT.intensity>>1)))); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0, thisBright)); } @@ -3884,7 +3865,7 @@ uint16_t mode_percent(void) { if (SEGMENT.speed == 255) size = 255; if (percent <= 100) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (i < SEGENV.aux1) { if (SEGMENT.check1) SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,0,100,0,255), false, false, 0)); @@ -3896,7 +3877,7 @@ uint16_t mode_percent(void) { } } } else { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (i < (SEGLEN - SEGENV.aux1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); } @@ -3946,8 +3927,8 @@ uint16_t mode_heartbeat(void) { SEGENV.step = strip.now; } - for (int i = 0; i < SEGLEN; i++) { - SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); + for (unsigned i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255 - (SEGENV.aux1 >> 8)))); } return FRAMETIME; @@ -3980,19 +3961,19 @@ static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;01;m1 // Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino // // Add one layer of waves into the led array -static CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) +static CRGB pacifica_one_layer(uint16_t i, const CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) { unsigned ci = cistart; unsigned waveangle = ioff; unsigned wavescale_half = (wavescale >> 1) + 20; waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i - unsigned s16 = sin16(waveangle) + 32768; + unsigned s16 = sin16_t(waveangle) + 32768; unsigned cs = scale16(s16, wavescale_half) + wavescale_half; ci += (cs * i); - unsigned sindex16 = sin16(ci) + 32768; + unsigned sindex16 = sin16_t(ci) + 32768; unsigned sindex8 = scale16(sindex16, 240); - return ColorFromPalette(p, sindex8, bri, LINEARBLEND); + return CRGB(ColorFromPalette(p, sindex8, bri, LINEARBLEND)); } uint16_t mode_pacifica() @@ -4022,34 +4003,34 @@ uint16_t mode_pacifica() uint64_t deltat = (strip.now >> 2) + ((strip.now * SEGMENT.speed) >> 7); strip.now = deltat; - unsigned speedfactor1 = beatsin16(3, 179, 269); - unsigned speedfactor2 = beatsin16(4, 179, 269); + unsigned speedfactor1 = beatsin16_t(3, 179, 269); + unsigned speedfactor2 = beatsin16_t(4, 179, 269); uint32_t deltams1 = (deltams * speedfactor1) / 256; uint32_t deltams2 = (deltams * speedfactor2) / 256; uint32_t deltams21 = (deltams1 + deltams2) / 2; - sCIStart1 += (deltams1 * beatsin88(1011,10,13)); - sCIStart2 -= (deltams21 * beatsin88(777,8,11)); - sCIStart3 -= (deltams1 * beatsin88(501,5,7)); - sCIStart4 -= (deltams2 * beatsin88(257,4,6)); + sCIStart1 += (deltams1 * beatsin88_t(1011,10,13)); + sCIStart2 -= (deltams21 * beatsin88_t(777,8,11)); + sCIStart3 -= (deltams1 * beatsin88_t(501,5,7)); + sCIStart4 -= (deltams2 * beatsin88_t(257,4,6)); SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2; SEGENV.step = (sCIStart4 << 16) | (sCIStart3 & 0xFFFF); // Clear out the LED array to a dim background blue-green //SEGMENT.fill(132618); - unsigned basethreshold = beatsin8( 9, 55, 65); + unsigned basethreshold = beatsin8_t( 9, 55, 65); unsigned wave = beat8( 7 ); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { CRGB c = CRGB(2, 6, 10); // Render each of four layers, with different scales and speeds, that vary over time - c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301)); - c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401)); - c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8(9, 10,38) , 0-beat16(503)); - c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8(8, 10,28) , beat16(601)); + c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16_t(3, 11 * 256, 14 * 256), beatsin8_t(10, 70, 130), 0-beat16(301)); + c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16_t(4, 6 * 256, 9 * 256), beatsin8_t(17, 40, 80), beat16(401)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8_t(9, 10,38) , 0-beat16(503)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8_t(8, 10,28) , beat16(601)); // Add extra 'white' to areas where the four layers of light have lined up brightly - unsigned threshold = scale8( sin8( wave), 20) + basethreshold; + unsigned threshold = scale8( sin8_t( wave), 20) + basethreshold; wave += 7; unsigned l = c.getAverageLight(); if (l > threshold) { @@ -4076,7 +4057,7 @@ static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=5 * Mode simulates a gradual sunrise */ uint16_t mode_sunrise() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); //speed 0 - static sun //speed 1 - 60: sunrise time in minutes //speed 60 - 120 : sunset time in minutes - 60; @@ -4103,15 +4084,12 @@ uint16_t mode_sunrise() { if (SEGMENT.speed > 60) stage = 0xFFFF - stage; //sunset } - for (int i = 0; i <= SEGLEN/2; i++) + for (unsigned i = 0; i <= SEGLEN/2; i++) { - //default palette is Fire - uint32_t c = SEGMENT.color_from_palette(0, false, true, 255); //background - + //default palette is Fire unsigned wave = triwave16((i * stage) / SEGLEN); - wave = (wave >> 8) + ((wave * SEGMENT.intensity) >> 15); - + uint32_t c; if (wave > 240) { //clipped, full white sun c = SEGMENT.color_from_palette( 240, false, true, 255); } else { //transition @@ -4123,7 +4101,7 @@ uint16_t mode_sunrise() { return FRAMETIME; } -static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min],Width;;!;;sx=60"; +static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min],Width;;!;;pal=35,sx=60"; /* @@ -4139,14 +4117,14 @@ static uint16_t phased_base(uint8_t moder) { // We're making si unsigned index = strip.now/64; // Set color rotation speed *phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4) - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (moder == 1) modVal = (inoise8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise. unsigned val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that led 0 is used. if (modVal == 0) modVal = 1; val += *phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline. unsigned b = cubicwave8(val); // Now we make an 8 bit sinewave. b = (b > cutOff) ? (b - cutOff) : 0; // A ternary operator to cutoff the light. - SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), b)); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), uint8_t(b))); index += 256 / SEGLEN; if (SEGLEN > 256) index ++; // Correction for segments longer than 256 LEDs } @@ -4171,9 +4149,9 @@ uint16_t mode_twinkleup(void) { // A very short twinkle routine unsigned prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work. - unsigned pixBri = sin8(ranstart + 16 * strip.now/(256-SEGMENT.speed)); + unsigned pixBri = sin8_t(ranstart + 16 * strip.now/(256-SEGMENT.speed)); if (random8() > SEGMENT.intensity) pixBri = 0; SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri)); } @@ -4199,24 +4177,21 @@ uint16_t mode_noisepal(void) { // Slow noise { SEGENV.step = strip.now; - unsigned baseI = random8(); - palettes[1] = CRGBPalette16(CHSV(baseI+random8(64), 255, random8(128,255)), CHSV(baseI+128, 255, random8(128,255)), CHSV(baseI+random8(92), 192, random8(128,255)), CHSV(baseI+random8(92), 255, random8(128,255))); + unsigned baseI = hw_random8(); + palettes[1] = CRGBPalette16(CHSV(baseI+hw_random8(64), 255, hw_random8(128,255)), CHSV(baseI+128, 255, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 192, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 255, hw_random8(128,255))); } - CRGB color; - //EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms) nblendPaletteTowardPalette(palettes[0], palettes[1], 48); // Blend towards the target palette over 48 iterations. if (SEGMENT.palette > 0) palettes[0] = SEGPALETTE; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. - color = ColorFromPalette(palettes[0], index, 255, LINEARBLEND); // Use the my own palette. - SEGMENT.setPixelColor(i, color.red, color.green, color.blue); + SEGMENT.setPixelColor(i, ColorFromPalette(palettes[0], index, 255, LINEARBLEND)); // Use my own palette. } - SEGENV.aux0 += beatsin8(10,1,4); // Moving along the distance. Vary it a bit with a sine wave. + SEGENV.aux0 += beatsin8_t(10,1,4); // Moving along the distance. Vary it a bit with a sine wave. return FRAMETIME; } @@ -4234,8 +4209,8 @@ uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tul SEGENV.step += SEGMENT.speed/16; // Speed of animation. unsigned freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal. - for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows: - int pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub).. + for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows: + uint8_t pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub).. //setPixCol(i, i*colorIndex/255, pixBri); SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri)); } @@ -4289,7 +4264,7 @@ static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //ver */ uint16_t mode_chunchun(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); SEGMENT.fade_out(254); // add a bit of trail unsigned counter = strip.now * (6 + (SEGMENT.speed >> 4)); unsigned numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment @@ -4298,11 +4273,10 @@ uint16_t mode_chunchun(void) for (unsigned i = 0; i < numBirds; i++) { counter -= span; - unsigned megumin = sin16(counter) + 0x8000; + unsigned megumin = sin16_t(counter) + 0x8000; unsigned bird = uint32_t(megumin * SEGLEN) >> 16; - uint32_t c = SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping bird = constrain(bird, 0U, SEGLEN-1U); - SEGMENT.setPixelColor(bird, c); + SEGMENT.setPixelColor(bird, SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0)); // no palette wrapping } return FRAMETIME; } @@ -4341,7 +4315,7 @@ typedef struct Spotlight { */ uint16_t mode_dancing_shadows(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); unsigned numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 bool initialize = SEGENV.aux0 != numSpotlights; SEGENV.aux0 = numSpotlights; @@ -4366,21 +4340,21 @@ uint16_t mode_dancing_shadows(void) spotlights[i].lastUpdateTime = time; } - respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (SEGLEN + 2)) + respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (int)(SEGLEN + 2)) || (spotlights[i].speed < 0.0 && spotlights[i].position < -(spotlights[i].width + 2)); } if (initialize || respawn) { - spotlights[i].colorIdx = random8(); - spotlights[i].width = random8(1, 10); + spotlights[i].colorIdx = hw_random8(); + spotlights[i].width = hw_random8(1, 10); - spotlights[i].speed = 1.0/random8(4, 50); + spotlights[i].speed = 1.0/hw_random8(4, 50); if (initialize) { - spotlights[i].position = random16(SEGLEN); - spotlights[i].speed *= random8(2) ? 1.0 : -1.0; + spotlights[i].position = hw_random16(SEGLEN); + spotlights[i].speed *= hw_random8(2) ? 1.0 : -1.0; } else { - if (random8(2)) { + if (hw_random8(2)) { spotlights[i].position = SEGLEN + spotlights[i].width; spotlights[i].speed *= -1.0; }else { @@ -4389,14 +4363,14 @@ uint16_t mode_dancing_shadows(void) } spotlights[i].lastUpdateTime = time; - spotlights[i].type = random8(SPOT_TYPES_COUNT); + spotlights[i].type = hw_random8(SPOT_TYPES_COUNT); } uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 255); int start = spotlights[i].position; if (spotlights[i].width <= 1) { - if (start >= 0 && start < SEGLEN) { + if (start >= 0 && start < (int)SEGLEN) { SEGMENT.blendPixelColor(start, color, 128); } } else { @@ -4466,8 +4440,8 @@ uint16_t mode_washing_machine(void) { SEGENV.step += (speed * 2048) / (512 - SEGMENT.speed); - for (int i = 0; i < SEGLEN; i++) { - uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7)); + for (unsigned i = 0; i < SEGLEN; i++) { + uint8_t col = sin8_t(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7)); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); } @@ -4485,7 +4459,7 @@ uint16_t mode_blends(void) { unsigned dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed uint32_t* pixels = reinterpret_cast(SEGENV.data); - unsigned blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); + uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); unsigned shift = (strip.now * ((SEGMENT.speed >> 3) +1)) >> 8; for (unsigned i = 0; i < pixelLen; i++) { @@ -4496,7 +4470,7 @@ uint16_t mode_blends(void) { unsigned offset = 0; for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, pixels[offset++]); - if (offset > pixelLen) offset = 0; + if (offset >= pixelLen) offset = 0; } return FRAMETIME; @@ -4548,10 +4522,10 @@ uint16_t mode_tv_simulator(void) { // create a new sceene if (((strip.now - tvSimulator->sceeneStart) >= tvSimulator->sceeneDuration) || SEGENV.aux1 == 0) { tvSimulator->sceeneStart = strip.now; // remember the start of the new sceene - tvSimulator->sceeneDuration = random16(60* 250* colorSpeed, 60* 750 * colorSpeed); // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider) - tvSimulator->sceeneColorHue = random16( 0, 768); // random start color-tone for the sceene - tvSimulator->sceeneColorSat = random8 ( 100, 130 + colorIntensity); // random start color-saturation for the sceene - tvSimulator->sceeneColorBri = random8 ( 200, 240); // random start color-brightness for the sceene + tvSimulator->sceeneDuration = hw_random16(60* 250* colorSpeed, 60* 750 * colorSpeed); // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider) + tvSimulator->sceeneColorHue = hw_random16( 0, 768); // random start color-tone for the sceene + tvSimulator->sceeneColorSat = hw_random8 ( 100, 130 + colorIntensity); // random start color-saturation for the sceene + tvSimulator->sceeneColorBri = hw_random8 ( 200, 240); // random start color-brightness for the sceene SEGENV.aux1 = 1; SEGENV.aux0 = 0; } @@ -4559,16 +4533,16 @@ uint16_t mode_tv_simulator(void) { // slightly change the color-tone in this sceene if (SEGENV.aux0 == 0) { // hue change in both directions - j = random8(4 * colorIntensity); - hue = (random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative + j = hw_random8(4 * colorIntensity); + hue = (hw_random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative ((j + tvSimulator->sceeneColorHue) < 767 ? tvSimulator->sceeneColorHue + j : tvSimulator->sceeneColorHue + j - 767) ; // positive // saturation - j = random8(2 * colorIntensity); + j = hw_random8(2 * colorIntensity); sat = (tvSimulator->sceeneColorSat - j) < 0 ? 0 : tvSimulator->sceeneColorSat - j; // brightness - j = random8(100); + j = hw_random8(100); bri = (tvSimulator->sceeneColorBri - j) < 0 ? 0 : tvSimulator->sceeneColorBri - j; // calculate R,G,B from HSV @@ -4594,9 +4568,9 @@ uint16_t mode_tv_simulator(void) { SEGENV.aux0 = 1; // randomize total duration and fade duration for the actual color - tvSimulator->totalTime = random16(250, 2500); // Semi-random pixel-to-pixel time - tvSimulator->fadeTime = random16(0, tvSimulator->totalTime); // Pixel-to-pixel transition time - if (random8(10) < 3) tvSimulator->fadeTime = 0; // Force scene cut 30% of time + tvSimulator->totalTime = hw_random16(250, 2500); // Semi-random pixel-to-pixel time + tvSimulator->fadeTime = hw_random16(0, tvSimulator->totalTime); // Pixel-to-pixel transition time + if (hw_random8(10) < 3) tvSimulator->fadeTime = 0; // Force scene cut 30% of time tvSimulator->startTime = strip.now; } // end of initialization @@ -4616,7 +4590,7 @@ uint16_t mode_tv_simulator(void) { } // set strip color - for (i = 0; i < SEGLEN; i++) { + for (i = 0; i < (int)SEGLEN; i++) { SEGMENT.setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit } @@ -4630,7 +4604,7 @@ uint16_t mode_tv_simulator(void) { return FRAMETIME; } -static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;"; +static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01"; /* @@ -4661,15 +4635,15 @@ class AuroraWave { public: void init(uint32_t segment_length, CRGB color) { - ttl = random16(500, 1501); + ttl = hw_random16(500, 1501); basecolor = color; - basealpha = random8(60, 101) / (float)100; + basealpha = hw_random8(60, 101) / (float)100; age = 0; - width = random16(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier + width = hw_random16(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier if (!width) width = 1; - center = random8(101) / (float)100 * segment_length; - goingleft = random8(0, 2) == 0; - speed_factor = (random8(10, 31) / (float)100 * W_MAX_SPEED / 255); + center = hw_random8(101) / (float)100 * segment_length; + goingleft = hw_random8(0, 2) == 0; + speed_factor = (hw_random8(10, 31) / (float)100 * W_MAX_SPEED / 255); alive = true; } @@ -4754,7 +4728,7 @@ uint16_t mode_aurora(void) { waves = reinterpret_cast(SEGENV.data); for (int i = 0; i < SEGENV.aux1; i++) { - waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random8(0, 3)))); + waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3)))); } } else { waves = reinterpret_cast(SEGENV.data); @@ -4766,7 +4740,7 @@ uint16_t mode_aurora(void) { if(!(waves[i].stillAlive())) { //If a wave dies, reinitialize it starts over. - waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random8(0, 3)))); + waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3)))); } } @@ -4775,7 +4749,7 @@ uint16_t mode_aurora(void) { if (SEGCOLOR(1)) backlight++; if (SEGCOLOR(2)) backlight++; //Loop through LEDs to determine color - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { CRGB mixedRgb = CRGB(backlight, backlight, backlight); //For each LED we must check each wave if it is "active" at this position. @@ -4803,7 +4777,7 @@ static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pa // 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline. // Controls are speed, # of pixels, faderate. uint16_t mode_perlinmove(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); SEGMENT.fade_out(255-SEGMENT.custom1); for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { unsigned locn = inoise16(strip.now*128/(260-SEGMENT.speed)+i*15000, strip.now*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. @@ -4822,9 +4796,9 @@ static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixel // Uses beatsin8() + phase shifting. By: Andrew Tuline uint16_t mode_wavesins(void) { - for (int i = 0; i < SEGLEN; i++) { - uint8_t bri = sin8(strip.now/4 + i * SEGMENT.intensity); - uint8_t index = beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider + for (unsigned i = 0; i < SEGLEN; i++) { + uint8_t bri = sin8_t(strip.now/4 + i * SEGMENT.intensity); + uint8_t index = beatsin8_t(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider //SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, index, bri, LINEARBLEND)); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, bri)); } @@ -4839,16 +4813,16 @@ static const char _data_FX_MODE_WAVESINS[] PROGMEM = "Wavesins@!,Brightness vari ////////////////////////////// // By: ldirko https://editor.soulmatelights.com/gallery/392-flow-led-stripe , modifed by: Andrew Tuline uint16_t mode_FlowStripe(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); const int hl = SEGLEN * 10 / 13; uint8_t hue = strip.now / (SEGMENT.speed+1); uint32_t t = strip.now / (SEGMENT.intensity/8+1); - for (int i = 0; i < SEGLEN; i++) { - int c = (abs(i - hl) / hl) * 127; - c = sin8(c); - c = sin8(c / 2 + t); - byte b = sin8(c + t/8); + for (unsigned i = 0; i < SEGLEN; i++) { + int c = (abs((int)i - hl) / hl) * 127; + c = sin8_t(c); + c = sin8_t(c / 2 + t); + byte b = sin8_t(c + t/8); SEGMENT.setPixelColor(i, CHSV(b + hue, 255, 255)); } @@ -4867,22 +4841,22 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; int x, y; SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails unsigned long t = strip.now/128; // timebase // outer stars for (size_t i = 0; i < 8; i++) { - x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i); - y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i); + x = beatsin8_t(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i); + y = beatsin8_t(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i); SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255)); } // inner stars 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); - y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i); + x = beatsin8_t(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i); + y = beatsin8_t(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i); SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255)); } // central white dot @@ -4901,8 +4875,8 @@ static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Ou uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGENV.aux0 = 0; // start with red hue @@ -4914,13 +4888,13 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so byte numLines = SEGMENT.intensity/16 + 1; SEGENV.aux0++; // hue - SEGMENT.fadeToBlackBy(40); + SEGMENT.fadeToBlackBy(40 - SEGMENT.check2 * 8); for (size_t i = 0; i < numLines; i++) { - byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1)); - byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (rows - 1)); - 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); - CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); + byte x1 = beatsin8_t(2 + SEGMENT.speed/16, 0, (cols - 1)); + byte x2 = beatsin8_t(1 + SEGMENT.speed/16, 0, (rows - 1)); + byte y1 = beatsin8_t(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24); + byte y2 = beatsin8_t(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64); + uint32_t color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); byte xsteps = abs8(x1 - y1) + 1; byte ysteps = abs8(x2 - y2) + 1; @@ -4940,11 +4914,11 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so SEGMENT.setPixelColorXY(y1, y2, DARKSLATEGRAY); } } - if (SEGMENT.custom3) SEGMENT.blur(SEGMENT.custom3/2); + SEGMENT.blur(SEGMENT.custom3>>1, SEGMENT.check2); return FRAMETIME; } // mode_2DColoredBursts() -static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines,,,Blur,Gradient,,Dots;;!;2;c3=16"; +static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines,,,Blur,Gradient,Smear,Dots;;!;2;c3=16"; ///////////////////// @@ -4953,20 +4927,19 @@ static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Spee uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(64); for (int i = 0; i < cols; i++) { - SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+strip.now/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND)); - SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+strip.now/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND)); + SEGMENT.setPixelColorXY(i, beatsin8_t(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+strip.now/17, beatsin8_t(5, 55, 255, 0, i*10), LINEARBLEND)); + SEGMENT.setPixelColorXY(i, beatsin8_t(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+strip.now/17, beatsin8_t(5, 55, 255, 0, i*10+128), LINEARBLEND)); } - SEGMENT.blur(SEGMENT.intensity>>3); + SEGMENT.blur(SEGMENT.intensity / (8 - (SEGMENT.check1 * 2)), SEGMENT.check1); return FRAMETIME; } // mode_2Ddna() -static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2"; - +static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur,,,,Smear;;!;2;ix=0"; ///////////////////////// // 2D DNA Spiral // @@ -4974,8 +4947,8 @@ static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2"; uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/512-dna-spiral-variation , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -4988,8 +4961,8 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma SEGMENT.fadeToBlackBy(135); for (int i = 0; i < rows; i++) { - int x = beatsin8(speeds, 0, cols - 1, 0, i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, i * freq + 128); - int x1 = beatsin8(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq); + int x = beatsin8_t(speeds, 0, cols - 1, 0, i * freq) + beatsin8_t(speeds - 7, 0, cols - 1, 0, i * freq + 128); + int x1 = beatsin8_t(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8_t(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq); unsigned hue = (i * 128 / rows) + ms; // skip every 4th row every now and then (fade it more) if ((i + ms / 8) & 3) { @@ -5009,10 +4982,11 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma SEGMENT.setPixelColorXY(x1, i, WHITE); } } + SEGMENT.blur(((uint16_t)SEGMENT.custom1 * 3) / (6 + SEGMENT.check1), SEGMENT.check1); return FRAMETIME; } // mode_2DDNASpiral() -static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency;;!;2"; +static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency,Blur,,,Smear;;!;2;c1=0"; ///////////////////////// @@ -5021,8 +4995,8 @@ static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const int colsCenter = (cols>>1) + (cols%2); const int rowsCenter = (rows>>1) + (rows%2); @@ -5038,11 +5012,11 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli SEGMENT.setPixelColorXY(colsCenter + mySin, rowsCenter + myCos, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND)); if (SEGMENT.check1) SEGMENT.setPixelColorXY(colsCenter + myCos, rowsCenter + mySin, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND)); } - SEGMENT.blur(SEGMENT.intensity>>3); + SEGMENT.blur(SEGMENT.intensity>>(3 - SEGMENT.check2), SEGMENT.check2); return FRAMETIME; } // mode_2DDrift() -static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount,,,,Twin;;!;2"; +static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur,,,,Twin,Smear;;!;2;ix=0"; ////////////////////////// @@ -5051,8 +5025,8 @@ static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur a uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5062,15 +5036,11 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline unsigned yscale = SEGMENT.speed*8; unsigned indexx = 0; - CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : CRGBPalette16(CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black, - CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, - CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, - CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); - + CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : SEGMENT.loadPalette(pal, 35); for (int j=0; j < cols; j++) { for (int i=0; i < rows; i++) { indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. - SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*(indexx)>>4, 255U), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*indexx/11, 225U), i*255/rows, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. } // for i } // for j @@ -5085,20 +5055,19 @@ static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y sca uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; - SEGMENT.fadeToBlackBy(16); + SEGMENT.fadeToBlackBy(16 + SEGMENT.check1 * 10); for (size_t i = 8; i > 0; i--) { - SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1), - beatsin8(SEGMENT.intensity/8 - i, 0, rows - 1), - ColorFromPalette(SEGPALETTE, beatsin8(12, 0, 255), 255, LINEARBLEND)); + SEGMENT.addPixelColorXY(beatsin8_t(SEGMENT.speed/8 + i, 0, cols - 1), + beatsin8_t(SEGMENT.intensity/8 - i, 0, rows - 1), + ColorFromPalette(SEGPALETTE, beatsin8_t(12, 0, 255), 255, LINEARBLEND)); } - SEGMENT.blur(SEGMENT.custom1>>3); - + SEGMENT.blur(SEGMENT.custom1 >> (3 + SEGMENT.check1), SEGMENT.check1); return FRAMETIME; } // mode_2DFrizzles() -static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur;;!;2"; +static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur,,,Smear;;!;2"; /////////////////////////////////////////// @@ -5112,8 +5081,8 @@ typedef struct ColorCount { uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const unsigned dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled const int crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi) @@ -5126,15 +5095,14 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { SEGENV.step = strip.now; SEGENV.aux0 = 0; - //random16_set_seed(millis()>>2); //seed the random generator //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - unsigned state = random8()%2; + unsigned state = hw_random8()%2; if (state == 0) SEGMENT.setPixelColorXY(x,y, backgroundColor); else - SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); + SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(hw_random8(), false, PALETTE_SOLID_WRAP, 255)); } for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; @@ -5189,9 +5157,9 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int i=0; i<9 && colorsCount[i].count != 0; i++) if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; // assign the dominant color w/ a bit of randomness to avoid "gliders" - if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); - } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation - SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); + if (dominantColorCount.count > 0 && hw_random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); + } else if ((col == bgc) && (neighbors == 2) && !hw_random8(128)) { // Mutation + SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(hw_random8(), false, PALETTE_SOLID_WRAP, 255)); } // else do nothing! } //x,y @@ -5218,13 +5186,13 @@ static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2 uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const uint32_t a = strip.now / ((SEGMENT.custom3>>1)+1); for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { - SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8(cos8(x * SEGMENT.speed/16 + a / 3) + sin8(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8_t(cos8_t(x * SEGMENT.speed/16 + a / 3) + sin8_t(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0)); } } @@ -5250,8 +5218,8 @@ typedef struct Julia { uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (!SEGENV.allocateData(sizeof(julia))) return mode_static(); Julia* julias = reinterpret_cast(SEGENV.data); @@ -5300,8 +5268,8 @@ uint16_t mode_2DJulia(void) { // An animated Julia set reAl = -0.94299f; // PixelBlaze example imAg = 0.3162f; - reAl += sin_t((float)strip.now/305.f)/20.f; - imAg += sin_t((float)strip.now/405.f)/20.f; + reAl += (float)sin16_t(strip.now * 34) / 655340.f; + imAg += (float)sin16_t(strip.now * 26) / 655340.f; dx = (xmax - xmin) / (cols); // Scale the delta x and y values to our matrix size. dy = (ymax - ymin) / (rows); @@ -5343,11 +5311,12 @@ uint16_t mode_2DJulia(void) { // An animated Julia set } y += dy; } -// SEGMENT.blur(64); + if(SEGMENT.check1) + SEGMENT.blur(100, true); return FRAMETIME; } // mode_2DJulia() -static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size;!;!;2;ix=24,c1=128,c2=128,c3=16"; +static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size, Blur;!;!;2;ix=24,c1=128,c2=128,c3=16"; ////////////////////////////// @@ -5356,26 +5325,27 @@ static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per p uint16_t mode_2DLissajous(void) { // By: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(SEGMENT.intensity); uint_fast16_t phase = (strip.now * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed //for (int i=0; i < 4*(cols+rows); i ++) { for (int i=0; i < 256; i ++) { - //float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f; - //float ylocn = float(cos8(now/4+i*2)) / 255.0f; - uint_fast8_t xlocn = sin8(phase/2 + (i*SEGMENT.speed)/32); - uint_fast8_t ylocn = cos8(phase/2 + i*2); + //float xlocn = float(sin8_t(now/4+i*(SEGMENT.speed>>5))) / 255.0f; + //float ylocn = float(cos8_t(now/4+i*2)) / 255.0f; + uint_fast8_t xlocn = sin8_t(phase/2 + (i*SEGMENT.speed)/32); + uint_fast8_t ylocn = cos8_t(phase/2 + i*2); xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map() SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0)); } + SEGMENT.blur(SEGMENT.custom1 >> (1 + SEGMENT.check1 * 3), SEGMENT.check1); return FRAMETIME; } // mode_2DLissajous() -static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;c3=15"; +static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,Blur,,Speed,Smear;!;!;2;c1=0"; /////////////////////// @@ -5384,8 +5354,8 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,F uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned dataSize = (SEGMENT.length()+7) >> 3; //1 bit per LED for trails if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -5433,8 +5403,8 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. } // spawn new falling code - if (random8() <= SEGMENT.intensity || emptyScreen) { - uint8_t spawnX = random8(cols); + if (hw_random8() <= SEGMENT.intensity || emptyScreen) { + uint8_t spawnX = hw_random8(cols); SEGMENT.setPixelColorXY(spawnX, 0, spawnColor); // update hint for next run unsigned index = XY(spawnX, 0) >> 3; @@ -5454,8 +5424,8 @@ static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Tra uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; float speed = 0.25f * (1+(SEGMENT.speed>>6)); @@ -5467,8 +5437,8 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have int y3 = map(inoise8(strip.now * speed, 25355, 22685), 0, 255, 0, rows-1); // and one Lissajou function - int x1 = beatsin8(23 * speed, 0, cols-1); - int y1 = beatsin8(28 * speed, 0, rows-1); + int x1 = beatsin8_t(23 * speed, 0, cols-1); + int y1 = beatsin8_t(28 * speed, 0, rows-1); for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) { @@ -5476,15 +5446,15 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have // and add them together with weightening unsigned dx = abs(x - x1); unsigned dy = abs(y - y1); - unsigned dist = 2 * sqrt16((dx * dx) + (dy * dy)); + unsigned dist = 2 * sqrt32_bw((dx * dx) + (dy * dy)); dx = abs(x - x2); dy = abs(y - y2); - dist += sqrt16((dx * dx) + (dy * dy)); + dist += sqrt32_bw((dx * dx) + (dy * dy)); dx = abs(x - x3); dy = abs(y - y3); - dist += sqrt16((dx * dx) + (dy * dy)); + dist += sqrt32_bw((dx * dx) + (dy * dy)); // inverse result int color = dist ? 1000 / dist : 255; @@ -5513,8 +5483,8 @@ static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2"; uint16_t mode_2Dnoise(void) { // By Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const unsigned scale = SEGMENT.intensity+2; @@ -5536,8 +5506,8 @@ static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2"; uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); uint_fast32_t t = (strip.now * 8) / (256 - SEGMENT.speed); // optimized to avoid float @@ -5570,16 +5540,12 @@ static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fad //////////////////////////////// // 2D Polar Lights // //////////////////////////////// -//static float fmap(const float x, const float in_min, const float in_max, const float out_min, const float out_max) { -// return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min; -//} -uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline + +uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline & @dedehai (palette support) if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); - - CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5588,37 +5554,22 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https float adjustHeight = (float)map(rows, 8, 32, 28, 12); // maybe use mapf() ??? unsigned adjScale = map(cols, 8, 64, 310, 63); -/* - if (SEGENV.aux1 != SEGMENT.custom1/12) { // Hacky palette rotation. We need that black. - SEGENV.aux1 = SEGMENT.custom1/12; - for (int i = 0; i < 16; i++) { - long ilk; - ilk = (long)currentPalette[i].r << 16; - ilk += (long)currentPalette[i].g << 8; - ilk += (long)currentPalette[i].b; - ilk = (ilk << SEGENV.aux1) | (ilk >> (24 - SEGENV.aux1)); - currentPalette[i].r = ilk >> 16; - currentPalette[i].g = ilk >> 8; - currentPalette[i].b = ilk; - } - } -*/ unsigned _scale = map(SEGMENT.intensity, 0, 255, 30, adjScale); int _speed = map(SEGMENT.speed, 0, 255, 128, 16); for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { SEGENV.step++; - SEGMENT.setPixelColorXY(x, y, ColorFromPalette(auroraPalette, - qsub8( - inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), - fabsf((float)rows / 2.0f - (float)y) * adjustHeight))); + uint8_t palindex = qsub8(inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), fabsf((float)rows / 2.0f - (float)y) * adjustHeight); + uint8_t palbrightness = palindex; + if(SEGMENT.check1) palindex = 255 - palindex; //flip palette + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(palindex, false, false, 255, palbrightness)); } } return FRAMETIME; } // mode_2DPolarLights() -static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale;;;2"; +static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale,,,,Flip Palette;;!;2;pal=71"; ///////////////////////// @@ -5627,13 +5578,13 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale; uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); uint32_t a = strip.now / (18 - SEGMENT.speed / 16); int x = (a / 14) % cols; - int y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0); + int y = map((sin8_t(a * 5) + sin8_t(a * 4) + sin8_t(a * 2)), 0, 765, rows-1, 0); SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND)); SEGMENT.blur(SEGMENT.intensity>>4); @@ -5649,27 +5600,27 @@ static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2"; uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); } - SEGMENT.fadeToBlackBy(SEGMENT.custom1>>3); + SEGMENT.fadeToBlackBy((SEGMENT.custom1>>3) + (SEGMENT.check1 * 24)); byte t1 = strip.now / (257 - SEGMENT.speed); // 20; - byte t2 = sin8(t1) / 4 * 2; + byte t2 = sin8_t(t1) / 4 * 2; for (int i = 0; i < 13; i++) { - int x = sin8(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15! - int y = sin8(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15! + int x = sin8_t(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15! + int y = sin8_t(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15! SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, i * 255 / 13, 255, LINEARBLEND)); } - SEGMENT.blur(SEGMENT.custom2>>3); + SEGMENT.blur(SEGMENT.custom2 >> (3 + SEGMENT.check1), SEGMENT.check1); return FRAMETIME; } // mode_2DSindots() -static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fade rate,Blur;;!;2"; +static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fade rate,Blur,,Smear;;!;2;"; ////////////////////////////// @@ -5680,21 +5631,21 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g // Modifed by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const uint8_t kBorderWidth = 2; - SEGMENT.fadeToBlackBy(24); + SEGMENT.fadeToBlackBy(1 + SEGMENT.intensity / 5); SEGMENT.blur(SEGMENT.custom3>>1); // Use two out-of-sync sine waves - int i = beatsin8(19, kBorderWidth, cols-kBorderWidth); - int j = beatsin8(22, kBorderWidth, cols-kBorderWidth); - int k = beatsin8(17, kBorderWidth, cols-kBorderWidth); - int m = beatsin8(18, kBorderWidth, rows-kBorderWidth); - int n = beatsin8(15, kBorderWidth, rows-kBorderWidth); - int p = beatsin8(20, kBorderWidth, rows-kBorderWidth); + int i = beatsin8_t(19, kBorderWidth, cols-kBorderWidth); + int j = beatsin8_t(22, kBorderWidth, cols-kBorderWidth); + int k = beatsin8_t(17, kBorderWidth, cols-kBorderWidth); + int m = beatsin8_t(18, kBorderWidth, rows-kBorderWidth); + int n = beatsin8_t(15, kBorderWidth, rows-kBorderWidth); + int p = beatsin8_t(20, kBorderWidth, rows-kBorderWidth); SEGMENT.addPixelColorXY(i, m, ColorFromPalette(SEGPALETTE, strip.now/29, 255, LINEARBLEND)); SEGMENT.addPixelColorXY(j, n, ColorFromPalette(SEGPALETTE, strip.now/41, 255, LINEARBLEND)); @@ -5702,7 +5653,7 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g return FRAMETIME; } // mode_2Dsquaredswirl() -static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Blur;;!;2"; +static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,Fade,,,Blur;;!;2"; ////////////////////////////// @@ -5711,8 +5662,8 @@ static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Bl uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_static(); //allocation failed byte *bump = reinterpret_cast(SEGENV.data); @@ -5761,8 +5712,8 @@ static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Varian uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5770,19 +5721,19 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so uint8_t hue, bri; size_t intensity; - int offsetX = beatsin16(3, -360, 360); - int offsetY = beatsin16(2, -360, 360); + int offsetX = beatsin16_t(3, -360, 360); + int offsetY = beatsin16_t(2, -360, 360); int sharpness = SEGMENT.custom3 / 8; // 0-3 for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { - hue = x * beatsin16(10, 1, 10) + offsetY; - intensity = bri = sin8(x * SEGMENT.speed/2 + offsetX); + hue = x * beatsin16_t(10, 1, 10) + offsetY; + intensity = bri = sin8_t(x * SEGMENT.speed/2 + offsetX); for (int i=0; i>= 8*sharpness; SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND)); hue = y * 3 + offsetX; - intensity = bri = sin8(y * SEGMENT.intensity/2 + offsetY); + intensity = bri = sin8_t(y * SEGMENT.intensity/2 + offsetY); for (int i=0; i>= 8*sharpness; SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND)); @@ -5800,26 +5751,26 @@ static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,S uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek) if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; uint32_t tb = strip.now >> 12; // every ~4s if (tb > SEGENV.step) { int dir = ++SEGENV.aux0; - dir += (int)random8(3)-1; + dir += (int)hw_random8(3)-1; if (dir > 7) SEGENV.aux0 = 0; else if (dir < 0) SEGENV.aux0 = 7; else SEGENV.aux0 = dir; - SEGENV.step = tb + random8(4); + SEGENV.step = tb + hw_random8(4); } SEGMENT.fadeToBlackBy(map(SEGMENT.speed, 0, 255, 248, 16)); SEGMENT.move(SEGENV.aux0, 1); for (size_t i = 0; i < 8; i++) { - int x = beatsin8(12 + i, 2, cols - 3); - int y = beatsin8(15 + i, 2, rows - 3); - CRGB color = ColorFromPalette(SEGPALETTE, beatsin8(12 + i, 0, 255), 255); + int x = beatsin8_t(12 + i, 2, cols - 3); + int y = beatsin8_t(15 + i, 2, rows - 3); + uint32_t color = ColorFromPalette(SEGPALETTE, beatsin8_t(12 + i, 0, 255), 255); SEGMENT.addPixelColorXY(x, y, color); if (cols > 24 || rows > 24) { SEGMENT.addPixelColorXY(x+1, y, color); @@ -5828,23 +5779,23 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht SEGMENT.addPixelColorXY(x, y-1, color); } } - SEGMENT.blur(SEGMENT.intensity>>3); + SEGMENT.blur(SEGMENT.intensity >> 3, SEGMENT.check1); return FRAMETIME; } -static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2"; +static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur,,,,Smear;;!;2"; ///////////////////////// // 2D Crazy Bees // ///////////////////////// -//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek) +//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek), improved by @dedehai #define MAX_BEES 5 uint16_t mode_2Dcrazybees(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1); @@ -5878,14 +5829,14 @@ uint16_t mode_2Dcrazybees(void) { if (strip.now > SEGENV.step) { SEGENV.step = strip.now + (FRAMETIME * 16 / ((SEGMENT.speed>>4)+1)); - - SEGMENT.fadeToBlackBy(32); - + SEGMENT.fadeToBlackBy(32 + ((SEGMENT.check1*SEGMENT.intensity) / 25)); + SEGMENT.blur(SEGMENT.intensity / (2 + SEGMENT.check1 * 9), SEGMENT.check1); for (size_t i = 0; i < n; i++) { - SEGMENT.addPixelColorXY(bee[i].aimX + 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255)); - SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY + 1, CHSV(bee[i].hue, 255, 255)); - SEGMENT.addPixelColorXY(bee[i].aimX - 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255)); - SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, CHSV(bee[i].hue, 255, 255)); + uint32_t flowerCcolor = SEGMENT.color_from_palette(bee[i].hue, false, true, 255); + SEGMENT.addPixelColorXY(bee[i].aimX + 1, bee[i].aimY, flowerCcolor); + SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY + 1, flowerCcolor); + SEGMENT.addPixelColorXY(bee[i].aimX - 1, bee[i].aimY, flowerCcolor); + SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, flowerCcolor); if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) { SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255))); int error2 = bee[i].error * 2; @@ -5901,13 +5852,13 @@ uint16_t mode_2Dcrazybees(void) { bee[i].aimed(cols, rows); } } - SEGMENT.blur(SEGMENT.intensity>>4); } return FRAMETIME; } -static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; +static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur,,,,Smear;;!;2;pal=11,ix=0"; #undef MAX_BEES + ///////////////////////// // 2D Ghost Rider // ///////////////////////// @@ -5916,8 +5867,8 @@ static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; uint16_t mode_2Dghostrider(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; typedef struct Lighter { int16_t gPosX; @@ -5940,9 +5891,8 @@ uint16_t mode_2Dghostrider(void) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; SEGENV.aux1 = rows; - //random16_set_seed(strip.now); - lighter->angleSpeed = random8(0,20) - 10; - lighter->gAngle = random16(); + lighter->angleSpeed = hw_random8(0,20) - 10; + lighter->gAngle = hw_random16(); lighter->Vspeed = 5; lighter->gPosX = (cols/2) * 10; lighter->gPosY = (rows/2) * 10; @@ -5970,7 +5920,7 @@ uint16_t mode_2Dghostrider(void) { if (lighter->gPosY < 0) lighter->gPosY = (rows - 1) * 10; if (lighter->gPosY > (rows - 1) * 10) lighter->gPosY = 0; for (size_t i = 0; i < maxLighters; i++) { - lighter->time[i] += random8(5, 20); + lighter->time[i] += hw_random8(5, 20); if (lighter->time[i] >= 255 || (lighter->lightersPosX[i] <= 0) || (lighter->lightersPosX[i] >= (cols - 1) * 10) || @@ -5981,7 +5931,7 @@ uint16_t mode_2Dghostrider(void) { if (lighter->reg[i]) { lighter->lightersPosY[i] = lighter->gPosY; lighter->lightersPosX[i] = lighter->gPosX; - lighter->Angle[i] = lighter->gAngle + ((int)random8(20) - 10); + lighter->Angle[i] = lighter->gAngle + ((int)hw_random8(20) - 10); lighter->time[i] = 0; lighter->reg[i] = false; } else { @@ -6006,8 +5956,8 @@ static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate, uint16_t mode_2Dfloatingblobs(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; typedef struct Blob { float x[MAX_BLOBS], y[MAX_BLOBS]; @@ -6027,12 +5977,12 @@ uint16_t mode_2Dfloatingblobs(void) { SEGENV.aux1 = rows; //SEGMENT.fill(BLACK); for (size_t i = 0; i < MAX_BLOBS; i++) { - blob->r[i] = random8(1, cols>8 ? (cols/4) : 2); - blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x - blob->sY[i] = (float) random8(3, rows) / (float)(256 - SEGMENT.speed); // speed y - blob->x[i] = random8(0, cols-1); - blob->y[i] = random8(0, rows-1); - blob->color[i] = random8(); + blob->r[i] = hw_random8(1, cols>8 ? (cols/4) : 2); + blob->sX[i] = (float) hw_random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x + blob->sY[i] = (float) hw_random8(3, rows) / (float)(256 - SEGMENT.speed); // speed y + blob->x[i] = hw_random8(0, cols-1); + blob->y[i] = hw_random8(0, rows-1); + blob->color[i] = hw_random8(); blob->grow[i] = (blob->r[i] < 1.f); if (blob->sX[i] == 0) blob->sX[i] = 1; if (blob->sY[i] == 0) blob->sY[i] = 1; @@ -6071,19 +6021,19 @@ uint16_t mode_2Dfloatingblobs(void) { else blob->y[i] += blob->sY[i]; // bounce x if (blob->x[i] < 0.01f) { - blob->sX[i] = (float)random8(3, cols) / (256 - SEGMENT.speed); + blob->sX[i] = (float)hw_random8(3, cols) / (256 - SEGMENT.speed); blob->x[i] = 0.01f; } else if (blob->x[i] > (float)cols - 1.01f) { - blob->sX[i] = (float)random8(3, cols) / (256 - SEGMENT.speed); + blob->sX[i] = (float)hw_random8(3, cols) / (256 - SEGMENT.speed); blob->sX[i] = -blob->sX[i]; blob->x[i] = (float)cols - 1.01f; } // bounce y if (blob->y[i] < 0.01f) { - blob->sY[i] = (float)random8(3, rows) / (256 - SEGMENT.speed); + blob->sY[i] = (float)hw_random8(3, rows) / (256 - SEGMENT.speed); blob->y[i] = 0.01f; } else if (blob->y[i] > (float)rows - 1.01f) { - blob->sY[i] = (float)random8(3, rows) / (256 - SEGMENT.speed); + blob->sY[i] = (float)hw_random8(3, rows) / (256 - SEGMENT.speed); blob->sY[i] = -blob->sY[i]; blob->y[i] = (float)rows - 1.01f; } @@ -6104,8 +6054,8 @@ static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail; uint16_t mode_2Dscrollingtext(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned letterWidth, rotLW; unsigned letterHeight, rotLH; @@ -6144,13 +6094,23 @@ uint16_t mode_2Dscrollingtext(void) { if (!strlen(text)) { // fallback if empty segment name: display date and time sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); } else { + if (text[0] == '#') for (auto &c : text) c = std::toupper(c); if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime)); else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime)); else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime)); - else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), AmPmHour); - else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), minute(localTime)); + else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf (text, zero? ("%02d") : ("%d"), AmPmHour); + else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf (text, zero? ("%02d") : ("%d"), minute(localTime)); + else if (!strncmp_P(text,PSTR("#SS"),3)) sprintf (text, ("%02d") , second(localTime)); + else if (!strncmp_P(text,PSTR("#DD"),3)) sprintf (text, zero? ("%02d") : ("%d"), day(localTime)); + else if (!strncmp_P(text,PSTR("#DAY"),4)) sprintf (text, ("%s") , dayShortStr(day(localTime))); + else if (!strncmp_P(text,PSTR("#DDDD"),5)) sprintf (text, ("%s") , dayStr(day(localTime))); + else if (!strncmp_P(text,PSTR("#MO"),3)) sprintf (text, zero? ("%02d") : ("%d"), month(localTime)); + else if (!strncmp_P(text,PSTR("#MON"),4)) sprintf (text, ("%s") , monthShortStr(month(localTime))); + else if (!strncmp_P(text,PSTR("#MMMM"),5)) sprintf (text, ("%s") , monthStr(month(localTime))); + else if (!strncmp_P(text,PSTR("#YY"),3)) sprintf (text, ("%02d") , year(localTime)%100); + else if (!strncmp_P(text,PSTR("#YYYY"),5)) sprintf_P(text, zero?PSTR("%04d") : ("%d"), year(localTime)); } const int numberOfLetters = strlen(text); @@ -6180,17 +6140,21 @@ uint16_t mode_2Dscrollingtext(void) { } if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail + bool usePaletteGradient = false; + uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); + uint32_t col2 = BLACK; + if (SEGMENT.check1) { // use gradient + if(SEGMENT.palette == 0) { // use colors for gradient + col1 = SEGCOLOR(0); + col2 = SEGCOLOR(2); + } + else usePaletteGradient = true; + } for (int i = 0; i < numberOfLetters; i++) { int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i; if (xoffset + rotLW < 0) continue; // don't draw characters off-screen - uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); - uint32_t col2 = BLACK; - if (SEGMENT.check1 && SEGMENT.palette == 0) { - col1 = SEGCOLOR(0); - col2 = SEGCOLOR(2); - } - SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, map(SEGMENT.custom3, 0, 31, -2, 2)); + SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, map(SEGMENT.custom3, 0, 31, -2, 2), usePaletteGradient); } return FRAMETIME; @@ -6201,12 +6165,12 @@ static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Off //////////////////////////// // 2D Drift Rose // //////////////////////////// -//// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek) +//// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek) improved by @dedehai uint16_t mode_2Ddriftrose(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const float CX = (cols-cols%2)/2.f - .5f; const float CY = (rows-rows%2)/2.f - .5f; @@ -6215,15 +6179,16 @@ uint16_t mode_2Ddriftrose(void) { SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3)); for (size_t i = 1; i < 37; i++) { float angle = radians(i * 10); - uint32_t x = (CX + (sin_t(angle) * (beatsin8(i, 0, L*2)-L))) * 255.f; - uint32_t y = (CY + (cos_t(angle) * (beatsin8(i, 0, L*2)-L))) * 255.f; - SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255)); + uint32_t x = (CX + (sin_t(angle) * (beatsin8_t(i, 0, L*2)-L))) * 255.f; + uint32_t y = (CY + (cos_t(angle) * (beatsin8_t(i, 0, L*2)-L))) * 255.f; + if(SEGMENT.palette == 0) SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255)); + else SEGMENT.wu_pixel(x, y, ColorFromPalette(SEGPALETTE, i * 10)); } - SEGMENT.blur(SEGMENT.intensity>>4); + SEGMENT.blur(SEGMENT.intensity >> 4, SEGMENT.check1); return FRAMETIME; } -static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;2"; +static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur,,,,Smear;;!;2;pal=11"; ///////////////////////////// // 2D PLASMA ROTOZOOMER // @@ -6232,8 +6197,8 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;; uint16_t mode_2Dplasmarotozoom() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned dataSize = SEGMENT.length() + sizeof(float); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -6265,6 +6230,7 @@ uint16_t mode_2Dplasmarotozoom() { } } *a -= 0.03f + float(SEGENV.speed-128)*0.0002f; // rotation speed + if(*a < -6283.18530718f) *a += 6283.18530718f; // 1000*2*PI, protect sin/cos from very large input float values (will give wrong results) return FRAMETIME; } @@ -6278,52 +6244,12 @@ static const char _data_FX_MODE_2DPLASMAROTOZOOM[] PROGMEM = "Rotozoomer@!,Scale /////////////////////////////////////////////////////////////////////////////// -/* use the following code to pass AudioReactive usermod variables to effect - - uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment - bool samplePeak = false; - float FFT_MajorPeak = 1.0; - uint8_t *fftResult = nullptr; - float *fftBin = nullptr; - um_data_t *um_data; - if (UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { - volumeSmth = *(float*) um_data->u_data[0]; - volumeRaw = *(float*) um_data->u_data[1]; - fftResult = (uint8_t*) um_data->u_data[2]; - samplePeak = *(uint8_t*) um_data->u_data[3]; - FFT_MajorPeak = *(float*) um_data->u_data[4]; - my_magnitude = *(float*) um_data->u_data[5]; - maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element - binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element - fftBin = (float*) um_data->u_data[8]; - } else { - // add support for no audio data - um_data = simulateSound(SEGMENT.soundSim); - } -*/ - - -// a few constants needed for AudioReactive effects - -// for 22Khz sampling -#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion) -#define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY) - -// for 20Khz sampling -//#define MAX_FREQUENCY 10240 -//#define MAX_FREQ_LOG10 4.0103f - -// for 10Khz sampling -//#define MAX_FREQUENCY 5120 -//#define MAX_FREQ_LOG10 3.71f - - ///////////////////////////////// // * Ripple Peak // ///////////////////////////////// uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline. // This currently has no controls. - #define maxsteps 16 // Case statement wouldn't allow a variable. + #define MAXSTEPS 16 // Case statement wouldn't allow a variable. unsigned maxRipples = 16; unsigned dataSize = sizeof(Ripple) * maxRipples; @@ -6341,7 +6267,6 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli // printUmData(); if (SEGENV.call == 0) { - SEGENV.aux0 = 255; SEGMENT.custom1 = *binNum; SEGMENT.custom2 = *maxVol * 2; } @@ -6360,29 +6285,29 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli break; case 255: // Initialize ripple variables. - ripples[i].pos = random16(SEGLEN); + ripples[i].pos = hw_random16(SEGLEN); #ifdef ESP32 if (FFT_MajorPeak > 1) // log10(0) is "forbidden" (throws exception) ripples[i].color = (int)(log10f(FFT_MajorPeak)*128); else ripples[i].color = 0; #else - ripples[i].color = random8(); + ripples[i].color = hw_random8(); #endif ripples[i].state = 0; break; case 0: - SEGMENT.setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0)); + SEGMENT.setPixelColor(ripples[i].pos, SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0)); ripples[i].state++; break; - case maxsteps: // At the end of the ripples. 254 is an inactive mode. + case MAXSTEPS: // At the end of the ripples. 254 is an inactive mode. ripples[i].state = 254; break; default: // Middle of the ripples. - SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); - SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), uint8_t(2*255/ripples[i].state))); + SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), uint8_t(2*255/ripples[i].state))); ripples[i].state++; // Next step. break; } // switch step @@ -6401,8 +6326,8 @@ static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Ma uint16_t mode_2DSwirl(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -6412,8 +6337,8 @@ uint16_t mode_2DSwirl(void) { SEGMENT.blur(SEGMENT.custom1); - int i = beatsin8( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth); - int j = beatsin8( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth); + int i = beatsin8_t( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth); + int j = beatsin8_t( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth); int ni = (cols - 1) - i; int nj = (cols - 1) - j; @@ -6440,8 +6365,8 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B uint16_t mode_2DWaverly(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; um_data_t *um_data = getAudioData(); float volumeSmth = *(float*) um_data->u_data[0]; @@ -6480,7 +6405,10 @@ typedef struct Gravity { /////////////////////// // * GRAVCENTER // /////////////////////// -uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. +// Gravcenter effects By Andrew Tuline. +// Gravcenter base function for Gravcenter (0), Gravcentric (1), Gravimeter (2), Gravfreq (3) (merged by @dedehai) + +uint16_t mode_gravcenter_base(unsigned mode) { if (SEGLEN == 1) return mode_static(); const unsigned dataSize = sizeof(gravity); @@ -6490,83 +6418,92 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. um_data_t *um_data = getAudioData(); float volumeSmth = *(float*) um_data->u_data[0]; - //SEGMENT.fade_out(240); - SEGMENT.fade_out(251); // 30% + if(mode == 1) SEGMENT.fade_out(253); // //Gravcentric + else if(mode == 2) SEGMENT.fade_out(249); // Gravimeter + else if(mode == 3) SEGMENT.fade_out(250); // Gravfreq + else SEGMENT.fade_out(251); // Gravcenter + float mySampleAvg; + int tempsamp; float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; - segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivity" upscaling - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0f); // map to pixels available in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. + if(mode == 2) { //Gravimeter + segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivity" upscaling + mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment + tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. + } + else { // Gravcenter or Gravcentric or Gravfreq + segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivity" upscaling + mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment + tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. + } + uint8_t gravity = 8 - SEGMENT.speed/32; - - for (int i=0; i= gravcen->topLED) gravcen->topLED = tempsamp-offset; + else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--; + + if(mode == 1) { //Gravcentric + for (int i=0; itopLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); + } } - - if (tempsamp >= gravcen->topLED) - gravcen->topLED = tempsamp-1; - else if (gravcen->gravityCounter % gravity == 0) - gravcen->topLED--; - - if (gravcen->topLED >= 0) { - SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0)); - SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0)); + else if(mode == 2) { //Gravimeter + for (int i=0; itopLED > 0) { + SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0)); + } } + else if(mode == 3) { //Gravfreq + for (int i=0; iu_data[4]; // used in mode 3: Gravfreq + if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; + uint8_t index = (log10f(FFT_MajorPeak) - (MAX_FREQ_LOG10 - 1.78f)) * 255; + SEGMENT.setPixelColor(i+SEGLEN/2, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(SEGLEN/2-i-1, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); + } + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); + } + } + else { //Gravcenter + for (int i=0; itopLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0)); + } + } gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; return FRAMETIME; -} // mode_gravcenter() -static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin +} +uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. + return mode_gravcenter_base(0); +} +static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin /////////////////////// // * GRAVCENTRIC // /////////////////////// -uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. - if (SEGLEN == 1) return mode_static(); - - unsigned dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - Gravity* gravcen = reinterpret_cast(SEGENV.data); - - um_data_t *um_data = getAudioData(); - float volumeSmth = *(float*) um_data->u_data[0]; - - // printUmData(); - - //SEGMENT.fade_out(240); - //SEGMENT.fade_out(240); // twice? really? - SEGMENT.fade_out(253); // 50% - - float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; - segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivity" upscaling - - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed/32; - - for (int i=0; i= gravcen->topLED) - gravcen->topLED = tempsamp-1; - else if (gravcen->gravityCounter % gravity == 0) - gravcen->topLED--; - - if (gravcen->topLED >= 0) { - SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); - SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); - } - gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; - - return FRAMETIME; -} // mode_gravcentric() +uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. + return mode_gravcenter_base(1); +} static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=3,si=0"; // Corner, Beatsin @@ -6574,45 +6511,20 @@ static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fal // * GRAVIMETER // /////////////////////// uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. - if (SEGLEN == 1) return mode_static(); - - unsigned dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - Gravity* gravcen = reinterpret_cast(SEGENV.data); - - um_data_t *um_data = getAudioData(); - float volumeSmth = *(float*) um_data->u_data[0]; - - //SEGMENT.fade_out(240); - SEGMENT.fade_out(249); // 25% - - float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; - segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivity" upscaling - - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed/32; - - for (int i=0; i= gravcen->topLED) - gravcen->topLED = tempsamp; - else if (gravcen->gravityCounter % gravity == 0) - gravcen->topLED--; - - if (gravcen->topLED > 0) { - SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0)); - } - gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; - - return FRAMETIME; -} // mode_gravimeter() + return mode_gravcenter_base(2); +} static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin +/////////////////////// +// ** Gravfreq // +/////////////////////// +uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. + return mode_gravcenter_base(3); +} +static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensitivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin + + ////////////////////// // * JUGGLES // ////////////////////// @@ -6621,11 +6533,11 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. float volumeSmth = *(float*) um_data->u_data[0]; SEGMENT.fade_out(224); // 6.25% - unsigned my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0); + uint8_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0); for (size_t i=0; iSEGLEN/2) maxLen = SEGLEN/2; - for (int i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { + for (unsigned i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { uint8_t index = inoise8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis. SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } - SEGENV.aux0=SEGENV.aux0+beatsin8(5,0,10); - SEGENV.aux1=SEGENV.aux1+beatsin8(4,0,10); + SEGENV.aux0=SEGENV.aux0+beatsin8_t(5,0,10); + SEGENV.aux1=SEGENV.aux1+beatsin8_t(4,0,10); return FRAMETIME; } // mode_midnoise() @@ -6708,13 +6628,12 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. if (SEGENV.call == 0) SEGMENT.fill(BLACK); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i*SEGMENT.speed/64,strip.now*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. - CRGB color = ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND); // Use the my own palette. - SEGMENT.setPixelColor(i, color); + SEGMENT.setPixelColor(i, ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND)); // Use my own palette. } return FRAMETIME; @@ -6736,17 +6655,17 @@ uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. SEGMENT.fade_out(fadeRate); float tmpSound2 = volumeRaw * 2.0 * (float)SEGMENT.intensity / 255.0; - int maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive. - if (maxLen <0) maxLen = 0; - if (maxLen >SEGLEN) maxLen = SEGLEN; + unsigned maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive. + if (maxLen < 0) maxLen = 0; + if (maxLen > SEGLEN) maxLen = SEGLEN; - for (int i=0; ithisphase) & 0xFF)/2; - thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. + thisbright += cos8_t(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. uint8_t colorIndex=thisbright; if (volumeSmth * SEGMENT.intensity / 64 < thisbright) {thisbright = 0;} @@ -6820,72 +6739,54 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;01v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin -/////////////////////// -// * PUDDLEPEAK // -/////////////////////// -// Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing. -uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. - if (SEGLEN == 1) return mode_static(); - +////////////////////// +// * PUDDLES // +////////////////////// +// Puddles/Puddlepeak By Andrew Tuline. Merged by @dedehai +uint16_t mode_puddles_base(bool peakdetect) { + if (SEGLEN <= 1) return mode_static(); unsigned size = 0; - uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254); - unsigned pos = random16(SEGLEN); // Set a random starting position. + uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254); + unsigned pos = hw_random16(SEGLEN); // Set a random starting position. + SEGMENT.fade_out(fadeVal); um_data_t *um_data = getAudioData(); + int volumeRaw = *(int16_t*)um_data->u_data[1]; uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; uint8_t *binNum = (uint8_t*)um_data->u_data[7]; float volumeSmth = *(float*) um_data->u_data[0]; - if (SEGENV.call == 0) { - SEGMENT.custom1 = *binNum; - SEGMENT.custom2 = *maxVol * 2; + if(peakdetect) { // puddles peak + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + if (samplePeak == 1) { + size = volumeSmth * SEGMENT.intensity /256 /4 + 1; // Determine size of the flash based on the volume. + if (pos+size>= SEGLEN) size = SEGLEN - pos; + } } - - *binNum = SEGMENT.custom1; // Select a bin. - *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. - - SEGMENT.fade_out(fadeVal); - - if (samplePeak == 1) { - size = volumeSmth * SEGMENT.intensity /256 /4 + 1; // Determine size of the flash based on the volume. - if (pos+size>= SEGLEN) size = SEGLEN - pos; + else { // puddles + if (volumeRaw > 1) { + size = volumeRaw * SEGMENT.intensity /256 /8 + 1; // Determine size of the flash based on the volume. + if (pos+size >= SEGLEN) size = SEGLEN - pos; + } } - - for (unsigned i=0; iu_data[1]; - - if (volumeRaw > 1) { - size = volumeRaw * SEGMENT.intensity /256 /8 + 1; // Determine size of the flash based on the volume. - if (pos+size >= SEGLEN) size = SEGLEN - pos; - } - + for (unsigned i=0; i SPEED_FORMULA_L) { - unsigned segLoc = random16(SEGLEN); - SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); + unsigned segLoc = hw_random16(SEGLEN); + SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, (int)SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), uint8_t(2*fftResult[SEGENV.aux0%16]))); ++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16 SEGENV.step = 1; - SEGMENT.blur(SEGMENT.intensity); + SEGMENT.blur(SEGMENT.intensity); // note: blur > 210 results in a alternating pattern, this could be fixed by mapping but some may like it (very old bug) } return FRAMETIME; @@ -6991,7 +6886,7 @@ static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;01f;m12=2, // ** Freqmap // //////////////////// uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate. - if (SEGLEN == 1) return mode_static(); + if (SEGLEN <= 1) return mode_static(); // Start frequency = 60 Hz and log10(60) = 1.78 // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10 @@ -7007,11 +6902,11 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. if (locn < 1) locn = 0; // avoid underflow - if (locn >=SEGLEN) locn = SEGLEN-1; + if (locn >= (int)SEGLEN) locn = SEGLEN-1; unsigned pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow - unsigned bright = (int)my_magnitude; + uint8_t bright = (uint8_t)my_magnitude; SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); @@ -7097,8 +6992,8 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow for (int i=0; i < SEGMENT.intensity/32+1; i++) { - unsigned locn = random16(0,SEGLEN); - SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + unsigned locn = hw_random16(0,SEGLEN); + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude)); } return FRAMETIME; @@ -7160,8 +7055,8 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun // shift the pixels one pixel outwards // if SEGLEN equals 1 these loops won't execute - for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left - for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right + for (unsigned i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (unsigned i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; @@ -7169,54 +7064,6 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;01f;m12=2,si=0"; // Circle, Beatsin -/////////////////////// -// ** Gravfreq // -/////////////////////// -uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. - if (SEGLEN == 1) return mode_static(); - unsigned dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - Gravity* gravcen = reinterpret_cast(SEGENV.data); - - um_data_t *um_data = getAudioData(); - float FFT_MajorPeak = *(float*)um_data->u_data[4]; - float volumeSmth = *(float*)um_data->u_data[0]; - if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) - - SEGMENT.fade_out(250); - - float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; - segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivity" upscaling - - float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0f); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed/32; - - for (int i=0; i= gravcen->topLED) - gravcen->topLED = tempsamp-1; - else if (gravcen->gravityCounter % gravity == 0) - gravcen->topLED--; - - if (gravcen->topLED >= 0) { - SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); - SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); - } - gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; - - return FRAMETIME; -} // mode_gravfreq() -static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensitivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin - - ////////////////////// // ** Noisemove // ////////////////////// @@ -7232,12 +7079,12 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli unsigned locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise. // if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over. - SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4)); + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), uint8_t(fftResult[i % 16]*4))); } return FRAMETIME; } // mode_noisemove() -static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin +static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Move speed,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -7266,7 +7113,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac frTemp -= 132.0f; // This should give us a base musical note of C3 frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255; - unsigned i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); + unsigned i = map(beatsin8_t(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); i = constrain(i, 0U, SEGLEN-1U); SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp)); @@ -7281,8 +7128,11 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;01f;m12= // Combines peak detection with FFT_MajorPeak and FFT_Magnitude. uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline // effect can work on single pixels, we just lose the shifting effect - - um_data_t *um_data = getAudioData(); + unsigned dataSize = sizeof(uint32_t) * SEGLEN; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t* pixels = reinterpret_cast(SEGENV.data); + + um_data_t *um_data = getAudioData(); uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; float FFT_MajorPeak = *(float*) um_data->u_data[4]; uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; @@ -7292,7 +7142,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); + for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer SEGENV.aux0 = 255; SEGMENT.custom1 = *binNum; SEGMENT.custom2 = *maxVol * 2; @@ -7309,13 +7159,18 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin uint8_t pixCol = (log10f(FFT_MajorPeak) - 2.26f) * 150; // 22Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.967 (9260hz). Let's scale accordingly. if (FFT_MajorPeak < 182.0f) pixCol = 0; // handle underflow + unsigned k = SEGLEN-1; if (samplePeak) { - SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92)); + pixels[k] = (uint32_t)CRGB(CHSV(92,92,92)); } else { - SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude); } + SEGMENT.setPixelColor(k, pixels[k]); // loop will not execute if SEGLEN equals 1 - for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left + for (unsigned i = 0; i < k; i++) { + pixels[i] = pixels[i+1]; // shift left + SEGMENT.setPixelColor(i, pixels[i]); + } } return FRAMETIME; @@ -7331,8 +7186,8 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed uint16_t *previousBarHeight = reinterpret_cast(SEGENV.data); //array of previous bar heights per frequency band @@ -7384,8 +7239,8 @@ static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; int NUMB_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); int barWidth = (cols / NUMB_BANDS); @@ -7472,8 +7327,8 @@ static uint8_t akemi[] PROGMEM = { uint16_t mode_2DAkemi(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; @@ -7521,7 +7376,7 @@ uint16_t mode_2DAkemi(void) { unsigned band = map(x, 0, max(xMax,4), 0, 15); // map 0..cols/8 to 16 GEQ bands band = constrain(band, 0, 15); int barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); - CRGB color = CRGB(SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0)); + uint32_t color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); for (int y=0; y < barHeight; y++) { SEGMENT.setPixelColorXY(x, rows/2-y, color); @@ -7541,8 +7396,8 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea uint16_t mode_2Ddistortionwaves() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; uint8_t speed = SEGMENT.speed/32; uint8_t scale = SEGMENT.intensity/32; @@ -7553,12 +7408,12 @@ uint16_t mode_2Ddistortionwaves() { unsigned a2 = a/2; unsigned a3 = a/3; - unsigned cx = beatsin8(10-speed,0,cols-1)*scale; - unsigned cy = beatsin8(12-speed,0,rows-1)*scale; - unsigned cx1 = beatsin8(13-speed,0,cols-1)*scale; - unsigned cy1 = beatsin8(15-speed,0,rows-1)*scale; - unsigned cx2 = beatsin8(17-speed,0,cols-1)*scale; - unsigned cy2 = beatsin8(14-speed,0,rows-1)*scale; + unsigned cx = beatsin8_t(10-speed,0,cols-1)*scale; + unsigned cy = beatsin8_t(12-speed,0,rows-1)*scale; + unsigned cx1 = beatsin8_t(13-speed,0,cols-1)*scale; + unsigned cy1 = beatsin8_t(15-speed,0,rows-1)*scale; + unsigned cx2 = beatsin8_t(17-speed,0,cols-1)*scale; + unsigned cy2 = beatsin8_t(14-speed,0,rows-1)*scale; unsigned xoffs = 0; for (int x = 0; x < cols; x++) { @@ -7568,17 +7423,17 @@ uint16_t mode_2Ddistortionwaves() { for (int y = 0; y < rows; y++) { yoffs += scale; - byte rdistort = cos8((cos8(((x<<3)+a )&255)+cos8(((y<<3)-a2)&255)+a3 )&255)>>1; - byte gdistort = cos8((cos8(((x<<3)-a2)&255)+cos8(((y<<3)+a3)&255)+a+32 )&255)>>1; - byte bdistort = cos8((cos8(((x<<3)+a3)&255)+cos8(((y<<3)-a) &255)+a2+64)&255)>>1; + byte rdistort = cos8_t((cos8_t(((x<<3)+a )&255)+cos8_t(((y<<3)-a2)&255)+a3 )&255)>>1; + byte gdistort = cos8_t((cos8_t(((x<<3)-a2)&255)+cos8_t(((y<<3)+a3)&255)+a+32 )&255)>>1; + byte bdistort = cos8_t((cos8_t(((x<<3)+a3)&255)+cos8_t(((y<<3)-a) &255)+a2+64)&255)>>1; byte valueR = rdistort+ w* (a- ( ((xoffs - cx) * (xoffs - cx) + (yoffs - cy) * (yoffs - cy))>>7 )); byte valueG = gdistort+ w* (a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 )); byte valueB = bdistort+ w* (a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 )); - valueR = gamma8(cos8(valueR)); - valueG = gamma8(cos8(valueG)); - valueB = gamma8(cos8(valueB)); + valueR = gamma8(cos8_t(valueR)); + valueG = gamma8(cos8_t(valueG)); + valueB = gamma8(cos8_t(valueB)); SEGMENT.setPixelColorXY(x, y, RGBW32(valueR, valueG, valueB, 0)); } @@ -7596,8 +7451,8 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@ uint16_t mode_2Dsoap() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed @@ -7613,9 +7468,9 @@ uint16_t mode_2Dsoap() { // init if (SEGENV.call == 0) { - *noise32_x = random16(); - *noise32_y = random16(); - *noise32_z = random16(); + *noise32_x = hw_random(); + *noise32_y = hw_random(); + *noise32_z = hw_random(); } else { *noise32_x += mov; *noise32_y += mov; @@ -7698,7 +7553,7 @@ uint16_t mode_2Dsoap() { return FRAMETIME; } -static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; +static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2;pal=11"; //Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21 @@ -7708,8 +7563,8 @@ static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; uint16_t mode_2Doctopus() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const uint8_t mapp = 180 / MAX(cols,rows); typedef struct { @@ -7735,8 +7590,10 @@ uint16_t mode_2Doctopus() { const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255; for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { - rMap[XY(x, y)].angle = int(40.7436f * atan2f((y - C_Y), (x - C_X))); // avoid 128*atan2()/PI - rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu + int dx = (x - C_X); + int dy = (y - C_Y); + rMap[XY(x, y)].angle = int(40.7436f * atan2_t(dy, dx)); // avoid 128*atan2()/PI + rMap[XY(x, y)].radius = sqrtf(dx * dx + dy * dy) * mapp; //thanks Sutaburosu } } } @@ -7746,37 +7603,41 @@ uint16_t mode_2Doctopus() { for (int y = 0; y < rows; y++) { byte angle = rMap[XY(x,y)].angle; byte radius = rMap[XY(x,y)].radius; - //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1))); - unsigned intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); + //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8_t(sin8_t((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1))); + unsigned intensity = sin8_t(sin8_t((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); intensity = map((intensity*intensity) & 0xFFFF, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display - CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity); - SEGMENT.setPixelColorXY(x, y, c); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity)); } } return FRAMETIME; } -static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs;;!;2;"; +static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs,fasttan;;!;2;"; //Waving Cell //@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells) -// adapted for WLED by @blazoncek +// adapted for WLED by @blazoncek, improvements by @dedehai uint16_t mode_2Dwavingcell() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); - - uint32_t t = strip.now/(257-SEGMENT.speed); - uint8_t aX = SEGMENT.custom1/16 + 9; - uint8_t aY = SEGMENT.custom2/16 + 1; - uint8_t aZ = SEGMENT.custom3 + 1; - for (int x = 0; x < cols; x++) for (int y = 0; y >3; + uint32_t aX = SEGMENT.custom1/16 + 9; + uint32_t aY = SEGMENT.custom2/16 + 1; + uint32_t aZ = SEGMENT.custom3 + 1; + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + uint32_t wave = sin8_t((x * aX) + sin8_t((((y<<8) + t) * aY)>>8)) + cos8_t(y * aZ); // bit shifts to increase temporal resolution + uint8_t colorIndex = wave + (t>>(8-(SEGMENT.check2*3))); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, colorIndex)); + } + } + SEGMENT.blur(SEGMENT.intensity); return FRAMETIME; } -static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitude 1,Amplitude 2,Amplitude 3;;!;2"; +static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,Blur,Amplitude 1,Amplitude 2,Amplitude 3,,Flow;;!;2;ix=0"; #endif // WLED_DISABLE_2D @@ -7897,7 +7758,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_COLORTWINKLE, &mode_colortwinkle, _data_FX_MODE_COLORTWINKLE); addEffect(FX_MODE_LAKE, &mode_lake, _data_FX_MODE_LAKE); addEffect(FX_MODE_METEOR, &mode_meteor, _data_FX_MODE_METEOR); - addEffect(FX_MODE_METEOR_SMOOTH, &mode_meteor_smooth, _data_FX_MODE_METEOR_SMOOTH); + //addEffect(FX_MODE_METEOR_SMOOTH, &mode_meteor_smooth, _data_FX_MODE_METEOR_SMOOTH); // merged with mode_meteor addEffect(FX_MODE_RAILWAY, &mode_railway, _data_FX_MODE_RAILWAY); addEffect(FX_MODE_RIPPLE, &mode_ripple, _data_FX_MODE_RIPPLE); addEffect(FX_MODE_TWINKLEFOX, &mode_twinklefox, _data_FX_MODE_TWINKLEFOX); diff --git a/wled00/FX.h b/wled00/FX.h index 545161546..f54b3ba5c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -16,6 +16,7 @@ #include #include "const.h" +#include "bus_manager.h" #define FASTLED_INTERNAL //remove annoying pragma messages #define USE_GET_MILLISECOND_TIMER @@ -42,10 +43,21 @@ #define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) #endif +extern bool realtimeRespectLedMaps; // used in getMappedPixelIndex() +extern byte realtimeMode; // used in getMappedPixelIndex() + /* Not used in all effects yet */ #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) #define FRAMETIME strip.getFrameTime() +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) + #define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive +#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + #define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core +#else + #define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY +#endif +#define FPS_UNLIMITED 0 // FPS calculation (can be defined as compile flag for debugging) #ifndef FPS_CALC_AVG @@ -67,9 +79,9 @@ #define MAX_NUM_SEGMENTS 32 #endif #if defined(ARDUINO_ARCH_ESP32S2) - #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default (S2 is short on free RAM) + #define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*768) // 24k by default (S2 is short on free RAM) #else - #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default + #define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default #endif #endif @@ -77,16 +89,14 @@ assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ #define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments()) -#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) - #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT strip._segments[strip.getCurrSegmentId()] #define SEGENV strip._segments[strip.getCurrSegmentId()] -//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x]) -//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength() -#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */ +#define SEGCOLOR(x) Segment::getCurrentColor(x) #define SEGPALETTE Segment::getCurrentPalette() -#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */ +#define SEGLEN Segment::vLength() +#define SEG_W Segment::vWidth() +#define SEG_H Segment::vHeight() #define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) // some common colors @@ -198,7 +208,7 @@ #define FX_MODE_COLORTWINKLE 74 #define FX_MODE_LAKE 75 #define FX_MODE_METEOR 76 -#define FX_MODE_METEOR_SMOOTH 77 +//#define FX_MODE_METEOR_SMOOTH 77 // merged with meteor #define FX_MODE_RAILWAY 78 #define FX_MODE_RIPPLE 79 #define FX_MODE_TWINKLEFOX 80 @@ -363,6 +373,7 @@ typedef struct Segment { }; uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows + // note: two bytes of padding are added here char *name; // runtime data @@ -391,7 +402,7 @@ typedef struct Segment { uint32_t _stepT; uint32_t _callT; uint8_t *_dataT; - uint16_t _dataLenT; + unsigned _dataLenT; TemporarySegmentData() : _dataT(nullptr) // just in case... , _dataLenT(0) @@ -409,15 +420,20 @@ typedef struct Segment { uint8_t _reserved : 4; }; }; - uint16_t _dataLen; - static uint16_t _usedSegmentData; - - // perhaps this should be per segment, not static + uint8_t _default_palette; // palette number that gets assigned to pal0 + unsigned _dataLen; + static unsigned _usedSegmentData; + static uint8_t _segBri; // brightness of segment for current effect + static unsigned _vLength; // 1D dimension used for current effect + static unsigned _vWidth, _vHeight; // 2D dimensions used for current effect + static uint32_t _currentColors[NUM_COLORS]; // colors used for current effect + static bool _colorScaled; // color has been scaled prior to setPixelColor() call static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) static CRGBPalette16 _randomPalette; // actual random palette static CRGBPalette16 _newRandomPalette; // target random palette static uint16_t _lastPaletteChange; // last random palette change time in millis()/1000 static uint16_t _lastPaletteBlend; // blend palette according to set Transition Delay in millis()%0xFFFF + static uint16_t _transitionprogress; // current transition progress 0 - 0xFFFF #ifndef WLED_DISABLE_MODE_BLEND static bool _modeBlend; // mode/effect blending semaphore #endif @@ -444,6 +460,8 @@ typedef struct Segment { {} } *_t; + [[gnu::hot]] void _setPixelColorXY_raw(const int& x, const int& y, uint32_t& col) const; // set pixel without mapping (internal use only) + public: Segment(uint16_t sStart=0, uint16_t sStop=30) : @@ -476,6 +494,7 @@ typedef struct Segment { aux1(0), data(nullptr), _capabilities(0), + _default_palette(0), _dataLen(0), _t(nullptr) { @@ -499,7 +518,7 @@ typedef struct Segment { //if (data) Serial.printf(" %d->(%p)", (int)_dataLen, data); //Serial.println(); #endif - if (name) { delete[] name; name = nullptr; } + if (name) { free(name); name = nullptr; } stopTransition(); deallocateData(); } @@ -515,7 +534,6 @@ typedef struct Segment { inline bool isSelected() const { return selected; } inline bool isInTransition() const { return _t != nullptr; } inline bool isActive() const { return stop > start; } - inline bool is2D() const { return (width()>1 && height()>1); } inline bool hasRGB() const { return _isRGB; } inline bool hasWhite() const { return _hasW; } inline bool isCCT() const { return _isCCT; } @@ -524,23 +542,30 @@ typedef struct Segment { inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t groupLength() const { return grouping + spacing; } inline uint8_t getLightCapabilities() const { return _capabilities; } + inline void deactivate() { setGeometry(0,0); } - inline static uint16_t getUsedSegmentData() { return _usedSegmentData; } - inline static void addUsedSegmentData(int len) { _usedSegmentData += len; } + inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; } + inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; } #ifndef WLED_DISABLE_MODE_BLEND - inline static void modeBlend(bool blend) { _modeBlend = blend; } + inline static void modeBlend(bool blend) { _modeBlend = blend; } #endif - static void handleRandomPalette(); + inline static unsigned vLength() { return Segment::_vLength; } + inline static unsigned vWidth() { return Segment::_vWidth; } + inline static unsigned vHeight() { return Segment::_vHeight; } + inline static uint32_t getCurrentColor(unsigned i) { return Segment::_currentColors[i]; } // { return i < 3 ? Segment::_currentColors[i] : 0; } inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; } + inline static uint8_t getCurrentBrightness() { return Segment::_segBri; } + static void handleRandomPalette(); - void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); + void beginDraw(); // set up parameters for current effect + void setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t m12=0); Segment &setColor(uint8_t slot, uint32_t c); Segment &setCCT(uint16_t k); Segment &setOpacity(uint8_t o); Segment &setOption(uint8_t n, bool val); Segment &setMode(uint8_t fx, bool loadDefaults = false); Segment &setPalette(uint8_t pal); - uint8_t differs(Segment& b) const; + uint8_t differs(const Segment& b) const; void refreshLightCapabilities(); // runtime data functions @@ -559,28 +584,28 @@ typedef struct Segment { // transition functions void startTransition(uint16_t dur); // transition has to start before actual segment values change void stopTransition(); // ends transition mode by destroying transition structure (does nothing if not in transition) - inline void handleTransition() { if (progress() == 0xFFFFU) stopTransition(); } + inline void handleTransition() { updateTransitionProgress(); if (progress() == 0xFFFFU) stopTransition(); } #ifndef WLED_DISABLE_MODE_BLEND void swapSegenv(tmpsegd_t &tmpSegD); // copies segment data into specifed buffer, if buffer is not a transition buffer, segment data is overwritten from transition buffer - void restoreSegenv(tmpsegd_t &tmpSegD); // restores segment data from buffer, if buffer is not transition buffer, changed values are copied to transition buffer + void restoreSegenv(const tmpsegd_t &tmpSegD); // restores segment data from buffer, if buffer is not transition buffer, changed values are copied to transition buffer #endif - [[gnu::hot]] uint16_t progress() const; // transition progression between 0-65535 + [[gnu::hot]] void updateTransitionProgress(); // set current progression of transition + inline uint16_t progress() const { return Segment::_transitionprogress; } // transition progression between 0-65535 [[gnu::hot]] uint8_t currentBri(bool useCct = false) const; // current segment brightness/CCT (blended while in transition) uint8_t currentMode() const; // currently active effect/mode (while in transition) [[gnu::hot]] uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition) CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); - void setCurrentPalette(); // 1D strip [[gnu::hot]] uint16_t virtualLength() const; - [[gnu::hot]] void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color - inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } - inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } - inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } + [[gnu::hot]] void setPixelColor(int i, uint32_t c) const; // set relative pixel within segment with color + inline void setPixelColor(unsigned n, uint32_t c) const { setPixelColor(int(n), c); } + inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) const { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) const { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } #ifdef WLED_USE_AA_PIXELS - 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, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } + void setPixelColor(float i, uint32_t c, bool aa = true) const; + inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) const { setPixelColor(i, RGBW32(r,g,b,w), aa); } + inline void setPixelColor(float i, CRGB c, bool aa = true) const { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } #endif [[gnu::hot]] uint32_t getPixelColor(int i) const; // 1D support functions (some implement 2D as well) @@ -590,21 +615,19 @@ typedef struct Segment { void fadeToBlackBy(uint8_t fadeBy); inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); } inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColor(int n, uint32_t color, bool fast = false) { setPixelColor(n, color_add(getPixelColor(n), color, fast)); } - 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); } - inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } + inline void addPixelColor(int n, uint32_t color, bool preserveCR = true) { setPixelColor(n, color_add(getPixelColor(n), color, preserveCR)); } + inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColor(n, RGBW32(r,g,b,w), preserveCR); } + inline void addPixelColor(int n, CRGB c, bool preserveCR = true) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), preserveCR); } inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); } [[gnu::hot]] uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const; [[gnu::hot]] uint32_t color_wheel(uint8_t pos) const; // 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); + blur2D(0, 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); + blur2D(blur_amount, 0, smear); } // 2D matrix @@ -618,46 +641,45 @@ typedef struct Segment { #endif } #ifndef WLED_DISABLE_2D - [[gnu::hot]] uint16_t XY(int x, int y); // support function to get relative index within segment - [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color - 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, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); } + inline bool is2D() const { return (width()>1 && height()>1); } + [[gnu::hot]] int XY(int x, int y) const; // support function to get relative index within segment + [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c) const; // set relative pixel within segment with color + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) const { setPixelColorXY(int(x), int(y), c); } + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) const { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } + inline void setPixelColorXY(int x, int y, CRGB c) const { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) const { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); } #ifdef WLED_USE_AA_PIXELS - void setPixelColorXY(float x, float y, uint32_t c, bool aa = true); - 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); } + void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) const; + inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) const { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } + inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) const { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } #endif [[gnu::hot]] uint32_t getPixelColorXY(int x, int y) const; // 2D support functions inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, 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 addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast)); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } - inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } - inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } - void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur - void blur2D(uint8_t blur_amount, bool smear = false); - void blurRow(uint32_t row, fract8 blur_amount, bool smear = false); - void blurCol(uint32_t col, fract8 blur_amount, bool smear = false); - void moveX(int8_t delta, bool wrap = false); - void moveY(int8_t delta, bool wrap = false); - void move(uint8_t dir, uint8_t delta, bool wrap = false); + inline void addPixelColorXY(int x, int y, uint32_t color, bool preserveCR = true) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, preserveCR)); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(r,g,b,w), preserveCR); } + inline void addPixelColorXY(int x, int y, CRGB c, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), preserveCR); } + inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } + //void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur + void blur2D(uint8_t blur_x, uint8_t blur_y, bool smear = false); + void moveX(int delta, bool wrap = false); + void moveY(int delta, bool wrap = false); + void move(unsigned dir, unsigned delta, bool wrap = 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); } void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); 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); } void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false); inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft); } // 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, int8_t rotate = 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, int8_t rotate = 0, bool usePalGrad = false); inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.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, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline + 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, bool usePalGrad = false) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate, usePalGrad); } // automatic inline void wu_pixel(uint32_t x, uint32_t y, CRGB c); - inline void blur2d(fract8 blur_amount) { blur(blur_amount); } inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } #else - inline uint16_t XY(uint16_t x, uint16_t y) { return x; } + inline constexpr bool is2D() const { return false; } + inline int XY(int x, int y) const { return x; } inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } @@ -671,16 +693,16 @@ typedef struct Segment { inline uint32_t getPixelColorXY(int x, int y) { return getPixelColor(x); } inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); } - inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } + inline void addPixelColorXY(int x, int y, uint32_t color, bool saturate = false) { addPixelColor(x, color, saturate); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColor(x, RGBW32(r,g,b,w), saturate); } + inline void addPixelColorXY(int x, int y, CRGB c, bool saturate = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), saturate); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } - inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} - inline void blur2D(uint8_t blur_amount, bool smear = false) {} - inline void blurRow(uint32_t row, fract8 blur_amount, bool smear = false) {} - inline void blurCol(uint32_t col, fract8 blur_amount, bool smear = false) {} - inline void moveX(int8_t delta, bool wrap = false) {} - inline void moveY(int8_t delta, bool wrap = false) {} + //inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} + inline void blur2D(uint8_t blur_x, uint8_t blur_y, bool smear = false) {} + inline void blurRow(int row, fract8 blur_amount, bool smear = false) {} + inline void blurCol(int col, fract8 blur_amount, bool smear = false) {} + inline void moveX(int delta, bool wrap = false) {} + inline void moveY(int delta, bool wrap = false) {} inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {} inline 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) {} @@ -688,9 +710,9 @@ typedef struct Segment { inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {} inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false) {} inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) {} - inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0, bool = false) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} - 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, bool usePalGrad = false) {} inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} #endif } segment; @@ -728,9 +750,6 @@ class WS2812FX { // 96 bytes #endif correctWB(false), cctFromRgb(false), - // semi-private (just obscured) used in effect functions through macros - _colors_t{0,0,0}, - _virtualSegmentLength(0), // true private variables _suspend(false), _length(DEFAULT_LED_COUNT), @@ -748,6 +767,7 @@ class WS2812FX { // 96 bytes customMappingTable(nullptr), customMappingSize(0), _lastShow(0), + _lastServiceShow(0), _segment_index(0), _mainSegment(0) { @@ -759,7 +779,7 @@ class WS2812FX { // 96 bytes } ~WS2812FX() { - if (customMappingTable) delete[] customMappingTable; + if (customMappingTable) free(customMappingTable); _mode.clear(); _modeData.clear(); _segments.clear(); @@ -777,29 +797,25 @@ class WS2812FX { // 96 bytes #endif finalizeInit(), // initialises strip components service(), // executes effect functions when due and calls strip.show() - setMode(uint8_t segid, uint8_t m), // sets effect/mode for given segment (high level API) - setColor(uint8_t slot, uint32_t c), // sets color (in slot) for given segment (high level API) setCCT(uint16_t k), // sets global CCT (either in relative 0-255 value or in K) setBrightness(uint8_t b, bool direct = false), // sets strip brightness setRange(uint16_t i, uint16_t i2, uint32_t col), // used for clock overlay purgeSegments(), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint) - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), - setMainSegmentId(uint8_t n), + setMainSegmentId(unsigned n = 0), resetSegments(), // marks all segments for reset makeAutoSegments(bool forceReset = false), // will create segments based on configured outputs fixInvalidSegments(), // fixes incorrect segment configuration - setPixelColor(unsigned n, uint32_t c), // paints absolute strip pixel with index n and color c + setPixelColor(unsigned i, uint32_t c) const, // paints absolute strip pixel with index n and color c show(), // initiates LED output - setTargetFps(uint8_t fps), + setTargetFps(unsigned fps), setupEffectData(); // add default effects to the list; defined in FX.cpp inline void resetTimebase() { timebase = 0UL - millis(); } inline void restartRuntime() { for (Segment &seg : _segments) { seg.markForReset().resetIfRequired(); } } inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); } - inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } - inline void setPixelColor(unsigned n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } - inline void setPixelColor(unsigned n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } - inline void fill(uint32_t c) { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) + inline void setPixelColor(unsigned n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) const { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(unsigned n, CRGB c) const { setPixelColor(n, c.red, c.green, c.blue); } + inline void fill(uint32_t c) const { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) inline void trigger() { _triggered = true; } // Forces the next frame to be computed on all active segments. inline void setShowCallback(show_callback cb) { _callback = cb; } inline void setTransition(uint16_t t) { _transitionDur = t; } // sets transition time (in ms) @@ -809,12 +825,12 @@ class WS2812FX { // 96 bytes bool paletteFade, - checkSegmentAlignment(), + checkSegmentAlignment() const, hasRGBWBus() const, hasCCTBus() const, - isUpdating() const, // return true if the strip is being sent pixel updates - deserializeMap(uint8_t n=0); + deserializeMap(unsigned n = 0); + inline bool isUpdating() const { return !BusManager::canAllShow(); } // return true if the strip is being sent pixel updates inline bool isServicing() const { return _isServicing; } // returns true if strip.service() is executing inline bool hasWhiteChannel() const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset) @@ -831,7 +847,7 @@ class WS2812FX { // 96 bytes addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp; inline uint8_t getBrightness() const { return _brightness; } // returns current strip brightness - inline uint8_t getMaxSegments() const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline static constexpr unsigned getMaxSegments() { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) inline uint8_t getSegmentsNum() const { return _segments.size(); } // returns currently present segments inline uint8_t getCurrSegmentId() const { return _segment_index; } // returns current segment index (only valid while strip.isServicing()) inline uint8_t getMainSegmentId() const { return _mainSegment; } // returns main segment index @@ -841,28 +857,27 @@ class WS2812FX { // 96 bytes uint16_t getLengthPhysical() const, - getLengthTotal() const, // will include virtual/nonexistent pixels in matrix - getFps() const, - getMappedPixelIndex(uint16_t index) const; + getLengthTotal() const; // will include virtual/nonexistent pixels in matrix + inline uint16_t getFps() const { return (millis() - _lastShow > 2000) ? 0 : (FPS_MULTIPLIER * _cumulativeFps) >> FPS_CALC_SHIFT; } // Returns the refresh rate of the LED strip (_cumulativeFps is stored in fixed point) inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms) - inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) + inline uint16_t getMinShowDelay() const { return MIN_FRAME_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H) inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms) + inline uint16_t getMappedPixelIndex(uint16_t index) const { // convert logical address to physical + if (index < customMappingSize && (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index]; + return index; + }; unsigned long now, timebase; - uint32_t getPixelColor(unsigned) const; + uint32_t getPixelColor(unsigned i) const; - inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call - inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } // returns currently valid color (for slot i) AKA SEGCOLOR(); may be blended between two colors while in transition + inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call - const char * - getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } + const char *getModeData(unsigned id = 0) const { return (id && id < _modeCount) ? _modeData[id] : PSTR("Solid"); } + inline const char **getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data - const char ** - getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data - - Segment& getSegment(uint8_t id); + Segment& getSegment(unsigned id); inline Segment& getFirstSelectedSeg() { return _segments[getFirstSelectedSegId()]; } // returns reference to first segment that is "selected" inline Segment& getMainSegment() { return _segments[getMainSegmentId()]; } // returns reference to main segment inline Segment* getSegments() { return &(_segments[0]); } // returns pointer to segment vector structure (warning: use carefully) @@ -904,11 +919,11 @@ class WS2812FX { // 96 bytes void setUpMatrix(); // sets up automatic matrix ledmap from panel configuration // outsmart the compiler :) by correctly overloading - inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor((unsigned)(y * Segment::maxWidth + x), 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, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColorXY(int x, int y, uint32_t c) const { setPixelColor((unsigned)(y * Segment::maxWidth + x), c); } + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) const { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } + inline void setPixelColorXY(int x, int y, CRGB c) const { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - inline uint32_t getPixelColorXY(int x, int y) const { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x); } + inline uint32_t getPixelColorXY(int x, int y) const { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x); } // end 2D support @@ -921,13 +936,8 @@ class WS2812FX { // 96 bytes bool cctFromRgb : 1; }; - // using public variables to reduce code size increase due to inline function getSegment() (with bounds checking) - // and color transitions - uint32_t _colors_t[3]; // color used for effect (includes transition) - uint16_t _virtualSegmentLength; - std::vector _segments; - friend class Segment; + friend struct Segment; private: volatile bool _suspend; @@ -958,6 +968,7 @@ class WS2812FX { // 96 bytes uint16_t customMappingSize; unsigned long _lastShow; + unsigned long _lastServiceShow; uint8_t _segment_index; uint8_t _mainSegment; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 7c1ae366b..0d38578a3 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -50,8 +50,8 @@ void WS2812FX::setUpMatrix() { customMappingSize = 0; // prevent use of mapping if anything goes wrong - if (customMappingTable) delete[] customMappingTable; - customMappingTable = new uint16_t[getLengthTotal()]; + if (customMappingTable) free(customMappingTable); + customMappingTable = static_cast(malloc(sizeof(uint16_t)*getLengthTotal())); if (customMappingTable) { customMappingSize = getLengthTotal(); @@ -68,7 +68,7 @@ void WS2812FX::setUpMatrix() { // content of the file is just raw JSON array in the form of [val1,val2,val3,...] // there are no other "key":"value" pairs in it // allowed values are: -1 (missing pixel/no LED attached), 0 (inactive/unused pixel), 1 (active/used pixel) - char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); // reduce flash footprint + char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); bool isFile = WLED_FS.exists(fileName); size_t gapSize = 0; int8_t *gapTable = nullptr; @@ -85,7 +85,7 @@ void WS2812FX::setUpMatrix() { JsonArray map = pDoc->as(); gapSize = map.size(); if (!map.isNull() && gapSize >= matrixSize) { // not an empty map - gapTable = new int8_t[gapSize]; + gapTable = static_cast(malloc(gapSize)); if (gapTable) for (size_t i = 0; i < gapSize; i++) { gapTable[i] = constrain(map[i], -1, 1); } @@ -113,7 +113,7 @@ void WS2812FX::setUpMatrix() { } // delete gap array as we no longer need it - if (gapTable) delete[] gapTable; + if (gapTable) free(gapTable); #ifdef WLED_DEBUG DEBUG_PRINT(F("Matrix ledmap:")); @@ -146,74 +146,82 @@ void WS2812FX::setUpMatrix() { #ifndef WLED_DISABLE_2D // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) -uint16_t IRAM_ATTR_YN Segment::XY(int x, int y) +int IRAM_ATTR_YN Segment::XY(int x, int y) const { - unsigned width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive) - unsigned height = virtualHeight(); // segment height in logical pixels (is always >= 1) - return isActive() ? (x%width) + (y%height) * width : 0; + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) + return isActive() ? (x%vW) + (y%vH) * vW : 0; } -void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) +// raw setColor function without checks (checks are done in setPixelColorXY()) +void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint32_t& col) const +{ + const int baseX = start + x; + const int baseY = startY + y; +#ifndef WLED_DISABLE_MODE_BLEND + // if blending modes, blend with underlying pixel + if (_modeBlend) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress()); +#endif + strip.setPixelColorXY(baseX, baseY, col); + + // Apply mirroring + if (mirror || mirror_y) { + auto setMirroredPixel = [&](int mx, int my) { + strip.setPixelColorXY(mx, my, col); + }; + + const int mirrorX = start + width() - x - 1; + const int mirrorY = startY + height() - y - 1; + + if (mirror) setMirroredPixel(transpose ? baseX : mirrorX, transpose ? mirrorY : baseY); + if (mirror_y) setMirroredPixel(transpose ? mirrorX : baseX, transpose ? baseY : mirrorY); + if (mirror && mirror_y) setMirroredPixel(mirrorX, mirrorY); + } +} + +void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) const { if (!isActive()) return; // not active - if ((unsigned)x >= virtualWidth() || (unsigned)y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - uint8_t _bri_t = currentBri(); - if (_bri_t < 255) { - col = color_fade(col, _bri_t); - } + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) + // negative values of x & y cast into unsigend will become very large values and will therefore be greater than vW/vH + if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return; // if pixel would fall out of virtual segment just exit - if (reverse ) x = virtualWidth() - x - 1; - if (reverse_y) y = virtualHeight() - y - 1; + // if color is unscaled + if (!_colorScaled) col = color_fade(col, _segBri); + + if (reverse ) x = vW - x - 1; + if (reverse_y) y = vH - y - 1; if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed + unsigned groupLen = groupLength(); - x *= groupLength(); // expand to physical pixels - y *= groupLength(); // expand to physical pixels - - int W = width(); - int H = height(); - if (x >= W || y >= H) return; // if pixel would fall out of segment just exit - - uint32_t tmpCol = col; - for (int j = 0; j < grouping; j++) { // groupping vertically - for (int g = 0; g < grouping; g++) { // groupping horizontally - int xX = (x+g), yY = (y+j); - if (xX >= W || yY >= H) continue; // we have reached one dimension's end - -#ifndef WLED_DISABLE_MODE_BLEND - // if blending modes, blend with underlying pixel - if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); -#endif - - strip.setPixelColorXY(start + xX, startY + yY, tmpCol); - - if (mirror) { //set the corresponding horizontally mirrored pixel - if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol); - else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol); - } - if (mirror_y) { //set the corresponding vertically mirrored pixel - if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol); - else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol); - } - if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel - strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, tmpCol); + if (groupLen > 1) { + int W = width(); + int H = height(); + x *= groupLen; // expand to physical pixels + y *= groupLen; // expand to physical pixels + const int maxY = std::min(y + grouping, H); + const int maxX = std::min(x + grouping, W); + for (int yY = y; yY < maxY; yY++) { + for (int xX = x; xX < maxX; xX++) { + _setPixelColorXY_raw(xX, yY, col); } } + } else { + _setPixelColorXY_raw(x, y, col); } } #ifdef WLED_USE_AA_PIXELS // anti-aliased version of setPixelColorXY() -void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) +void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) const { if (!isActive()) return; // not active if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); - - float fX = x * (cols-1); - float fY = y * (rows-1); + float fX = x * (vWidth()-1); + float fY = y * (vHeight()-1); if (aa) { unsigned xL = roundf(fX-0.49f); unsigned xR = roundf(fX+0.49f); @@ -251,9 +259,11 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (!isActive()) return 0; // not active - if ((unsigned)x >= virtualWidth() || (unsigned)y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit - if (reverse ) x = virtualWidth() - x - 1; - if (reverse_y) y = virtualHeight() - y - 1; + const int vW = vWidth(); + const int vH = vHeight(); + if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return 0; // if pixel would fall out of virtual segment just exit + if (reverse ) x = vW - x - 1; + if (reverse_y) y = vH - y - 1; if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed x *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels @@ -261,128 +271,69 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { return strip.getPixelColorXY(start + x, startY + y); } -// blurRow: perform a blur on a row of a rectangular matrix -void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ - if (!isActive() || blur_amount == 0) return; // not active - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); - - if (row >= rows) return; - // blur one row - uint8_t keep = smear ? 255 : 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - uint32_t carryover = BLACK; - uint32_t lastnew; +// 2D blurring, can be asymmetrical +void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) { + if (!isActive()) return; // not active + const unsigned cols = vWidth(); + const unsigned rows = vHeight(); + uint32_t lastnew; // not necessary to initialize lastnew and last, as both will be initialized by the first loop iteration uint32_t last; - uint32_t curnew = BLACK; - for (unsigned x = 0; x < cols; x++) { - uint32_t cur = getPixelColorXY(x, row); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (x > 0) { - if (carryover) - curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); - if (last != prev) // optimization: only set pixel if color has changed - setPixelColorXY(x - 1, row, prev); - } else // first pixel - setPixelColorXY(x, row, curnew); - lastnew = curnew; - last = cur; // save original value for comparison on next iteration - carryover = part; - } - setPixelColorXY(cols-1, row, curnew); // set last pixel -} - -// blurCol: perform a blur on a column of a rectangular matrix -void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { - if (!isActive() || blur_amount == 0) return; // not active - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); - - if (col >= cols) return; - // blur one column - uint8_t keep = smear ? 255 : 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - uint32_t carryover = BLACK; - uint32_t lastnew; - uint32_t last; - uint32_t curnew = BLACK; - for (unsigned y = 0; y < rows; y++) { - uint32_t cur = getPixelColorXY(col, y); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (y > 0) { - if (carryover) - curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); - if (last != prev) // optimization: only set pixel if color has changed - setPixelColorXY(col, y - 1, prev); - } else // first pixel - setPixelColorXY(col, y, curnew); - lastnew = curnew; - last = cur; //save original value for comparison on next iteration - carryover = part; - } - setPixelColorXY(col, rows - 1, curnew); -} - -void Segment::blur2D(uint8_t blur_amount, bool smear) { - if (!isActive() || blur_amount == 0) return; // not active - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); - - const uint8_t keep = smear ? 255 : 255 - blur_amount; - const uint8_t seep = blur_amount >> (1 + smear); - uint32_t lastnew; - uint32_t last; - for (unsigned row = 0; row < rows; row++) { - uint32_t carryover = BLACK; - uint32_t curnew = BLACK; - for (unsigned x = 0; x < cols; x++) { - uint32_t cur = getPixelColorXY(x, row); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (x > 0) { - if (carryover) curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); - // optimization: only set pixel if color has changed - if (last != prev) setPixelColorXY(x - 1, row, prev); - } else setPixelColorXY(x, row, curnew); // first pixel - lastnew = curnew; - last = cur; // save original value for comparison on next iteration - carryover = part; + if (blur_x) { + const uint8_t keepx = smear ? 255 : 255 - blur_x; + const uint8_t seepx = blur_x >> 1; + for (unsigned row = 0; row < rows; row++) { // blur rows (x direction) + uint32_t carryover = BLACK; + uint32_t curnew = BLACK; + for (unsigned x = 0; x < cols; x++) { + uint32_t cur = getPixelColorXY(x, row); + uint32_t part = color_fade(cur, seepx); + curnew = color_fade(cur, keepx); + if (x > 0) { + if (carryover) curnew = color_add(curnew, carryover); + uint32_t prev = color_add(lastnew, part); + // optimization: only set pixel if color has changed + if (last != prev) setPixelColorXY(x - 1, row, prev); + } else setPixelColorXY(x, row, curnew); // first pixel + lastnew = curnew; + last = cur; // save original value for comparison on next iteration + carryover = part; + } + setPixelColorXY(cols-1, row, curnew); // set last pixel } - setPixelColorXY(cols-1, row, curnew); // set last pixel } - for (unsigned col = 0; col < cols; col++) { - uint32_t carryover = BLACK; - uint32_t curnew = BLACK; - for (unsigned y = 0; y < rows; y++) { - uint32_t cur = getPixelColorXY(col, y); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (y > 0) { - if (carryover) curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); - // optimization: only set pixel if color has changed - if (last != prev) setPixelColorXY(col, y - 1, prev); - } else setPixelColorXY(col, y, curnew); // first pixel - lastnew = curnew; - last = cur; //save original value for comparison on next iteration - carryover = part; + if (blur_y) { + const uint8_t keepy = smear ? 255 : 255 - blur_y; + const uint8_t seepy = blur_y >> 1; + for (unsigned col = 0; col < cols; col++) { + uint32_t carryover = BLACK; + uint32_t curnew = BLACK; + for (unsigned y = 0; y < rows; y++) { + uint32_t cur = getPixelColorXY(col, y); + uint32_t part = color_fade(cur, seepy); + curnew = color_fade(cur, keepy); + if (y > 0) { + if (carryover) curnew = color_add(curnew, carryover); + uint32_t prev = color_add(lastnew, part); + // optimization: only set pixel if color has changed + if (last != prev) setPixelColorXY(col, y - 1, prev); + } else setPixelColorXY(col, y, curnew); // first pixel + lastnew = curnew; + last = cur; //save original value for comparison on next iteration + carryover = part; + } + setPixelColorXY(col, rows - 1, curnew); } - setPixelColorXY(col, rows - 1, curnew); } } +/* // 2D Box blur void Segment::box_blur(unsigned radius, bool smear) { if (!isActive() || radius == 0) return; // not active if (radius > 3) radius = 3; const unsigned d = (1 + 2*radius) * (1 + 2*radius); // averaging divisor - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); + const unsigned cols = vWidth(); + const unsigned rows = vHeight(); uint16_t *tmpRSum = new uint16_t[cols*rows]; uint16_t *tmpGSum = new uint16_t[cols*rows]; uint16_t *tmpBSum = new uint16_t[cols*rows]; @@ -448,40 +399,56 @@ void Segment::box_blur(unsigned radius, bool smear) { delete[] tmpBSum; delete[] tmpWSum; } - -void Segment::moveX(int8_t delta, bool wrap) { - if (!isActive()) return; // not active - const int cols = virtualWidth(); - const int rows = virtualHeight(); - if (!delta || abs(delta) >= cols) return; - uint32_t newPxCol[cols]; - for (int y = 0; y < rows; y++) { - if (delta > 0) { - for (int x = 0; x < cols-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y); - for (int x = cols-delta; x < cols; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - cols : x, y); - } else { - for (int x = cols-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y); - for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + cols : x, y); +*/ +void Segment::moveX(int delta, bool wrap) { + if (!isActive() || !delta) return; // not active + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) + int absDelta = abs(delta); + if (absDelta >= vW) return; + uint32_t newPxCol[vW]; + int newDelta; + int stop = vW; + int start = 0; + if (wrap) newDelta = (delta + vW) % vW; // +cols in case delta < 0 + else { + if (delta < 0) start = absDelta; + stop = vW - absDelta; + newDelta = delta > 0 ? delta : 0; + } + for (int y = 0; y < vH; y++) { + for (int x = 0; x < stop; x++) { + int srcX = x + newDelta; + if (wrap) srcX %= vW; // Wrap using modulo when `wrap` is true + newPxCol[x] = getPixelColorXY(srcX, y); } - for (int x = 0; x < cols; x++) setPixelColorXY(x, y, newPxCol[x]); + for (int x = 0; x < stop; x++) setPixelColorXY(x + start, y, newPxCol[x]); } } -void Segment::moveY(int8_t delta, bool wrap) { - if (!isActive()) return; // not active - const int cols = virtualWidth(); - const int rows = virtualHeight(); - if (!delta || abs(delta) >= rows) return; - uint32_t newPxCol[rows]; - for (int x = 0; x < cols; x++) { - if (delta > 0) { - for (int y = 0; y < rows-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta)); - for (int y = rows-delta; y < rows; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - rows : y); - } else { - for (int y = rows-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta)); - for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + rows : y); +void Segment::moveY(int delta, bool wrap) { + if (!isActive() || !delta) return; // not active + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) + int absDelta = abs(delta); + if (absDelta >= vH) return; + uint32_t newPxCol[vH]; + int newDelta; + int stop = vH; + int start = 0; + if (wrap) newDelta = (delta + vH) % vH; // +rows in case delta < 0 + else { + if (delta < 0) start = absDelta; + stop = vH - absDelta; + newDelta = delta > 0 ? delta : 0; + } + for (int x = 0; x < vW; x++) { + for (int y = 0; y < stop; y++) { + int srcY = y + newDelta; + if (wrap) srcY %= vH; // Wrap using modulo when `wrap` is true + newPxCol[y] = getPixelColorXY(x, srcY); } - for (int y = 0; y < rows; y++) setPixelColorXY(x, y, newPxCol[y]); + for (int y = 0; y < stop; y++) setPixelColorXY(x, y + start, newPxCol[y]); } } @@ -489,7 +456,7 @@ void Segment::moveY(int8_t delta, bool wrap) { // @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down // @param delta number of pixels to move // @param wrap around -void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { +void Segment::move(unsigned dir, unsigned delta, bool wrap) { if (delta==0) return; switch (dir) { case 0: moveX( delta, wrap); break; @@ -507,46 +474,49 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, if (!isActive() || radius == 0) return; // not active if (soft) { // Xiaolin Wu’s algorithm - int rsq = radius*radius; + const int rsq = radius*radius; int x = 0; int y = radius; unsigned oldFade = 0; while (x < y) { float yf = sqrtf(float(rsq - x*x)); // needs to be floating point - unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep + uint8_t fade = float(0xFF) * (ceilf(yf) - yf); // how much color to keep if (oldFade > fade) y--; oldFade = fade; - setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true)); - setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true)); - setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true)); - setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true)); - setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true)); - setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true)); - setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true)); - setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true)); - setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true)); - setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true)); - setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true)); - setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true)); - setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true)); - setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true)); - setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true)); - setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true)); + int px, py; + for (uint8_t i = 0; i < 16; i++) { + int swaps = (i & 0x4 ? 1 : 0); // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 + int adj = (i < 8) ? 0 : 1; // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 + int dx = (i & 1) ? -1 : 1; // 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 + int dy = (i & 2) ? -1 : 1; // 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1 + if (swaps) { + px = cx + (y - adj) * dx; + py = cy + x * dy; + } else { + px = cx + x * dx; + py = cy + (y - adj) * dy; + } + uint32_t pixCol = getPixelColorXY(px, py); + setPixelColorXY(px, py, adj ? + color_blend(pixCol, col, fade) : + color_blend(col, pixCol, fade)); + } x++; } } else { + // pre-scale color for all pixels + col = color_fade(col, _segBri); + _colorScaled = true; // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; while (y >= x) { - setPixelColorXY(cx+x, cy+y, col); - setPixelColorXY(cx-x, cy+y, col); - setPixelColorXY(cx+x, cy-y, col); - setPixelColorXY(cx-x, cy-y, col); - setPixelColorXY(cx+y, cy+x, col); - setPixelColorXY(cx-y, cy+x, col); - setPixelColorXY(cx+y, cy-x, col); - setPixelColorXY(cx-y, cy-x, col); + for (int i = 0; i < 4; i++) { + int dx = (i & 1) ? -x : x; + int dy = (i & 2) ? -y : y; + setPixelColorXY(cx + dx, cy + dy, col); + setPixelColorXY(cx + dy, cy + dx, col); + } x++; if (d > 0) { y--; @@ -555,33 +525,38 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, d += 4 * x + 6; } } + _colorScaled = false; } } // 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) { if (!isActive() || radius == 0) return; // not active + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) // draw soft bounding circle if (soft) drawCircle(cx, cy, radius, col, soft); + // pre-scale color for all pixels + col = color_fade(col, _segBri); + _colorScaled = true; // fill it - const int cols = virtualWidth(); - const int rows = virtualHeight(); for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { if (x * x + y * y <= radius * radius && - int(cx)+x>=0 && int(cy)+y>=0 && - int(cx)+x= 0 && int(cy)+y >= 0 && + int(cx)+x < vW && int(cy)+y < vH) setPixelColorXY(cx + x, cy + y, col); } } + _colorScaled = false; } //line function void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) { if (!isActive()) return; // not active - const int cols = virtualWidth(); - const int rows = virtualHeight(); - if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) + if (x0 >= vW || x1 >= vW || y0 >= vH || y1 >= vH) return; const int dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2; // error direction for (;;) { @@ -628,6 +606,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 if (e2 >-dx) { err -= dy; x0 += sx; } if (e2 < dy) { err += dx; y0 += sy; } } + _colorScaled = false; } } @@ -639,16 +618,15 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 // draws a raster font character on canvas // 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, int8_t rotate) { +void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate, bool usePalGrad) { if (!isActive()) return; // not active if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported chr -= 32; // align with font table entries - const int cols = virtualWidth(); - const int rows = virtualHeight(); const int font = w*h; CRGB col = CRGB(color); CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col); + if(usePalGrad) grad = SEGPALETTE; // selected palette as gradient //if (w<5 || w>6 || h!=8) return; for (int i = 0; i= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen + if (x0 < 0 || x0 >= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen if (((bits>>(j+(8-w))) & 0x01)) { // bit set - setPixelColorXY(x0, y0, col); + setPixelColorXY(x0, y0, c); } } + _colorScaled = false; } } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 4aa4f6d1d..8887688b0 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -66,15 +66,21 @@ static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTy /////////////////////////////////////////////////////////////////////////////// // Segment class implementation /////////////////////////////////////////////////////////////////////////////// -uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[] -uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; -uint16_t Segment::maxHeight = 1; - +unsigned Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[] +uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; +uint16_t Segment::maxHeight = 1; +unsigned Segment::_vLength = 0; +unsigned Segment::_vWidth = 0; +unsigned Segment::_vHeight = 0; +uint8_t Segment::_segBri = 0; +uint32_t Segment::_currentColors[NUM_COLORS] = {0,0,0}; +bool Segment::_colorScaled = false; CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black); CRGBPalette16 Segment::_randomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR); CRGBPalette16 Segment::_newRandomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR); uint16_t Segment::_lastPaletteChange = 0; // perhaps it should be per segment uint16_t Segment::_lastPaletteBlend = 0; //in millis (lowest 16 bits only) +uint16_t Segment::_transitionprogress = 0xFFFF; #ifndef WLED_DISABLE_MODE_BLEND bool Segment::_modeBlend = false; @@ -88,7 +94,7 @@ Segment::Segment(const Segment &orig) { name = nullptr; data = nullptr; _dataLen = 0; - if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } + if (orig.name) { name = static_cast(malloc(strlen(orig.name)+1)); if (name) strcpy(name, orig.name); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } } @@ -107,7 +113,7 @@ Segment& Segment::operator= (const Segment &orig) { //DEBUG_PRINTF_P(PSTR("-- Copying segment: %p -> %p\n"), &orig, this); if (this != &orig) { // clean destination - if (name) { delete[] name; name = nullptr; } + if (name) { free(name); name = nullptr; } stopTransition(); deallocateData(); // copy source @@ -116,7 +122,7 @@ Segment& Segment::operator= (const Segment &orig) { data = nullptr; _dataLen = 0; // copy source data - if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } + if (orig.name) { name = static_cast(malloc(strlen(orig.name)+1)); if (name) strcpy(name, orig.name); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } } return *this; @@ -126,7 +132,7 @@ Segment& Segment::operator= (const Segment &orig) { Segment& Segment::operator= (Segment &&orig) noexcept { //DEBUG_PRINTF_P(PSTR("-- Moving segment: %p -> %p\n"), &orig, this); if (this != &orig) { - if (name) { delete[] name; name = nullptr; } // free old name + if (name) { free(name); name = nullptr; } // free old name stopTransition(); deallocateData(); // free old runtime data memcpy((void*)this, (void*)&orig, sizeof(Segment)); @@ -195,24 +201,12 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip //default palette. Differs depending on effect - if (pal == 0) switch (mode) { - case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette - case FX_MODE_COLORWAVES : pal = 26; break; // landscape 33 - case FX_MODE_FILLNOISE8 : pal = 9; break; // ocean colors - case FX_MODE_NOISE16_1 : pal = 20; break; // Drywet - case FX_MODE_NOISE16_2 : pal = 43; break; // Blue cyan yellow - case FX_MODE_NOISE16_3 : pal = 35; break; // heat palette - case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33 - case FX_MODE_GLITTER : pal = 11; break; // rainbow colors - case FX_MODE_SUNRISE : pal = 35; break; // heat palette - case FX_MODE_RAILWAY : pal = 3; break; // prim + sec - case FX_MODE_2DSOAP : pal = 11; break; // rainbow colors - } + if (pal == 0) pal = _default_palette; //load default palette set in FX _data, party colors as default switch (pal) { case 0: //default palette. Exceptions for specific effects above targetPalette = PartyColors_p; break; case 1: //randomly generated palette - targetPalette = _randomPalette; //random palette is generated at intervals in handleRandomPalette() + targetPalette = _randomPalette; //random palette is generated at intervals in handleRandomPalette() break; case 2: {//primary color only CRGB prim = gamma32(colors[0]); @@ -236,23 +230,11 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec); } break;} - case 6: //Party colors - targetPalette = PartyColors_p; break; - case 7: //Cloud colors - targetPalette = CloudColors_p; break; - case 8: //Lava colors - targetPalette = LavaColors_p; break; - case 9: //Ocean colors - targetPalette = OceanColors_p; break; - case 10: //Forest colors - targetPalette = ForestColors_p; break; - case 11: //Rainbow colors - targetPalette = RainbowColors_p; break; - case 12: //Rainbow stripe colors - targetPalette = RainbowStripeColors_p; break; default: //progmem palettes if (pal>245) { targetPalette = strip.customPalettes[255-pal]; // we checked bounds above + } else if (pal < 13) { // palette 6 - 12, fastled palettes + targetPalette = *fastledPalettes[pal-6]; } else { byte tcp[72]; memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72); @@ -271,7 +253,7 @@ void Segment::startTransition(uint16_t dur) { if (isInTransition()) return; // already in transition no need to store anything // starting a transition has to occur before change so we get current values 1st - _t = new Transition(dur); // no previous transition running + _t = new(std::nothrow) Transition(dur); // no previous transition running if (!_t) return; // failed to allocate data //DEBUG_PRINTF_P(PSTR("-- Started transition: %p (%p)\n"), this, _t); @@ -314,15 +296,16 @@ void Segment::stopTransition() { delete _t; _t = nullptr; } + _transitionprogress = 0xFFFFU; // stop means stop - transition has ended } // transition progression between 0-65535 -uint16_t IRAM_ATTR Segment::progress() const { +inline void Segment::updateTransitionProgress() { + _transitionprogress = 0xFFFFU; if (isInTransition()) { unsigned diff = millis() - _t->_start; - if (_t->_dur > 0 && diff < _t->_dur) return diff * 0xFFFFU / _t->_dur; + if (_t->_dur > 0 && diff < _t->_dur) _transitionprogress = diff * 0xFFFFU / _t->_dur; } - return 0xFFFFU; } #ifndef WLED_DISABLE_MODE_BLEND @@ -344,7 +327,7 @@ void Segment::swapSegenv(tmpsegd_t &tmpSeg) { tmpSeg._callT = call; tmpSeg._dataT = data; tmpSeg._dataLenT = _dataLen; - if (_t && &tmpSeg != &(_t->_segT)) { + if (isInTransition() && &tmpSeg != &(_t->_segT)) { // swap SEGENV with transitional data options = _t->_segT._optionsT; for (size_t i=0; i_segT._colorT[i]; @@ -365,9 +348,9 @@ void Segment::swapSegenv(tmpsegd_t &tmpSeg) { } } -void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { +void Segment::restoreSegenv(const tmpsegd_t &tmpSeg) { //DEBUG_PRINTF_P(PSTR("-- Restoring temp seg: %p->(%p) [%d->%p]\n"), &tmpSeg, this, _dataLen, data); - if (_t && &(_t->_segT) != &tmpSeg) { + if (isInTransition() && &(_t->_segT) != &tmpSeg) { // update possibly changed variables to keep old effect running correctly _t->_segT._aux0T = aux0; _t->_segT._aux1T = aux1; @@ -396,9 +379,9 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { } #endif -uint8_t IRAM_ATTR Segment::currentBri(bool useCct) const { - unsigned prog = progress(); - if (prog < 0xFFFFU) { +uint8_t Segment::currentBri(bool useCct) const { + unsigned prog = isInTransition() ? progress() : 0xFFFFU; + if (prog < 0xFFFFU) { // progress() < 0xFFFF implies that _t is a valid pointer unsigned curBri = (useCct ? cct : (on ? opacity : 0)) * prog; curBri += (useCct ? _t->_cctT : _t->_briT) * (0xFFFFU - prog); return curBri / 0xFFFFU; @@ -408,24 +391,39 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) const { uint8_t Segment::currentMode() const { #ifndef WLED_DISABLE_MODE_BLEND - unsigned prog = progress(); - if (modeBlending && prog < 0xFFFFU) return _t->_modeT; + unsigned prog = isInTransition() ? progress() : 0xFFFFU; + if (modeBlending && prog < 0xFFFFU) return _t->_modeT; // progress() < 0xFFFF implies that _t is a valid pointer #endif return mode; } -uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const { +uint32_t Segment::currentColor(uint8_t slot) const { if (slot >= NUM_COLORS) slot = 0; #ifndef WLED_DISABLE_MODE_BLEND - return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot]; + return isInTransition() ? color_blend16(_t->_segT._colorT[slot], colors[slot], progress()) : colors[slot]; #else - return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot]; + return isInTransition() ? color_blend16(_t->_colorT[slot], colors[slot], progress()) : colors[slot]; #endif } -void Segment::setCurrentPalette() { +// pre-calculate drawing parameters for faster access (based on the idea from @softhack007 from MM fork) +void Segment::beginDraw() { + _vWidth = virtualWidth(); + _vHeight = virtualHeight(); + _vLength = virtualLength(); + _segBri = currentBri(); + unsigned prog = isInTransition() ? progress() : 0xFFFFU; // transition progress; 0xFFFFU = no transition active + // adjust gamma for effects + for (unsigned i = 0; i < NUM_COLORS; i++) { + #ifndef WLED_DISABLE_MODE_BLEND + uint32_t col = isInTransition() ? color_blend16(_t->_segT._colorT[i], colors[i], prog) : colors[i]; + #else + uint32_t col = isInTransition() ? color_blend16(_t->_colorT[i], colors[i], prog) : colors[i]; + #endif + _currentColors[i] = gamma32(col); + } + // load palette into _currentPalette loadPalette(_currentPalette, palette); - unsigned prog = progress(); if (strip.paletteFade && prog < 0xFFFFU) { // blend palettes // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) @@ -442,7 +440,7 @@ void Segment::handleRandomPalette() { if ((uint16_t)((uint16_t)(millis() / 1000U) - _lastPaletteChange) > randomPaletteChangeTime){ _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); _lastPaletteChange = (uint16_t)(millis() / 1000U); - _lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately + _lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately } // if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls) @@ -455,8 +453,10 @@ void Segment::handleRandomPalette() { nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48); } -// segId is given when called from network callback, changes are queued if that segment is currently in its effect function -void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { +// sets Segment geometry (length or width/height and grouping, spacing and offset as well as 2D mapping) +// strip must be suspended (strip.suspend()) before calling this function +// this function may call fill() to clear pixels if spacing or mapping changed (which requires setting _vWidth, _vHeight, _vLength or beginDraw()) +void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t m12) { // return if neither bounds nor grouping have changed bool boundsUnchanged = (start == i1 && stop == i2); #ifndef WLED_DISABLE_2D @@ -464,11 +464,19 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t #endif if (boundsUnchanged && (!grp || (grouping == grp && spacing == spc)) - && (ofs == UINT16_MAX || ofs == offset)) return; + && (ofs == UINT16_MAX || ofs == offset) + && (m12 == map1D2D) + ) return; stateChanged = true; // send UDP/WS broadcast - if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) + if (stop || spc != spacing || m12 != map1D2D) { + _vWidth = virtualWidth(); + _vHeight = virtualHeight(); + _vLength = virtualLength(); + _segBri = currentBri(); + fill(BLACK); // turn old segment range off or clears pixels if changing spacing (requires _vWidth/_vHeight/_vLength/_segBri) + } if (grp) { // prevent assignment of 0 grouping = grp; spacing = spc; @@ -477,6 +485,7 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t spacing = 0; } if (ofs < UINT16_MAX) offset = ofs; + map1D2D = constrain(m12, 0, 7); DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1); DEBUG_PRINT(','); DEBUG_PRINT(i2); @@ -565,9 +574,9 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) { if (modeBlending) startTransition(strip.getTransition()); // set effect transitions #endif mode = fx; + int sOpt; // load default values from effect string if (loadDefaults) { - int sOpt; sOpt = extractModeDefaults(fx, "sx"); speed = (sOpt >= 0) ? sOpt : DEFAULT_SPEED; sOpt = extractModeDefaults(fx, "ix"); intensity = (sOpt >= 0) ? sOpt : DEFAULT_INTENSITY; sOpt = extractModeDefaults(fx, "c1"); custom1 = (sOpt >= 0) ? sOpt : DEFAULT_C1; @@ -584,6 +593,9 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) { 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) setPalette(sOpt); //else setPalette(0); } + sOpt = extractModeDefaults(fx, "pal"); // always extract 'pal' to set _default_palette + if(sOpt <= 0) sOpt = 6; // partycolors if zero or not set + _default_palette = sOpt; // _deault_palette is loaded into pal0 in loadPalette() (if selected) markForReset(); stateChanged = true; // send UDP/WS broadcast } @@ -602,14 +614,14 @@ Segment &Segment::setPalette(uint8_t pal) { } // 2D matrix -unsigned IRAM_ATTR Segment::virtualWidth() const { +unsigned Segment::virtualWidth() const { unsigned groupLen = groupLength(); unsigned 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; } -unsigned IRAM_ATTR Segment::virtualHeight() const { +unsigned Segment::virtualHeight() const { unsigned groupLen = groupLength(); unsigned 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 @@ -653,7 +665,7 @@ static int getPinwheelLength(int vW, int vH) { #endif // 1D strip -uint16_t IRAM_ATTR Segment::virtualLength() const { +uint16_t Segment::virtualLength() const { #ifndef WLED_DISABLE_2D if (is2D()) { unsigned vW = virtualWidth(); @@ -667,7 +679,7 @@ uint16_t IRAM_ATTR Segment::virtualLength() const { vLen = max(vW,vH); // get the longest dimension break; case M12_pArc: - vLen = sqrt16(vH*vH + vW*vW); // use diagonal + vLen = sqrt32_bw(vH*vH + vW*vW); // use diagonal break; case M12_sPinwheel: vLen = getPinwheelLength(vW, vH); @@ -685,20 +697,33 @@ uint16_t IRAM_ATTR Segment::virtualLength() const { return vLength; } -void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) +void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) const { - if (!isActive()) return; // not active + if (!isActive() || i < 0) return; // not active or invalid index #ifndef WLED_DISABLE_2D - int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) + int vStrip = 0; #endif - i &= 0xFFFF; - - if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit + int vL = vLength(); + // if the 1D effect is using virtual strips "i" will have virtual strip id stored in upper 16 bits + // in such case "i" will be > virtualLength() + if (i >= vL) { + // check if this is a virtual strip + #ifndef WLED_DISABLE_2D + vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) + i &= 0xFFFF; //truncate vstrip index + if (i >= vL) return; // if pixel would still fall out of segment just exit + #else + return; + #endif + } #ifndef WLED_DISABLE_2D if (is2D()) { - int vH = virtualHeight(); // segment height in logical pixels - int vW = virtualWidth(); + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) + // pre-scale color for all pixels + col = color_fade(col, _segBri); + _colorScaled = true; switch (map1D2D) { case M12_Pixels: // use all available pixels as a long strip @@ -706,12 +731,12 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) break; case M12_pBar: // expand 1D effect vertically or have it play on virtual strips - if (vStrip>0) setPixelColorXY(vStrip - 1, vH - i - 1, col); - else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col); + if (vStrip > 0) setPixelColorXY(vStrip - 1, vH - i - 1, col); + else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col); break; case M12_pArc: // expand in circular fashion from center - if (i==0) + if (i == 0) setPixelColorXY(0, 0, col); else { float r = i; @@ -768,7 +793,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) // Odd rays start further from center if prevRay started at center. static int prevRay = INT_MIN; // previous ray number if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) { - int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel + int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel posx += inc_x * jump; posy += inc_y * jump; } @@ -790,13 +815,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) break; } } + _colorScaled = false; return; - } else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) { + } else if (Segment::maxHeight != 1 && (width() == 1 || height() == 1)) { if (start < Segment::maxWidth*Segment::maxHeight) { // we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed) int x = 0, y = 0; - if (virtualHeight()>1) y = i; - if (virtualWidth() >1) x = i; + if (vHeight() > 1) y = i; + if (vWidth() > 1) x = i; setPixelColorXY(x, y, col); return; } @@ -804,10 +830,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) #endif unsigned len = length(); - uint8_t _bri_t = currentBri(); - if (_bri_t < 255) { - col = color_fade(col, _bri_t); - } + // if color is unscaled + if (!_colorScaled) col = color_fade(col, _segBri); // expand pixel (taking into account start, grouping, spacing [and offset]) i = i * groupLength(); @@ -830,14 +854,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) indexMir += offset; // offset/phase if (indexMir >= stop) indexMir -= len; // wrap #ifndef WLED_DISABLE_MODE_BLEND - if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true); + if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexMir), col, uint16_t(0xFFFFU - progress())); #endif strip.setPixelColor(indexMir, tmpCol); } indexSet += offset; // offset/phase if (indexSet >= stop) indexSet -= len; // wrap #ifndef WLED_DISABLE_MODE_BLEND - if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true); + if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexSet), col, uint16_t(0xFFFFU - progress())); #endif strip.setPixelColor(indexSet, tmpCol); } @@ -846,7 +870,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) #ifdef WLED_USE_AA_PIXELS // anti-aliased normalized version of setPixelColor() -void Segment::setPixelColor(float i, uint32_t col, bool aa) +void Segment::setPixelColor(float i, uint32_t col, bool aa) const { if (!isActive()) return; // not active int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows) @@ -882,26 +906,23 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa) uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const { if (!isActive()) return 0; // not active -#ifndef WLED_DISABLE_2D - int vStrip = i>>16; -#endif - i &= 0xFFFF; #ifndef WLED_DISABLE_2D if (is2D()) { - int vH = virtualHeight(); // segment height in logical pixels - int vW = virtualWidth(); + const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + const int vH = vHeight(); // segment height in logical pixels (is always >= 1) switch (map1D2D) { case M12_Pixels: return getPixelColorXY(i % vW, i / vW); break; - case M12_pBar: - if (vStrip>0) return getPixelColorXY(vStrip - 1, vH - i -1); - else return getPixelColorXY(0, vH - i -1); - break; + case M12_pBar: { + int vStrip = i>>16; // virtual strips are only relevant in Bar expansion mode + if (vStrip > 0) return getPixelColorXY(vStrip - 1, vH - (i & 0xFFFF) -1); + else return getPixelColorXY(0, vH - i -1); + break; } case M12_pArc: if (i >= vW && i >= vH) { - unsigned vI = sqrt16(i*i/2); + unsigned vI = sqrt32_bw(i*i/2); return getPixelColorXY(vI,vI); // use diagonal } case M12_pCorner: @@ -942,7 +963,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const } #endif - if (reverse) i = virtualLength() - i - 1; + if (reverse) i = vLength() - i - 1; i *= groupLength(); i += start; // offset/phase @@ -951,7 +972,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const return strip.getPixelColor(i); } -uint8_t Segment::differs(Segment& b) const { +uint8_t Segment::differs(const Segment& b) const { uint8_t d = 0; if (start != b.start) d |= SEG_DIFFERS_BOUNDS; if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS; @@ -1031,12 +1052,16 @@ void Segment::refreshLightCapabilities() { */ void Segment::fill(uint32_t c) { if (!isActive()) return; // not active - const int cols = is2D() ? virtualWidth() : virtualLength(); - const int rows = virtualHeight(); // will be 1 for 1D + const int cols = is2D() ? vWidth() : vLength(); + const int rows = vHeight(); // will be 1 for 1D + // pre-scale color for all pixels + c = color_fade(c, _segBri); + _colorScaled = true; for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { if (is2D()) setPixelColorXY(x, y, c); else setPixelColor(x, c); } + _colorScaled = false; } /* @@ -1044,8 +1069,8 @@ void Segment::fill(uint32_t c) { */ void Segment::fade_out(uint8_t rate) { if (!isActive()) return; // not active - const int cols = is2D() ? virtualWidth() : virtualLength(); - const int rows = virtualHeight(); // will be 1 for 1D + const int cols = is2D() ? vWidth() : vLength(); + const int rows = vHeight(); // will be 1 for 1D rate = (255-rate) >> 1; float mappedRate = 1.0f / (float(rate) + 1.1f); @@ -1083,8 +1108,8 @@ void Segment::fade_out(uint8_t rate) { // fades all pixels to black using nscale8() void Segment::fadeToBlackBy(uint8_t fadeBy) { if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply - const int cols = is2D() ? virtualWidth() : virtualLength(); - const int rows = virtualHeight(); // will be 1 for 1D + const int cols = is2D() ? vWidth() : vLength(); + const int rows = vHeight(); // will be 1 for 1D for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy)); @@ -1094,22 +1119,23 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { /* * blurs segment content, source: FastLED colorutils.cpp + * Note: for blur_amount > 215 this function does not work properly (creates alternating pattern) */ void Segment::blur(uint8_t blur_amount, bool smear) { if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D - blur2D(blur_amount, smear); + blur2D(blur_amount, blur_amount, smear); // symmetrical 2D blur //box_blur(map(blur_amount,1,255,1,3), smear); return; } #endif uint8_t keep = smear ? 255 : 255 - blur_amount; - uint8_t seep = blur_amount >> (1 + smear); - unsigned vlength = virtualLength(); + uint8_t seep = blur_amount >> 1; + unsigned vlength = vLength(); uint32_t carryover = BLACK; - uint32_t lastnew; + uint32_t lastnew; // not necessary to initialize lastnew and last, as both will be initialized by the first loop iteration uint32_t last; uint32_t curnew = BLACK; for (unsigned i = 0; i < vlength; i++) { @@ -1117,12 +1143,11 @@ void Segment::blur(uint8_t blur_amount, bool smear) { uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); if (i > 0) { - if (carryover) curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); + if (carryover) curnew = color_add(curnew, carryover); + uint32_t prev = color_add(lastnew, part); // optimization: only set pixel if color has changed if (last != prev) setPixelColor(i - 1, prev); - } else // first pixel - setPixelColor(i, curnew); + } else setPixelColor(i, curnew); // first pixel lastnew = curnew; last = cur; // save original value for comparison on next iteration carryover = part; @@ -1137,11 +1162,11 @@ void Segment::blur(uint8_t blur_amount, bool smear) { */ uint32_t Segment::color_wheel(uint8_t pos) const { if (palette) return color_from_palette(pos, false, true, 0); // perhaps "strip.paletteBlend < 2" should be better instead of "true" - uint8_t w = W(currentColor(0)); + uint8_t w = W(getCurrentColor(0)); pos = 255 - pos; if (pos < 85) { return RGBW32((255 - pos * 3), 0, (pos * 3), w); - } else if(pos < 170) { + } else if (pos < 170) { pos -= 85; return RGBW32(0, (pos * 3), (255 - pos * 3), w); } else { @@ -1160,18 +1185,21 @@ uint32_t Segment::color_wheel(uint8_t pos) const { * @returns Single color from palette */ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) const { - uint32_t color = gamma32(currentColor(mcol)); - + uint32_t color = getCurrentColor(mcol < NUM_COLORS ? mcol : 0); // default palette or no RGB support on segment - if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) return (pbri == 255) ? color : color_fade(color, pbri, true); + if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { + return color_fade(color, pbri, true); + } + const int vL = vLength(); unsigned paletteIndex = i; - if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); + if (mapping && vL > 1) paletteIndex = (i*255)/(vL -1); // paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined) if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + palcol.w = W(color); - return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color)); + return palcol.color32; } @@ -1204,7 +1232,7 @@ void WS2812FX::finalizeInit() { static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins), "The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES"); - + unsigned prevLen = 0; unsigned pinsIndex = 0; for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { @@ -1215,7 +1243,7 @@ void WS2812FX::finalizeInit() { // if we need more pins than available all outputs have been configured if (pinsIndex + busPins > defNumPins) break; - + // Assign all pins first so we can check for conflicts on this bus for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j]; @@ -1286,14 +1314,9 @@ void WS2812FX::finalizeInit() { _isOffRefreshRequired |= bus->isOffRefreshRequired() && !bus->isPWM(); // use refresh bit for phase shift with analog unsigned busEnd = bus->getStart() + bus->getLength(); if (busEnd > _length) _length = busEnd; - #ifdef ESP8266 - // why do we need to reinitialise GPIO3??? - //if (!bus->isDigital() || bus->is2Pin()) continue; - //uint8_t pins[5]; - //if (!bus->getPins(pins)) continue; - //BusDigital* bd = static_cast(bus); - //if (pins[0] == 3) bd->reinit(); - #endif + + // This must be done after all buses have been created, as some kinds (parallel I2S) interact + bus->begin(); } Segment::maxWidth = _length; @@ -1309,7 +1332,14 @@ void WS2812FX::finalizeInit() { void WS2812FX::service() { unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days now = nowUp + timebase; - if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return; + if (_suspend) return; + unsigned long elapsed = nowUp - _lastServiceShow; + + if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited + if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime + if (elapsed < _frametime) return; // too early for service + } + bool doShow = false; _isServicing = true; @@ -1326,18 +1356,13 @@ void WS2812FX::service() { if (!seg.isActive()) continue; // last condition ensures all solid segments are updated at the same time - if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) + if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) { doShow = true; unsigned frameDelay = FRAMETIME; if (!seg.freeze) { //only run effect function if not frozen int oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based) - _virtualSegmentLength = seg.virtualLength(); //SEGLEN - _colors_t[0] = gamma32(seg.currentColor(0)); - _colors_t[1] = gamma32(seg.currentColor(1)); - _colors_t[2] = gamma32(seg.currentColor(2)); - seg.setCurrentPalette(); // load actual palette // when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio // when cctFromRgb is true we implicitly calculate WW and CW from RGB values if (cctFromRgb) BusManager::setSegmentCCT(-1); @@ -1349,13 +1374,14 @@ void WS2812FX::service() { // overwritten by later effect. To enable seamless blending for every effect, additional LED buffer // would need to be allocated for each effect and then blended together for each pixel. [[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition - frameDelay = (*_mode[seg.mode])(); // run new/current mode + seg.beginDraw(); // set up parameters for get/setPixelColor() + frameDelay = (*_mode[seg.mode])(); // run new/current mode #ifndef WLED_DISABLE_MODE_BLEND if (modeBlending && seg.mode != tmpMode) { Segment::tmpsegd_t _tmpSegData; Segment::modeBlend(true); // set semaphore seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) - _virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed) + seg.beginDraw(); // set up parameters for get/setPixelColor() unsigned d2 = (*_mode[tmpMode])(); // run old mode seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state) frameDelay = min(frameDelay,d2); // use shortest delay @@ -1371,24 +1397,24 @@ void WS2812FX::service() { } _segment_index++; } - _virtualSegmentLength = 0; _isServicing = false; _triggered = false; #ifdef WLED_DEBUG - if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); + if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); #endif if (doShow) { yield(); Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette show(); + _lastServiceShow = nowUp; // update timestamp, for precise FPS control } #ifdef WLED_DEBUG - if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); + if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); #endif } -void IRAM_ATTR WS2812FX::setPixelColor(unsigned i, uint32_t col) { +void IRAM_ATTR WS2812FX::setPixelColor(unsigned i, uint32_t col) const { i = getMappedPixelIndex(i); if (i >= _length) return; BusManager::setPixelColor(i, col); @@ -1404,13 +1430,13 @@ void WS2812FX::show() { // avoid race condition, capture _callback value show_callback callback = _callback; if (callback) callback(); + unsigned long showNow = millis(); // some buses send asynchronously and this method will return before // all of the data has been sent. // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods BusManager::show(); - unsigned long showNow = millis(); size_t diff = showNow - _lastShow; if (diff > 0) { // skip calculation if no time has passed @@ -1420,47 +1446,10 @@ void WS2812FX::show() { } } -/** - * Returns a true value if any of the strips are still being updated. - * On some hardware (ESP32), strip updates are done asynchronously. - */ -bool WS2812FX::isUpdating() const { - return !BusManager::canAllShow(); -} - -/** - * 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 - */ -uint16_t WS2812FX::getFps() const { - if (millis() - _lastShow > 2000) return 0; - return (FPS_MULTIPLIER * _cumulativeFps) >> FPS_CALC_SHIFT; // _cumulativeFps is stored in fixed point -} - -void WS2812FX::setTargetFps(uint8_t fps) { - if (fps > 0 && fps <= 120) _targetFps = fps; - _frametime = 1000 / _targetFps; -} - -void WS2812FX::setMode(uint8_t segid, uint8_t m) { - if (segid >= _segments.size()) return; - - if (m >= getModeCount()) m = getModeCount() - 1; - - if (_segments[segid].mode != m) { - _segments[segid].setMode(m); // do not load defaults - } -} - -//applies to all active and selected segments -void WS2812FX::setColor(uint8_t slot, uint32_t c) { - if (slot >= NUM_COLORS) return; - - for (segment &seg : _segments) { - if (seg.isActive() && seg.isSelected()) { - seg.setColor(slot, c); - } - } +void WS2812FX::setTargetFps(unsigned fps) { + if (fps <= 250) _targetFps = fps; + if (_targetFps > 0) _frametime = 1000 / _targetFps; + else _frametime = MIN_FRAME_DELAY; // unlimited mode } void WS2812FX::setCCT(uint16_t k) { @@ -1487,7 +1476,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) { BusManager::setBrightness(b); if (!direct) { unsigned long t = millis(); - if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon + if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon } } @@ -1509,7 +1498,7 @@ uint8_t WS2812FX::getFirstSelectedSegId() const { return getMainSegmentId(); } -void WS2812FX::setMainSegmentId(uint8_t n) { +void WS2812FX::setMainSegmentId(unsigned n) { _mainSegment = 0; if (n < _segments.size()) { _mainSegment = n; @@ -1585,23 +1574,10 @@ void WS2812FX::purgeSegments() { } } -Segment& WS2812FX::getSegment(uint8_t id) { +Segment& WS2812FX::getSegment(unsigned id) { return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors } -// sets new segment bounds, queues if that segment is currently running -void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) { - if (segId >= getSegmentsNum()) { - if (i2 <= i1) return; // do not append empty/inactive segments - appendSegment(Segment(0, strip.getLengthTotal())); - segId = getSegmentsNum()-1; // segments are added at the end of list - } - suspend(); - _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); - resume(); - if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector -} - void WS2812FX::resetSegments() { _segments.clear(); // destructs all Segment as part of clearing #ifndef WLED_DISABLE_2D @@ -1719,9 +1695,9 @@ void WS2812FX::fixInvalidSegments() { //true if all segments align with a bus, or if a segment covers the total length //irrelevant in 2D set-up -bool WS2812FX::checkSegmentAlignment() { +bool WS2812FX::checkSegmentAlignment() const { bool aligned = false; - for (segment &seg : _segments) { + for (const segment &seg : _segments) { for (unsigned b = 0; bgetStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; @@ -1800,7 +1776,7 @@ void WS2812FX::loadCustomPalettes() { } //load custom mapping table from JSON file (called from finalizeInit() or deserializeState()) -bool WS2812FX::deserializeMap(uint8_t n) { +bool WS2812FX::deserializeMap(unsigned n) { // 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one. char fileName[32]; @@ -1838,8 +1814,8 @@ bool WS2812FX::deserializeMap(uint8_t n) { Segment::maxHeight = min(max(root[F("height")].as(), 1), 128); } - if (customMappingTable) delete[] customMappingTable; - customMappingTable = new uint16_t[getLengthTotal()]; + if (customMappingTable) free(customMappingTable); + customMappingTable = static_cast(malloc(sizeof(uint16_t)*getLengthTotal())); if (customMappingTable) { DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName); @@ -1893,14 +1869,6 @@ bool WS2812FX::deserializeMap(uint8_t n) { return (customMappingSize > 0); } -uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) const { - // convert logical address to physical - if (index < customMappingSize - && (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index]; - - return index; -} - WS2812FX* WS2812FX::instance = nullptr; @@ -1913,5 +1881,5 @@ const char JSON_palette_names[] PROGMEM = R"=====([ "Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", "Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf", "Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide", -"Candy2" +"Candy2","Traffic Light" ])====="; diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index b108f294b..81b9ec346 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -126,10 +126,10 @@ void onAlexaChange(EspalexaDevice* dev) } else { colorKtoRGB(k, rgbw); } - strip.setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3])); + strip.getMainSegment().setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3])); } else { uint32_t color = dev->getRGB(); - strip.setColor(0, color); + strip.getMainSegment().setColor(0, color); } stateUpdated(CALL_MODE_ALEXA); } diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 5b948b9c4..6e159a82b 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -27,7 +27,7 @@ extern bool cctICused; uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); //udp.cpp -uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false); +uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri=255, bool isRGBW=false); // enable additional debug output #if defined(WLED_DEBUG_HOST) @@ -121,7 +121,7 @@ uint8_t *Bus::allocateData(size_t size) { } -BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) +BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814)) , _skip(bc.skipAmount) //sacrificial pixels , _colorOrder(bc.colorOrder) @@ -150,21 +150,11 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) //_buffering = bc.doubleBuffer; uint16_t lenToCreate = bc.count; if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus - _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz); + _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr); _valid = (_busPtr != nullptr); DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax); } -//fine tune power estimation constants for your setup -//you can set it to 0 if the ESP is powered by USB and the LEDs by external -#ifndef MA_FOR_ESP - #ifdef ESP8266 - #define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA) - #else - #define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA) - #endif -#endif - //DISCLAIMER //The following function attemps to calculate the current LED power usage, //and will limit the brightness to stay below a set amperage threshold. @@ -306,22 +296,22 @@ void BusDigital::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { +void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { if (!_valid) return; - uint8_t cctWW = 0, cctCW = 0; if (hasWhite()) c = autoWhiteCalc(c); if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT if (_data) { size_t offset = pix * getNumberOfChannels(); + uint8_t* dataptr = _data + offset; if (hasRGB()) { - _data[offset++] = R(c); - _data[offset++] = G(c); - _data[offset++] = B(c); + *dataptr++ = R(c); + *dataptr++ = G(c); + *dataptr++ = B(c); } - if (hasWhite()) _data[offset++] = W(c); + if (hasWhite()) *dataptr++ = W(c); // unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT // we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio) - if (hasCCT()) _data[offset] = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it + if (hasCCT()) *dataptr = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it } else { if (_reversed) pix = _len - pix -1; pix += _skip; @@ -336,16 +326,22 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; } } - if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); - PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); + uint16_t wwcw = 0; + if (hasCCT()) { + uint8_t cctWW = 0, cctCW = 0; + Bus::calculateCCT(c, cctWW, cctCW); + wwcw = (cctCW<<8) | cctWW; + } + + PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw); } } // returns original color if global buffering is enabled, else returns lossly restored color from bus -uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const { +uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { if (!_valid) return 0; if (_data) { - size_t offset = pix * getNumberOfChannels(); + const size_t offset = pix * getNumberOfChannels(); uint32_t c; if (!hasRGB()) { c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]); @@ -356,7 +352,7 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const { } else { if (_reversed) pix = _len - pix -1; pix += _skip; - unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); + const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri); if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs unsigned r = R(c); @@ -410,9 +406,9 @@ std::vector BusDigital::getLEDTypes() { }; } -void BusDigital::reinit() { +void BusDigital::begin() { if (!_valid) return; - PolyBus::begin(_busPtr, _iType, _pins); + PolyBus::begin(_busPtr, _iType, _pins, _frequencykHz); } void BusDigital::cleanup() { @@ -452,7 +448,7 @@ void BusDigital::cleanup() { #endif #endif -BusPwm::BusPwm(BusConfig &bc) +BusPwm::BusPwm(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering { if (!isPWM(bc.type)) return; @@ -501,7 +497,7 @@ BusPwm::BusPwm(BusConfig &bc) DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]); } -void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { +void BusPwm::setPixelColor(unsigned pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c); if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { @@ -538,7 +534,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { } //does no index check -uint32_t BusPwm::getPixelColor(uint16_t pix) const { +uint32_t BusPwm::getPixelColor(unsigned pix) const { if (!_valid) return 0; // TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented) switch (_type) { @@ -567,19 +563,15 @@ void BusPwm::show() { const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8) [[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits) - // use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness - // the formula is based on 12 bit resolution as there is no need for greater precision + // use CIE brightness formula (linear + cubic) to approximate human eye perceived brightness // see: https://en.wikipedia.org/wiki/Lightness - unsigned pwmBri = (unsigned)_bri * 100; // enlarge to use integer math for linear response - if (pwmBri < 2040) { - // linear response for values [0-20] - pwmBri = ((pwmBri << 12) + 115043) / 230087; //adding '0.5' before division for correct rounding - } else { - // cubic response for values [21-255] - pwmBri += 4080; - float temp = (float)pwmBri / 29580.0f; - temp = temp * temp * temp * (float)maxBri; - pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] + unsigned pwmBri = _bri; + if (pwmBri < 21) { // linear response for values [0-20] + pwmBri = (pwmBri * maxBri + 2300 / 2) / 2300 ; // adding '0.5' before division for correct rounding, 2300 gives a good match to CIE curve + } else { // cubic response for values [21-255] + float temp = float(pwmBri + 41) / float(255 + 41); // 41 is to match offset & slope to linear part + temp = temp * temp * temp * (float)maxBri; + pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] C } [[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri) @@ -654,7 +646,7 @@ void BusPwm::deallocatePins() { } -BusOnOff::BusOnOff(BusConfig &bc) +BusOnOff::BusOnOff(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed) , _onoffdata(0) { @@ -674,7 +666,7 @@ BusOnOff::BusOnOff(BusConfig &bc) DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); } -void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { +void BusOnOff::setPixelColor(unsigned pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel c = autoWhiteCalc(c); uint8_t r = R(c); @@ -684,7 +676,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { _data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; } -uint32_t BusOnOff::getPixelColor(uint16_t pix) const { +uint32_t BusOnOff::getPixelColor(unsigned pix) const { if (!_valid) return 0; return RGBW32(_data[0], _data[0], _data[0], _data[0]); } @@ -707,7 +699,7 @@ std::vector BusOnOff::getLEDTypes() { }; } -BusNetwork::BusNetwork(BusConfig &bc) +BusNetwork::BusNetwork(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, bc.count) , _broadcastLock(false) { @@ -734,7 +726,7 @@ BusNetwork::BusNetwork(BusConfig &bc) DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); } -void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { +void BusNetwork::setPixelColor(unsigned pix, uint32_t c) { if (!_valid || pix >= _len) return; if (_hasWhite) c = autoWhiteCalc(c); if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT @@ -745,7 +737,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { if (_hasWhite) _data[offset+3] = W(c); } -uint32_t BusNetwork::getPixelColor(uint16_t pix) const { +uint32_t BusNetwork::getPixelColor(unsigned pix) const { if (!_valid || pix >= _len) return 0; unsigned offset = pix * _UDPchannels; return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (hasWhite() ? _data[offset+3] : 0)); @@ -786,7 +778,7 @@ void BusNetwork::cleanup() { //utility to get the approx. memory usage of a given BusConfig -uint32_t BusManager::memUsage(BusConfig &bc) { +uint32_t BusManager::memUsage(const BusConfig &bc) { if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS; unsigned len = bc.count + bc.skipAmount; @@ -811,7 +803,7 @@ uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned return (maxChannels * maxCount * minBuses * multiplier); } -int BusManager::add(BusConfig &bc) { +int BusManager::add(const BusConfig &bc) { if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; if (Bus::isVirtual(bc.type)) { busses[numBusses] = new BusNetwork(bc); @@ -910,7 +902,7 @@ void BusManager::on() { if (busses[i]->isDigital() && busses[i]->getPins(pins)) { if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) { BusDigital *bus = static_cast(busses[i]); - bus->reinit(); + bus->begin(); break; } } @@ -943,7 +935,6 @@ void BusManager::show() { busses[i]->show(); _milliAmpsUsed += busses[i]->getUsedCurrent(); } - if (_milliAmpsUsed) _milliAmpsUsed += MA_FOR_ESP; } void BusManager::setStatusPixel(uint32_t c) { @@ -952,7 +943,7 @@ void BusManager::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) { +void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) { for (unsigned i = 0; i < numBusses; i++) { unsigned bstart = busses[i]->getStart(); if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue; @@ -975,7 +966,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { Bus::setCCT(cct); } -uint32_t BusManager::getPixelColor(uint16_t pix) { +uint32_t BusManager::getPixelColor(unsigned pix) { for (unsigned i = 0; i < numBusses; i++) { unsigned bstart = busses[i]->getStart(); if (!busses[i]->containsPixel(pix)) continue; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index e96b9de71..9aed01308 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -6,6 +6,7 @@ */ #include "const.h" +#include "pin_manager.h" #include //colors.cpp @@ -79,13 +80,14 @@ class Bus { virtual ~Bus() {} //throw the bus under the bus + virtual void begin() {}; virtual void show() = 0; virtual bool canShow() const { return true; } virtual void setStatusPixel(uint32_t c) {} - virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; + virtual void setPixelColor(unsigned pix, uint32_t c) = 0; virtual void setBrightness(uint8_t b) { _bri = b; }; virtual void setColorOrder(uint8_t co) {} - virtual uint32_t getPixelColor(uint16_t pix) const { return 0; } + virtual uint32_t getPixelColor(unsigned pix) const { return 0; } virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; } virtual uint16_t getLength() const { return isOk() ? _len : 0; } virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } @@ -109,7 +111,7 @@ class Bus { inline void setStart(uint16_t start) { _start = start; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } - inline uint8_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } + inline uint32_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } inline uint16_t getStart() const { return _start; } inline uint8_t getType() const { return _type; } inline bool isOk() const { return _valid; } @@ -118,8 +120,8 @@ class Bus { inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes - static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK - static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } + static constexpr uint32_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK + static constexpr uint32_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr bool hasRGB(uint8_t type) { return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF); } @@ -196,16 +198,16 @@ class Bus { class BusDigital : public Bus { public: - BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com); + BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com); ~BusDigital() { cleanup(); } void show() override; bool canShow() const override; void setBrightness(uint8_t b) override; void setStatusPixel(uint32_t c) override; - [[gnu::hot]] void setPixelColor(uint16_t pix, uint32_t c) override; + [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; void setColorOrder(uint8_t colorOrder) override; - [[gnu::hot]] uint32_t getPixelColor(uint16_t pix) const override; + [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; uint8_t getColorOrder() const override { return _colorOrder; } uint8_t getPins(uint8_t* pinArray = nullptr) const override; uint8_t skippedLeds() const override { return _skip; } @@ -213,7 +215,7 @@ class BusDigital : public Bus { uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; } uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } uint16_t getMaxCurrent() const override { return _milliAmpsMax; } - void reinit(); + void begin() override; void cleanup(); static std::vector getLEDTypes(); @@ -248,11 +250,11 @@ class BusDigital : public Bus { class BusPwm : public Bus { public: - BusPwm(BusConfig &bc); + BusPwm(const BusConfig &bc); ~BusPwm() { cleanup(); } - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; //does no index check + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; //does no index check uint8_t getPins(uint8_t* pinArray = nullptr) const override; uint16_t getFrequency() const override { return _frequency; } void show() override; @@ -275,11 +277,11 @@ class BusPwm : public Bus { class BusOnOff : public Bus { public: - BusOnOff(BusConfig &bc); + BusOnOff(const BusConfig &bc); ~BusOnOff() { cleanup(); } - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; uint8_t getPins(uint8_t* pinArray) const override; void show() override; void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); } @@ -294,12 +296,12 @@ class BusOnOff : public Bus { class BusNetwork : public Bus { public: - BusNetwork(BusConfig &bc); + BusNetwork(const BusConfig &bc); ~BusNetwork() { cleanup(); } bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; uint8_t getPins(uint8_t* pinArray = nullptr) const override; void show() override; void cleanup(); @@ -362,17 +364,27 @@ struct BusConfig { }; +//fine tune power estimation constants for your setup +//you can set it to 0 if the ESP is powered by USB and the LEDs by external +#ifndef MA_FOR_ESP + #ifdef ESP8266 + #define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA) + #else + #define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA) + #endif +#endif + class BusManager { public: BusManager() {}; //utility to get the approx. memory usage of a given BusConfig - static uint32_t memUsage(BusConfig &bc); + static uint32_t memUsage(const BusConfig &bc); static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1); - static uint16_t currentMilliamps() { return _milliAmpsUsed; } + static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; } static uint16_t ablMilliampsMax() { return _milliAmpsMax; } - static int add(BusConfig &bc); + static int add(const BusConfig &bc); static void useParallelOutput(); // workaround for inaccessible PolyBus //do not call this method from system context (network callback) @@ -384,13 +396,13 @@ class BusManager { static void show(); static bool canAllShow(); static void setStatusPixel(uint32_t c); - [[gnu::hot]] static void setPixelColor(uint16_t pix, uint32_t c); + [[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c); static void setBrightness(uint8_t b); // for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} - static uint32_t getPixelColor(uint16_t pix); + [[gnu::hot]] static uint32_t getPixelColor(unsigned pix); static inline int16_t getSegmentCCT() { return Bus::getCCT(); } static Bus* getBus(uint8_t busNr); diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 84c32f46b..d2a18c9d8 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -336,7 +336,7 @@ class PolyBus { // initialize SPI bus speed for DotStar methods template - static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) { + static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz /* 0 == use default */) { T dotStar_strip = static_cast(busPtr); #ifdef ESP8266 dotStar_strip->Begin(); @@ -363,7 +363,7 @@ class PolyBus { tm1914_strip->SetPixelSettings(NeoTm1914Settings()); //NeoTm1914_Mode_DinFdinAutoSwitch, NeoTm1914_Mode_DinOnly, NeoTm1914_Mode_FdinOnly } - static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) { + static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz /* only used by DotStar */) { switch (busType) { case I_NONE: break; #ifdef ESP8266 @@ -480,7 +480,7 @@ class PolyBus { } } - static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) { + static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel) { #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation @@ -597,7 +597,7 @@ class PolyBus { case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break; case I_SS_P98_3: busPtr = new B_SS_P98_3(len, pins[1], pins[0]); break; } - begin(busPtr, busType, pins, clock_kHz); + return busPtr; } diff --git a/wled00/button.cpp b/wled00/button.cpp index 4d6f954f6..5144f09f2 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -29,7 +29,7 @@ void shortPressAction(uint8_t b) #ifndef WLED_DISABLE_MQTT // publish MQTT message if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { - char subuf[64]; + char subuf[MQTT_MAX_TOPIC_LEN + 32]; sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, "short"); } @@ -62,7 +62,7 @@ void longPressAction(uint8_t b) #ifndef WLED_DISABLE_MQTT // publish MQTT message if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { - char subuf[64]; + char subuf[MQTT_MAX_TOPIC_LEN + 32]; sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, "long"); } @@ -83,19 +83,19 @@ void doublePressAction(uint8_t b) #ifndef WLED_DISABLE_MQTT // publish MQTT message if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { - char subuf[64]; + char subuf[MQTT_MAX_TOPIC_LEN + 32]; sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, "double"); } #endif } -bool isButtonPressed(uint8_t i) +bool isButtonPressed(uint8_t b) { - if (btnPin[i]<0) return false; - unsigned pin = btnPin[i]; + if (btnPin[b]<0) return false; + unsigned pin = btnPin[b]; - switch (buttonType[i]) { + switch (buttonType[b]) { case BTN_TYPE_NONE: case BTN_TYPE_RESERVED: break; @@ -113,7 +113,7 @@ bool isButtonPressed(uint8_t i) #ifdef SOC_TOUCH_VERSION_2 //ESP32 S2 and S3 provide a function to check touch state (state is updated in interrupt) if (touchInterruptGetLastStatus(pin)) return true; #else - if (digitalPinToTouchChannel(btnPin[i]) >= 0 && touchRead(pin) <= touchThreshold) return true; + if (digitalPinToTouchChannel(btnPin[b]) >= 0 && touchRead(pin) <= touchThreshold) return true; #endif #endif break; @@ -151,7 +151,7 @@ void handleSwitch(uint8_t b) #ifndef WLED_DISABLE_MQTT // publish MQTT message if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { - char subuf[64]; + char subuf[MQTT_MAX_TOPIC_LEN + 32]; if (buttonType[b] == BTN_TYPE_PIR_SENSOR) sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)b); else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, !buttonPressedBefore[b] ? "off" : "on"); @@ -375,6 +375,7 @@ void handleIO() if (rlyPin>=0) { pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT); digitalWrite(rlyPin, rlyMde); + delay(50); // wait for relay to switch and power to stabilize } offMode = false; } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 3f6cfbacb..443f16c73 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -436,13 +436,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { else gammaCorrectBri = false; if (light_gc_col > 1.0f) gammaCorrectCol = true; else gammaCorrectCol = false; - if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) { - if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); - } else { + if (gammaCorrectVal <= 1.0f || gammaCorrectVal > 3) { gammaCorrectVal = 1.0f; // no gamma correction gammaCorrectBri = false; gammaCorrectCol = false; } + NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table JsonObject light_tr = light["tr"]; CJSON(fadeTransition, light_tr["mode"]); @@ -523,6 +522,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { tdd = if_live[F("timeout")] | -1; if (tdd >= 0) realtimeTimeoutMs = tdd * 100; + + #ifdef WLED_ENABLE_DMX_INPUT + CJSON(dmxInputTransmitPin, if_live_dmx[F("inputRxPin")]); + CJSON(dmxInputReceivePin, if_live_dmx[F("inputTxPin")]); + CJSON(dmxInputEnablePin, if_live_dmx[F("inputEnablePin")]); + CJSON(dmxInputPort, if_live_dmx[F("dmxInputPort")]); + #endif + CJSON(arlsForceMaxBri, if_live[F("maxbri")]); CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false CJSON(arlsOffset, if_live[F("offset")]); // 0 @@ -1002,6 +1009,12 @@ void serializeConfig() { if_live_dmx[F("addr")] = DMXAddress; if_live_dmx[F("dss")] = DMXSegmentSpacing; if_live_dmx["mode"] = DMXMode; + #ifdef WLED_ENABLE_DMX_INPUT + if_live_dmx[F("inputRxPin")] = dmxInputTransmitPin; + if_live_dmx[F("inputTxPin")] = dmxInputReceivePin; + if_live_dmx[F("inputEnablePin")] = dmxInputEnablePin; + if_live_dmx[F("dmxInputPort")] = dmxInputPort; + #endif if_live[F("timeout")] = realtimeTimeoutMs / 100; if_live[F("maxbri")] = arlsForceMaxBri; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 478a0a277..f154a1aea 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -5,61 +5,56 @@ */ /* - * color blend function + * color blend function, based on FastLED blend function + * the calculation for each color is: result = (A*(amountOfA) + A + B*(amountOfB) + B) / 256 with amountOfA = 255 - amountOfB */ -uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) { - if (blend == 0) return color1; - unsigned blendmax = b16 ? 0xFFFF : 0xFF; - if (blend == blendmax) return color2; - unsigned shift = b16 ? 16 : 8; - - uint32_t w1 = W(color1); - uint32_t r1 = R(color1); - uint32_t g1 = G(color1); - uint32_t b1 = B(color1); - - uint32_t w2 = W(color2); - uint32_t r2 = R(color2); - uint32_t g2 = G(color2); - uint32_t b2 = B(color2); - - uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift; - uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift; - uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift; - uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift; - - return RGBW32(r3, g3, b3, w3); +uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { + // min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance + uint32_t rb1 = color1 & 0x00FF00FF; + uint32_t wg1 = (color1>>8) & 0x00FF00FF; + uint32_t rb2 = color2 & 0x00FF00FF; + uint32_t wg2 = (color2>>8) & 0x00FF00FF; + uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & 0x00FF00FF; + uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & 0xFF00FF00; + return rb3 | wg3; } /* * color add function that preserves ratio - * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + * original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + * speed optimisations by @dedehai */ -uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) +uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; - if (fast) { - uint8_t r = R(c1); - uint8_t g = G(c1); - uint8_t b = B(c1); - uint8_t w = W(c1); - r = qadd8(r, R(c2)); - g = qadd8(g, G(c2)); - b = qadd8(b, B(c2)); - w = qadd8(w, W(c2)); - return RGBW32(r,g,b,w); + uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once + uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF); + uint32_t r = rb >> 16; // extract single color values + uint32_t b = rb & 0xFFFF; + uint32_t w = wg >> 16; + uint32_t g = wg & 0xFFFF; + + if (preserveCR) { // preserve color ratios + uint32_t max = std::max(r,g); // check for overflow note + max = std::max(max,b); + max = std::max(max,w); + //unsigned max = r; // check for overflow note + //max = g > max ? g : max; + //max = b > max ? b : max; + //max = w > max ? w : max; + if (max > 255) { + uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead + rb = ((rb * scale) >> 8) & 0x00FF00FF; // + wg = (wg * scale) & 0xFF00FF00; + } else wg = wg << 8; //shift white and green back to correct position + return rb | wg; } else { - uint32_t r = R(c1) + R(c2); - uint32_t g = G(c1) + G(c2); - uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2); - unsigned max = r; - if (g > max) max = g; - if (b > max) max = b; - if (w > max) max = w; - if (max < 256) return RGBW32(r, g, b, w); - else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + r = r > 255 ? 255 : r; + g = g > 255 ? 255 : g; + b = b > 255 ? 255 : b; + w = w > 255 ? 255 : w; + return RGBW32(r,g,b,w); } } @@ -70,27 +65,53 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { - if (c1 == BLACK || amount + video == 0) return BLACK; + if (amount == 255) return c1; + if (c1 == BLACK || amount == 0) return BLACK; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB - uint32_t r = R(c1); - uint32_t g = G(c1); - uint32_t b = B(c1); - uint32_t w = W(c1); uint32_t scale = amount; // 32bit for faster calculation - if (video) { - scaledcolor = (((r * scale) >> 8) + ((r && scale) ? 1 : 0)) << 16; - scaledcolor |= (((g * scale) >> 8) + ((g && scale) ? 1 : 0)) << 8; - scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0); - scaledcolor |= (((w * scale) >> 8) + ((w && scale) ? 1 : 0)) << 24; - } else { - scaledcolor = ((r * scale) >> 8) << 16; - scaledcolor |= ((g * scale) >> 8) << 8; - scaledcolor |= (b * scale) >> 8; - scaledcolor |= ((w * scale) >> 8) << 24; + uint32_t addRemains = 0; + if (!video) scale++; // add one for correct scaling using bitshifts + else { // video scaling: make sure colors do not dim to zero if they started non-zero + addRemains = R(c1) ? 0x00010000 : 0; + addRemains |= G(c1) ? 0x00000100 : 0; + addRemains |= B(c1) ? 0x00000001 : 0; + addRemains |= W(c1) ? 0x01000000 : 0; } + uint32_t rb = (((c1 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue + uint32_t wg = (((c1 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green + scaledcolor = (rb | wg) + addRemains; return scaledcolor; } +// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) +uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) +{ + if (blendType == LINEARBLEND_NOWRAP) { + index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + unsigned hi4 = byte(index) >> 4; + const CRGB* entry = (CRGB*)((uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); + unsigned red1 = entry->r; + unsigned green1 = entry->g; + unsigned blue1 = entry->b; + if (blendType != NOBLEND) { + if (hi4 == 15) entry = &(pal[0]); + else ++entry; + unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 + unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max + red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; + green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; + blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; + } + if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + uint32_t scale = brightness + 1; // adjust for rounding (bitshift) + red1 = (red1 * scale) >> 8; + green1 = (green1 * scale) >> 8; + blue1 = (blue1 * scale) >> 8; + } + return RGBW32(red1,green1,blue1,0); +} + void setRandomColor(byte* rgb) { lastRandomIndex = get_random_wheel_index(lastRandomIndex); @@ -101,93 +122,93 @@ void setRandomColor(byte* rgb) * generates a random palette based on harmonic color theory * takes a base palette as the input, it will choose one color of the base palette and keep it */ -CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette) +CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette) { - CHSV palettecolors[4]; //array of colors for the new palette - uint8_t keepcolorposition = random8(4); //color position of current random palette to keep - palettecolors[keepcolorposition] = rgb2hsv_approximate(basepalette.entries[keepcolorposition*5]); //read one of the base colors of the current palette - palettecolors[keepcolorposition].hue += random8(10)-5; // +/- 5 randomness of base color - //generate 4 saturation and brightness value numbers - //only one saturation is allowed to be below 200 creating mostly vibrant colors - //only one brightness value number is allowed below 200, creating mostly bright palettes + CHSV palettecolors[4]; // array of colors for the new palette + uint8_t keepcolorposition = hw_random8(4); // color position of current random palette to keep + palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette + palettecolors[keepcolorposition].hue += hw_random8(10)-5; // +/- 5 randomness of base color + // generate 4 saturation and brightness value numbers + // only one saturation is allowed to be below 200 creating mostly vibrant colors + // only one brightness value number is allowed below 200, creating mostly bright palettes - for (int i = 0; i < 3; i++) { //generate three high values - palettecolors[i].saturation = random8(200,255); - palettecolors[i].value = random8(220,255); + for (int i = 0; i < 3; i++) { // generate three high values + palettecolors[i].saturation = hw_random8(200,255); + palettecolors[i].value = hw_random8(220,255); } - //allow one to be lower - palettecolors[3].saturation = random8(20,255); - palettecolors[3].value = random8(80,255); + // allow one to be lower + palettecolors[3].saturation = hw_random8(20,255); + palettecolors[3].value = hw_random8(80,255); - //shuffle the arrays + // shuffle the arrays for (int i = 3; i > 0; i--) { - std::swap(palettecolors[i].saturation, palettecolors[random8(i + 1)].saturation); - std::swap(palettecolors[i].value, palettecolors[random8(i + 1)].value); + std::swap(palettecolors[i].saturation, palettecolors[hw_random8(i + 1)].saturation); + std::swap(palettecolors[i].value, palettecolors[hw_random8(i + 1)].value); } - //now generate three new hues based off of the hue of the chosen current color + // now generate three new hues based off of the hue of the chosen current color uint8_t basehue = palettecolors[keepcolorposition].hue; - uint8_t harmonics[3]; //hues that are harmonic but still a little random - uint8_t type = random8(5); //choose a harmony type + uint8_t harmonics[3]; // hues that are harmonic but still a little random + uint8_t type = hw_random8(5); // choose a harmony type switch (type) { case 0: // analogous - harmonics[0] = basehue + random8(30, 50); - harmonics[1] = basehue + random8(10, 30); - harmonics[2] = basehue - random8(10, 30); + harmonics[0] = basehue + hw_random8(30, 50); + harmonics[1] = basehue + hw_random8(10, 30); + harmonics[2] = basehue - hw_random8(10, 30); break; case 1: // triadic - harmonics[0] = basehue + 113 + random8(15); - harmonics[1] = basehue + 233 + random8(15); - harmonics[2] = basehue - 7 + random8(15); + harmonics[0] = basehue + 113 + hw_random8(15); + harmonics[1] = basehue + 233 + hw_random8(15); + harmonics[2] = basehue - 7 + hw_random8(15); break; case 2: // split-complementary - harmonics[0] = basehue + 145 + random8(10); - harmonics[1] = basehue + 205 + random8(10); - harmonics[2] = basehue - 5 + random8(10); + harmonics[0] = basehue + 145 + hw_random8(10); + harmonics[1] = basehue + 205 + hw_random8(10); + harmonics[2] = basehue - 5 + hw_random8(10); break; - + case 3: // square - harmonics[0] = basehue + 85 + random8(10); - harmonics[1] = basehue + 175 + random8(10); - harmonics[2] = basehue + 265 + random8(10); + harmonics[0] = basehue + 85 + hw_random8(10); + harmonics[1] = basehue + 175 + hw_random8(10); + harmonics[2] = basehue + 265 + hw_random8(10); break; case 4: // tetradic - harmonics[0] = basehue + 80 + random8(20); - harmonics[1] = basehue + 170 + random8(20); - harmonics[2] = basehue - 15 + random8(30); + harmonics[0] = basehue + 80 + hw_random8(20); + harmonics[1] = basehue + 170 + hw_random8(20); + harmonics[2] = basehue - 15 + hw_random8(30); break; } - if (random8() < 128) { - //50:50 chance of shuffling hues or keep the color order + if (hw_random8() < 128) { + // 50:50 chance of shuffling hues or keep the color order for (int i = 2; i > 0; i--) { - std::swap(harmonics[i], harmonics[random8(i + 1)]); + std::swap(harmonics[i], harmonics[hw_random8(i + 1)]); } } - //now set the hues + // now set the hues int j = 0; for (int i = 0; i < 4; i++) { - if (i==keepcolorposition) continue; //skip the base color + if (i==keepcolorposition) continue; // skip the base color palettecolors[i].hue = harmonics[j]; j++; } bool makepastelpalette = false; - if (random8() < 25) { //~10% chance of desaturated 'pastel' colors + if (hw_random8() < 25) { // ~10% chance of desaturated 'pastel' colors makepastelpalette = true; } - //apply saturation & gamma correction + // apply saturation & gamma correction CRGB RGBpalettecolors[4]; for (int i = 0; i < 4; i++) { - if (makepastelpalette && palettecolors[i].saturation > 180) { + if (makepastelpalette && palettecolors[i].saturation > 180) { palettecolors[i].saturation -= 160; //desaturate all four colors - } + } RGBpalettecolors[i] = (CRGB)palettecolors[i]; //convert to RGB RGBpalettecolors[i] = gamma32(((uint32_t)RGBpalettecolors[i]) & 0x00FFFFFFU); //strip alpha from CRGB } @@ -198,34 +219,72 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette) RGBpalettecolors[3]); } -CRGBPalette16 generateRandomPalette() //generate fully random palette +CRGBPalette16 generateRandomPalette() // generate fully random palette { - return CRGBPalette16(CHSV(random8(), random8(160, 255), random8(128, 255)), - CHSV(random8(), random8(160, 255), random8(128, 255)), - CHSV(random8(), random8(160, 255), random8(128, 255)), - CHSV(random8(), random8(160, 255), random8(128, 255))); + return CRGBPalette16(CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)), + CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)), + CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)), + CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255))); } -void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb +void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0) { - float h = ((float)hue)/10922.5f; // hue*6/65535 - float s = ((float)sat)/255.0f; - int i = int(h); - float f = h - i; - int p = int(255.0f * (1.0f-s)); - int q = int(255.0f * (1.0f-s*f)); - int t = int(255.0f * (1.0f-s*(1.0f-f))); - p = constrain(p, 0, 255); - q = constrain(q, 0, 255); - t = constrain(t, 0, 255); - switch (i%6) { - case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break; - case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break; - case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break; - case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break; - case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break; - case 5: rgb[0]=255,rgb[1]=p, rgb[2]=q; break; + unsigned int remainder, region, p, q, t; + unsigned int h = hsv.h; + unsigned int s = hsv.s; + unsigned int v = hsv.v; + if (s == 0) { + rgb = v << 16 | v << 8 | v; + return; } + region = h / 10923; // 65536 / 6 = 10923 + remainder = (h - (region * 10923)) * 6; + p = (v * (255 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 16))) >> 8; + t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8; + switch (region) { + case 0: + rgb = v << 16 | t << 8 | p; break; + case 1: + rgb = q << 16 | v << 8 | p; break; + case 2: + rgb = p << 16 | v << 8 | t; break; + case 3: + rgb = p << 16 | q << 8 | v; break; + case 4: + rgb = t << 16 | p << 8 | v; break; + default: + rgb = v << 16 | p << 8 | q; break; + } +} + +void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version +{ + hsv.raw = 0; + int32_t r = (rgb>>16)&0xFF; + int32_t g = (rgb>>8)&0xFF; + int32_t b = rgb&0xFF; + int32_t minval, maxval, delta; + minval = min(r, g); + minval = min(minval, b); + maxval = max(r, g); + maxval = max(maxval, b); + if (maxval == 0) return; // black + hsv.v = maxval; + delta = maxval - minval; + hsv.s = (255 * delta) / maxval; + if (hsv.s == 0) return; // gray value + if (maxval == r) hsv.h = (10923 * (g - b)) / delta; + else if (maxval == g) hsv.h = 21845 + (10923 * (b - r)) / delta; + else hsv.h = 43690 + (10923 * (r - g)) / delta; +} + +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb + uint32_t crgb; + hsv2rgb(CHSV32(hue, sat, 255), crgb); + rgb[0] = byte((crgb) >> 16); + rgb[1] = byte((crgb) >> 8); + rgb[2] = byte(crgb); } //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) @@ -332,7 +391,7 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www rgb[2] = byte(255.0f*b); } -void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) +void colorRGBtoXY(const byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) { float X = rgb[0] * 0.664511f + rgb[1] * 0.154324f + rgb[2] * 0.162028f; float Y = rgb[0] * 0.283881f + rgb[1] * 0.668433f + rgb[2] * 0.047685f; @@ -343,7 +402,7 @@ void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.develo #endif // WLED_DISABLE_HUESYNC //RRGGBB / WWRRGGBB order for hex -void colorFromDecOrHexString(byte* rgb, char* in) +void colorFromDecOrHexString(byte* rgb, const char* in) { if (in[0] == 0) return; char first = in[0]; @@ -452,24 +511,8 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { } } -//gamma 2.8 lookup table used for color correction -uint8_t NeoGammaWLEDMethod::gammaT[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, - 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, - 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, - 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, - 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, - 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, - 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, - 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, - 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, - 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, - 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, - 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; +// gamma lookup table used for color correction (filled on 1st use (cfg.cpp & set.cpp)) +uint8_t NeoGammaWLEDMethod::gammaT[256]; // re-calculates & fills gamma table void NeoGammaWLEDMethod::calcGammaTable(float gamma) diff --git a/wled00/const.h b/wled00/const.h index 07873deca..3f82219d3 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -5,7 +5,7 @@ * Readability defines and their associated numerical values + compile-time constants */ -#define GRADIENT_PALETTE_COUNT 58 +#define GRADIENT_PALETTE_COUNT 59 // You can define custom product info from build flags. // This is useful to allow API consumer to identify what type of WLED version @@ -203,6 +203,8 @@ #define USERMOD_ID_LD2410 52 //Usermod "usermod_ld2410.h" #define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h" #define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h" +#define USERMOD_ID_DEEP_SLEEP 55 //Usermod "usermod_deep_sleep.h" +#define USERMOD_ID_RF433 56 //Usermod "usermod_v2_RF433.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot @@ -248,6 +250,7 @@ #define REALTIME_MODE_ARTNET 6 #define REALTIME_MODE_TPM2NET 7 #define REALTIME_MODE_DDP 8 +#define REALTIME_MODE_DMX 9 //realtime override modes #define REALTIME_OVERRIDE_NONE 0 diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index b58c0987a..8fa715bc8 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -167,9 +167,10 @@
-
- Currently in use custom palettes -
+
+
+ Currently in use custom palettes +
@@ -187,7 +188,7 @@ Available static palettes - + @@ -204,6 +205,13 @@ var paletteName = []; // Holds the names of the palettes after load. var svgSave = '' var svgEdit = '' + var svgDist = '' + var svgTrash = '' + + const distDiv = gId("distDiv"); + distDiv.addEventListener('click', distribute); + distDiv.setAttribute('title', 'Distribute colors equally'); + distDiv.innerHTML = svgDist; function recOf() { rect = gradientBox.getBoundingClientRect(); @@ -433,7 +441,7 @@ renderY = e.srcElement.getBoundingClientRect().y + 13; trash.id = "trash"; - trash.innerHTML = ''; + trash.innerHTML = svgTrash; trash.style.position = "absolute"; trash.style.left = (renderX) + "px"; trash.style.top = (renderY) + "px"; @@ -712,9 +720,27 @@ } } + function distribute() { + let colorMarkers = [...gradientBox.querySelectorAll('.color-marker')]; + colorMarkers.sort((a, b) => a.getAttribute('data-truepos') - b.getAttribute('data-truepos')); + colorMarkers = colorMarkers.slice(1, -1); + const spacing = Math.round(256 / (colorMarkers.length + 1)); + + colorMarkers.forEach((e, i) => { + const markerId = e.id.match(/\d+/)[0]; + const trueCol = e.getAttribute("data-truecol"); + gradientBox.removeChild(e); + gradientBox.removeChild(gId(`colorPicker${markerId}`)); + gradientBox.removeChild(gId(`colorPickerMarker${markerId}`)); + gradientBox.removeChild(gId(`deleteMarker${markerId}`)); + addC(spacing * (i + 1), trueCol); + }); + } + function rgbToHex(r, g, b) { const hex = ((r << 16) | (g << 8) | b).toString(16); return "#" + "0".repeat(6 - hex.length) + hex; } + diff --git a/wled00/data/index.css b/wled00/data/index.css index 0e36ff08c..31e2daa92 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -97,6 +97,7 @@ button { .labels { margin: 0; padding: 8px 0 2px 0; + font-size: 19px; } #namelabel { @@ -890,12 +891,12 @@ a.btn { line-height: 28px; } -/* Quick color select Black button (has white border) */ -.qcsb { +/* Quick color select Black and White button (has white/black border, depending on the theme) */ +.qcsb, .qcsw { width: 26px; height: 26px; line-height: 26px; - border: 1px solid #fff; + border: 1px solid var(--c-f); } /* Hex color input wrapper div */ @@ -1299,6 +1300,14 @@ TD .checkmark, TD .radiomark { width: 100%; } +#segutil { + margin-bottom: 12px; +} + +#segcont > div:first-child, #fxFind { + margin-top: 4px; +} + /* Simplify segments */ .simplified #segcont .lstI { margin-top: 4px; @@ -1438,6 +1447,11 @@ dialog { position: relative; } +.presin { + width: 100%; + box-sizing: border-box; +} + .btn-s, .btn-n { border: 1px solid var(--c-2); diff --git a/wled00/data/index.htm b/wled00/data/index.htm index e74ea0076..8adec791f 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -106,7 +106,7 @@
-
+

diff --git a/wled00/data/index.js b/wled00/data/index.js index d9c64bdfb..1482c27b5 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -2827,7 +2827,7 @@ function search(field, listId = null) { // restore default preset sorting if no search term is entered if (!search) { - if (listId === 'pcont') { populatePresets(); return; } + if (listId === 'pcont') { populatePresets(); return; } if (listId === 'pallist') { let id = parseInt(d.querySelector('#pallist input[name="palette"]:checked').value); // preserve selected palette populatePalettes(); @@ -2846,12 +2846,16 @@ function search(field, listId = null) { // filter list items but leave (Default & Solid) always visible const listItems = gId(listId).querySelectorAll('.lstI'); - listItems.forEach((listItem,i)=>{ - if (listId!=='pcont' && i===0) return; + listItems.forEach((listItem, i) => { + if (listId !== 'pcont' && i === 0) return; const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase(); const searchIndex = listItemName.indexOf(field.value.toUpperCase()); - listItem.style.display = (searchIndex < 0) ? 'none' : ''; - listItem.dataset.searchIndex = searchIndex; + if (searchIndex < 0) { + listItem.dataset.searchIndex = Number.MAX_SAFE_INTEGER; + } else { + listItem.dataset.searchIndex = searchIndex; + } + listItem.style.display = (searchIndex < 0) && !listItem.classList.contains("selected") ? 'none' : ''; }); // sort list items by search index and name @@ -2920,11 +2924,11 @@ function filterFx() { inputField.value = ''; inputField.focus(); clean(inputField.nextElementSibling); - gId("fxlist").querySelectorAll('.lstI').forEach((listItem,i) => { + gId("fxlist").querySelectorAll('.lstI').forEach((listItem, i) => { const listItemName = listItem.querySelector('.lstIname').innerText; let hide = false; - gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i>0 /*true*/; }); - listItem.style.display = hide ? 'none' : ''; + gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i > 0 /*true*/; }); + listItem.style.display = hide && !listItem.classList.contains("selected") ? 'none' : ''; }); } diff --git a/wled00/data/settings_2D.htm b/wled00/data/settings_2D.htm index a70ca76be..e372b3e21 100644 --- a/wled00/data/settings_2D.htm +++ b/wled00/data/settings_2D.htm @@ -54,8 +54,8 @@ Orientation:
Serpentine:
Dimensions (WxH): x
-Offset X: -Y:
(offset from top-left corner in # LEDs) +Offset X: +Y:
(offset from top-left corner in # LEDs) `; p.insertAdjacentHTML("beforeend", b); } diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 6be5becd1..956cf7d90 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -6,7 +6,7 @@ LED Settings