Compare commits

..

31 Commits

Author SHA1 Message Date
Liliputech
da7f107273 fix POV Display usermod (#4427)
* POV Display usermod

this usermod adds a new effect called "POV Image".
To get it to work:
- read the README :)
- upload a bmp image to the ESP filesystem using "/edit" url.
- select "POV Image" effect.
- set the filename (ie: "/myimage.bmp") as segment name.
- rotate the segment at approximately 20 RPM.
- enjoy the show!
* improve file extension checks
* improve README, remove PNGdec reference, clean usermod
* restrain to esp32 platform + reduce memory footprint with malloc
2025-08-29 20:42:54 +02:00
Damian Schneider
d5d7fde30f use video scaling instead of NPB luminance & new ABL (#4798)
* updated color scaling to preserve hue at low brightness resulting in much better colors
* replace NPBlg with NPB, moved brightness scaling to bus manager
* improved gamma table calculation: fixed mismatch in inverting gamma table calculation: inversion should now be as good as it gets
* code cleanup, fixed gamma being applied in unnecessary places

Improvements to ABL handling:
- removed strip level handling, ist now all done on bus level
- limiter now respects pixel mapping
- proper handling of white channel
- improved current estimation
- current is now always correctly reported to UI
- minimal FPS impact if the ABL is not limiting but slighly higher impact for global ABL limit due to double-scaling

- moved brightness scaling to BusDigital
- created new header file colors.h to be able to access color functions in bus-manager.
- updated colo_fade() with better video scaling to preserve hue's at low brightness
- added IRAM_ATTR to color_fade (negligible speed impact when compared to inline and benefits other functions)
- added IRAM_ATTR to color_blend as it is used a lot throughout the code, did not test speed impact but adding it to color_fade made it almost on-par with an inlined function

Additional changes:
- fixes for properly handling `scaledBri()` (by @blazoncek)
- also use bit-shift instead of division in blending for ESP8266
- improvements for faster "softlight" calculation in blending
- changed some variables to uint8_t to maybe let the compiler optimize better, uint8_t can be faster if read, store and set are all done in uint8_t, which is the case in the ones I changed
- various minor code formatting changes
2025-08-29 17:12:10 +02:00
Damian Schneider
8aeb9e1abe bugfix in PS pointer alignment
- bug was leading to crashes when heap is low
2025-08-28 18:57:11 +02:00
Damian Schneider
cfad0b8a52 bugfix to prevent "almost infinite" loops in palette blend (#4841)
this fixes a very long loop when an overflow was happening in palette blending.
- reset prevPaletteBlends to prevent overflow
- add safety check in case overflow should still happen in another combination (or in future changes)
2025-08-28 18:08:31 +02:00
netmindz
f15c1fbca6 Merge pull request #4880 from wled/copilot/fix-4879
Fix pr-merge.yaml to include PR title and link in Discord notifications
2025-08-28 08:43:28 +01:00
copilot-swe-agent[bot]
708baf1ed7 Fix pr-merge.yaml to include PR title and link in Discord notifications
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2025-08-28 06:51:22 +00:00
copilot-swe-agent[bot]
4155a6bc23 Initial plan 2025-08-28 06:45:52 +00:00
netmindz
c92f0a9d90 Merge pull request #4878 from willmmiles/fix-pwm
Fix operation of length 1 strips, such as PWM LEDs
2025-08-28 06:31:39 +01:00
Will Miles
5fa901c37c Fix operation of length 1 strips, such as PWM LEDs
This reverts commit e5ba97bbe2.
2025-08-27 22:28:24 -04:00
netmindz
1fb9eb771e Merge pull request #4856 from wled/copilot/fix-4855
Fix GitHub workflow secret access from forked PRs
2025-08-21 00:23:02 +01:00
netmindz
dee581f58d Merge pull request #4858 from wled/copilot/fix-4857
Add comprehensive GitHub Copilot instructions for WLED development workflow
2025-08-21 00:18:41 +01:00
copilot-swe-agent[bot]
7943b00017 Add comprehensive GitHub Copilot instructions for WLED development
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2025-08-20 23:07:03 +00:00
copilot-swe-agent[bot]
cd8ddb81e1 Fix GitHub workflow secret access from forked PRs
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2025-08-20 22:58:29 +00:00
copilot-swe-agent[bot]
890860ebf6 Initial plan 2025-08-20 22:55:59 +00:00
copilot-swe-agent[bot]
624042d97e Initial plan 2025-08-20 22:54:33 +00:00
Will Miles
3b5c6ca284 Fix bootloop if config is reset (#4852)
* Fix bootloop if config missing/reset

Can't reset the config if there's nothing to reset!

* ESP8266: Commit ACTIONT_TRACKER

* Use consistent naming for backups and reset cfgs

Use 'rst.cfg.json' instead of 'cfg.json.rst.json' for configs that were
reset.

* Add a little more PSTR to bootloop handling
2025-08-20 07:37:14 +02:00
netmindz
dcc1fbc96e Merge pull request #4846 from Arcitec/improve-version-info
Make version information consistent across update interfaces
2025-08-19 09:20:21 +01:00
Arcitec
7865985eeb Make version information consistent across update interfaces
The duplication of logic and the formatting differences between the "OTA Updates" and "Security & Updates" pages made it very difficult to find the exact version details.

With this change, both update-pages now share the same consistent and detailed formatting, making it easy for users to identify which exact version and binary of WLED they've installed.

The version format has also been improved to make it much easier to understand.
2025-08-17 22:45:20 +02:00
netmindz
7285efebca Merge pull request #4836 from netmindz/V4-cleanup
Remove old V3 IDF
2025-08-16 14:29:27 +01:00
Will Tatam
af2d46c30d Set VERSION 2025-08-16 12:23:21 +01:00
Damian Schneider
f4d89c4196 add IDF V3 support for bootloop detection 2025-08-16 08:45:41 +02:00
Damian Schneider
c9c442a933 Bootloop detection & recovery (#4793)
* added boot loop detection and config backup
* automatic OTA rollback if loading backup does not fix it
* added new file handling functions
* adding verification of json files, added config restore at bootup if broken
* added function to compare contents of two files for future use (currently not used)
2025-08-15 20:43:04 +02:00
TripleWhy
b8b59b2bb1 Make rainbow effects more colorful (#3681)
* Make color_wheel rotate in HSV sapce instead of linearly interpolating R->G->B
* Remove the rainbow wheel option, as that is the same as the rainbow palette
* Use hsv2rgb for color_wheel

This is the current result of the discussion in https://github.com/wled/WLED/pull/3681
2025-08-15 20:19:18 +02:00
Will Tatam
c33e303323 Fix eth build, update lib_deps for v4 2025-08-14 17:56:17 +01:00
Will Tatam
caf3c7a2f9 Remove old V3 IDF 2025-08-14 17:50:21 +01:00
Will Tatam
c8d8ab020e ARDUINO_USB_CDC_ON_BOOT should not be in common esp32_idf_V4 2025-08-14 14:52:49 +01:00
Will Tatam
297d5ced75 Use esp32_idf_V4.build_flags and esp32_idf_V4.lib_deps for all V4 builds 2025-08-14 14:52:49 +01:00
Will Miles
3f90366aa8 Merge pull request #4796 from willmmiles/asynctcp-update
Update AsyncTCP (and AsyncWebServer)
2025-08-09 12:05:13 -04:00
Will Miles
f74d1459b9 Downtune AsyncTCP stack size
We downtuned the stack usage of AsyncTCP, and at some point in the
history of our fork, this got folded in to the default.  Re-apply the
stack size we've been using and recover that RAM.
2025-08-04 14:21:11 -04:00
Will Miles
e374c7ae55 Update to AsyncTCP 3.4.7
Bugfix on 3.4.6
2025-08-02 15:49:47 -04:00
Will Miles
9e4675ef46 Update AsyncWebServer and AsyncTCP
This should fix (or at least improve) some of the crash cases under
excessive web server load.
2025-07-30 22:17:44 -04:00
39 changed files with 1430 additions and 729 deletions

138
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,138 @@
# WLED - ESP32/ESP8266 LED Controller Firmware
WLED is a fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs and SPI-based chipsets. The project consists of C++ firmware for microcontrollers and a modern web interface.
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
## Working Effectively
### Initial Setup
- Install Node.js 20+ (specified in `.nvmrc`): Check your version with `node --version`
- Install dependencies: `npm install` (takes ~5 seconds)
- Install PlatformIO for hardware builds: `pip install -r requirements.txt` (takes ~60 seconds)
### Build and Test Workflow
- **ALWAYS build web UI first**: `npm run build` -- takes 3 seconds. NEVER CANCEL.
- **Run tests**: `npm test` -- takes 40 seconds. NEVER CANCEL. Set timeout to 2+ minutes.
- **Development mode**: `npm run dev` -- monitors file changes and auto-rebuilds web UI
- **Hardware firmware build**: `pio run -e [environment]` -- takes 15+ minutes. NEVER CANCEL. Set timeout to 30+ minutes.
### Build Process Details
The build has two main phases:
1. **Web UI Generation** (`npm run build`):
- Processes files in `wled00/data/` (HTML, CSS, JS)
- Minifies and compresses web content
- Generates `wled00/html_*.h` files with embedded web content
- **CRITICAL**: Must be done before any hardware build
2. **Hardware Compilation** (`pio run`):
- Compiles C++ firmware for various ESP32/ESP8266 targets
- Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`
- List all targets: `pio run --list-targets`
## Validation and Testing
### Web UI Testing
- **ALWAYS validate web UI changes manually**:
- Start local server: `cd wled00/data && python3 -m http.server 8080`
- Open `http://localhost:8080/index.htm` in browser
- Test basic functionality: color picker, effects, settings pages
- **Check for JavaScript errors** in browser console
### Code Validation
- **No automated linting configured** - follow existing code style in files you edit
- **Code style**: Use tabs for web files (.html/.css/.js), spaces (2 per level) for C++ files
- **C++ formatting available**: `clang-format` is installed but not in CI
- **Always run tests before finishing**: `npm test`
### Manual Testing Scenarios
After making changes to web UI, always test:
- **Load main interface**: Verify index.htm loads without errors
- **Navigation**: Test switching between main page and settings pages
- **Color controls**: Verify color picker and brightness controls work
- **Effects**: Test effect selection and parameter changes
- **Settings**: Test form submission and validation
## Common Tasks
### Repository Structure
```
wled00/ # Main firmware source (C++)
├── data/ # Web interface files
│ ├── index.htm # Main UI
│ ├── settings*.htm # Settings pages
│ └── *.js/*.css # Frontend resources
├── *.cpp/*.h # Firmware source files
└── html_*.h # Generated embedded web files (DO NOT EDIT)
tools/ # Build tools (Node.js)
├── cdata.js # Web UI build script
└── cdata-test.js # Test suite
platformio.ini # Hardware build configuration
package.json # Node.js dependencies and scripts
.github/workflows/ # CI/CD pipelines
```
### Key Files and Their Purpose
- `wled00/data/index.htm` - Main web interface
- `wled00/data/settings*.htm` - Configuration pages
- `tools/cdata.js` - Converts web files to C++ headers
- `wled00/wled.h` - Main firmware configuration
- `platformio.ini` - Hardware build targets and settings
### Development Workflow
1. **For web UI changes**:
- Edit files in `wled00/data/`
- Run `npm run build` to regenerate headers
- Test with local HTTP server
- Run `npm test` to validate build system
2. **For firmware changes**:
- Edit files in `wled00/` (but NOT `html_*.h` files)
- Ensure web UI is built first (`npm run build`)
- Build firmware: `pio run -e [target]`
- Flash to device: `pio run -e [target] --target upload`
3. **For both web and firmware**:
- Always build web UI first
- Test web interface manually
- Build and test firmware if making firmware changes
## Build Timing and Timeouts
- **Web UI build**: 3 seconds - Set timeout to 30 seconds minimum
- **Test suite**: 40 seconds - Set timeout to 2 minutes minimum
- **Hardware builds**: 15+ minutes - Set timeout to 30+ minutes minimum
- **NEVER CANCEL long-running builds** - PlatformIO downloads and compilation can take significant time
## Troubleshooting
### Common Issues
- **Build fails with missing html_*.h**: Run `npm run build` first
- **Web UI looks broken**: Check browser console for JavaScript errors
- **PlatformIO network errors**: Try again, downloads can be flaky
- **Node.js version issues**: Ensure Node.js 20+ is installed (check `.nvmrc`)
### When Things Go Wrong
- **Clear generated files**: `rm -f wled00/html_*.h` then rebuild
- **Force web UI rebuild**: `npm run build -- --force` or `npm run build -- -f`
- **Clean PlatformIO cache**: `pio run --target clean`
- **Reinstall dependencies**: `rm -rf node_modules && npm install`
## Important Notes
- **DO NOT edit `wled00/html_*.h` files** - they are auto-generated
- **Always commit both source files AND generated html_*.h files**
- **Web UI must be built before firmware compilation**
- **Test web interface manually after any web UI changes**
- **Use VS Code with PlatformIO extension for best development experience**
- **Hardware builds require appropriate ESP32/ESP8266 development board**
## CI/CD Pipeline
The GitHub Actions workflow:
1. Installs Node.js and Python dependencies
2. Runs `npm test` to validate build system
3. Builds web UI with `npm run build`
4. Compiles firmware for multiple hardware targets
5. Uploads build artifacts
Match this workflow in your local development to ensure CI success.

View File

@@ -40,7 +40,10 @@ jobs:
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci
- run: |
npm ci
VERSION=`date +%y%m%d0`
sed -i -r -e "s/define VERSION .+/define VERSION $VERSION/" wled00/wled.h
- name: Cache PlatformIO
uses: actions/cache@v4
with:

View File

@@ -1,12 +1,13 @@
name: Notify Discord on PR Merge
on:
workflow_dispatch:
pull_request:
pull_request_target:
types: [closed]
jobs:
notify:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- name: Get User Permission
id: checkAccess
@@ -23,11 +24,15 @@
echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}"
echo "Job originally triggered by ${{ github.actor }}"
exit 1
- name: Checkout code
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # This is dangerous without the first access check
- name: Send Discord notification
# if: github.event.pull_request.merged == true
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
ACTOR: ${{ github.actor }}
run: |
curl -H "Content-Type: application/json" -d '{"content": "Pull Request ${{ github.event.pull_request.number }} merged by ${{ github.actor }}"}' ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
jq -n \
--arg content "Pull Request #${PR_NUMBER} \"${PR_TITLE}\" merged by ${ACTOR}
${PR_URL}" \
'{content: $content}' \
| curl -H "Content-Type: application/json" -d @- ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}

View File

@@ -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, esp32dev_V4, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover, usermods
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, usermods
src_dir = ./wled00
data_dir = ./wled00/data
@@ -142,7 +142,7 @@ lib_deps =
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.8.3
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2
# for I2C interface
;Wire
# ESP-NOW library
@@ -234,25 +234,20 @@ lib_deps_compat =
[esp32_all_variants]
lib_deps =
willmmiles/AsyncTCP @ 1.3.1
esp32async/AsyncTCP @ 3.4.7
bitbank2/AnimatedGIF@^1.4.7
https://github.com/Aircoookie/GifDecoder#bc3af18
build_flags =
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-D WLED_ENABLE_GIF
[esp32]
#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
platform = ${esp32_idf_V4.platform}
platform_packages =
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
${esp32_all_variants.build_flags}
build_flags = ${esp32_idf_V4.build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
@@ -260,10 +255,7 @@ extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem, coredump support
large_partitions = tools/WLED_ESP32_8MB.csv
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
lib_deps =
https://github.com/lorol/LITTLEFS.git
${esp32_all_variants.lib_deps}
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
# additional build flags for audioreactive - must be applied globally
AR_build_flags = ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
@@ -271,8 +263,7 @@ AR_lib_deps = ;; for pre-usermod-library platformio_override compatibility
[esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
;; very similar to the normal ESP32 flags, but omitting Lorol LittleFS, as littlefs is included in the new framework already.
;; build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
;;
;; 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.
@@ -283,14 +274,12 @@ 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
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
${esp32_all_variants.build_flags}
-D WLED_ENABLE_DMX_INPUT
lib_deps =
${esp32_all_variants.lib_deps}
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
@@ -305,10 +294,9 @@ build_flags = -g
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
${esp32_all_variants.build_flags}
${esp32_idf_V4.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
${esp32_idf_V4.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32c3]
@@ -323,10 +311,9 @@ build_flags = -g
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
${esp32_all_variants.build_flags}
${esp32_idf_V4.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
${esp32_idf_V4.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
board_build.flash_mode = qio
@@ -343,10 +330,9 @@ build_flags = -g
-DCO
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
${esp32_all_variants.build_flags}
${esp32_idf_V4.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
${esp32_idf_V4.lib_deps}
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
@@ -441,21 +427,11 @@ custom_usermods = audioreactive
[env:esp32dev]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.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}
custom_usermods = audioreactive
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
@@ -489,23 +465,9 @@ board_upload.maximum_size = 16777216
board_build.f_flash = 80000000L
board_build.flash_mode = dio
;[env:esp32dev_audioreactive]
;board = esp32dev
;platform = ${esp32.platform}
;platform_packages = ${esp32.platform_packages}
;custom_usermods = audioreactive
;build_unflags = ${common.build_unflags}
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET
;lib_deps = ${esp32.lib_deps}
;monitor_filters = esp32_exception_decoder
;board_build.partitions = ${esp32.default_partitions}
;; board_build.f_flash = 80000000L
;; board_build.flash_mode = dio
[env:esp32_eth]
board = esp32-poe
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
platform = ${esp32_idf_V4.platform}
upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags}
@@ -513,10 +475,10 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"
; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = dio
[env:esp32_wrover]
extends = esp32_idf_V4
platform = ${esp32_idf_V4.platform}
board = ttgo-t7-v14-mini32
board_build.f_flash = 80000000L
board_build.flash_mode = qio

View File

@@ -28,7 +28,6 @@ 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
; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following
; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
build_unflags = ${common.build_unflags}

View File

@@ -0,0 +1,48 @@
## POV Display usermod
This usermod adds a new effect called “POV Image”.
![the usermod at work](pov_display.gif?raw=true)
###How does it work?
With proper configuration (see below) the main segment will display a single row of pixels from an image stored on the ESP.
It displays the image row by row at a high refresh rate.
If you move the pixel segment at the right speed, you will see the full image floating in the air thanks to the persistence of vision.
RGB LEDs only (no RGBW), with grouping set to 1 and spacing set to 0.
Best results with high-density strips (e.g., 144 LEDs/m).
To get it working:
- Resize your image. The height must match the number of LEDs in your strip/segment.
- Rotate your image 90° clockwise (height becomes width).
- Upload a BMP image (24-bit, uncompressed) to the ESP filesystem using the “/edit” URL.
- Select the “POV Image” effect.
- Set the segment name to the absolute filesystem path of the image (e.g., “/myimage.bmp”).
- The path is case-sensitive and must start with “/”.
- Rotate the pixel strip at approximately 20 RPM.
- Tune as needed so that one full revolution maps to the image width (if the image appears stretched or compressed, adjust RPM slightly).
- Enjoy the show!
Notes:
- Only 24-bit uncompressed BMP files are supported.
- The image must fit into ~64 KB of RAM (width × height × 3 bytes, plus row padding to a 4-byte boundary).
- Examples (approximate, excluding row padding):
- 128×128 (49,152 bytes) fits.
- 160×160 (76,800 bytes) does NOT fit.
- 96×192 (55,296 bytes) fits; padding may add a small overhead.
- If the rendered image appears mirrored or upsidedown, rotate 90° the other way or flip horizontally in your editor and try again.
- The path must be absolute.
### Requirements
- 1D rotating LED strip/segment (POV setup). Ensure the segment length equals the number of physical LEDs.
- BMP image saved as 24bit, uncompressed (no alpha, no palette).
- Sufficient free RAM (~64 KB) for the image buffer.
### Troubleshooting
- Nothing displays: verify the file exists at the exact absolute path (casesensitive) and is a 24bit uncompressed BMP.
- Garbled colors or wrong orientation: reexport as 24bit BMP and retry the rotation/flip guidance above.
- Image too large: reduce width and/or height until it fits within ~64 KB (see examples).
- Path issues: confirm you uploaded the file via the “/edit” URL and can see it in the filesystem browser.
### Safety
- Secure the rotating assembly and keep clear of moving parts.
- Balance the strip/hub to minimize vibration before running at speed.

View File

@@ -0,0 +1,146 @@
#include "bmpimage.h"
#define BUF_SIZE 64000
byte * _buffer = nullptr;
uint16_t read16(File &f) {
uint16_t result;
f.read((uint8_t *)&result,2);
return result;
}
uint32_t read32(File &f) {
uint32_t result;
f.read((uint8_t *)&result,4);
return result;
}
bool BMPimage::init(const char * fn) {
File bmpFile;
int bmpDepth;
//first, check if filename exists
if (!WLED_FS.exists(fn)) {
return false;
}
bmpFile = WLED_FS.open(fn);
if (!bmpFile) {
_valid=false;
return false;
}
//so, the file exists and is opened
// Parse BMP header
uint16_t header = read16(bmpFile);
if(header != 0x4D42) { // BMP signature
_valid=false;
bmpFile.close();
return false;
}
//read and ingnore file size
read32(bmpFile);
(void)read32(bmpFile); // Read & ignore creator bytes
_imageOffset = read32(bmpFile); // Start of image data
// Read DIB header
read32(bmpFile);
_width = read32(bmpFile);
_height = read32(bmpFile);
if(read16(bmpFile) != 1) { // # planes -- must be '1'
_valid=false;
bmpFile.close();
return false;
}
bmpDepth = read16(bmpFile); // bits per pixel
if((bmpDepth != 24) || (read32(bmpFile) != 0)) { // 0 = uncompressed {
_width=0;
_valid=false;
bmpFile.close();
return false;
}
// If _height is negative, image is in top-down order.
// This is not canon but has been observed in the wild.
if(_height < 0) {
_height = -_height;
}
//now, we have successfully got all the basics
// BMP rows are padded (if needed) to 4-byte boundary
_rowSize = (_width * 3 + 3) & ~3;
//check image size - if it is too large, it will be unusable
if (_rowSize*_height>BUF_SIZE) {
_valid=false;
bmpFile.close();
return false;
}
bmpFile.close();
// Ensure filename fits our buffer (segment name length constraint).
size_t len = strlen(fn);
if (len > WLED_MAX_SEGNAME_LEN) {
return false;
}
strncpy(filename, fn, sizeof(filename));
filename[sizeof(filename) - 1] = '\0';
_valid = true;
return true;
}
void BMPimage::clear(){
strcpy(filename, "");
_width=0;
_height=0;
_rowSize=0;
_imageOffset=0;
_loaded=false;
_valid=false;
}
bool BMPimage::load(){
const size_t size = (size_t)_rowSize * (size_t)_height;
if (size > BUF_SIZE) {
return false;
}
File bmpFile = WLED_FS.open(filename);
if (!bmpFile) {
return false;
}
if (_buffer != nullptr) free(_buffer);
_buffer = (byte*)malloc(size);
if (_buffer == nullptr) return false;
bmpFile.seek(_imageOffset);
const size_t readBytes = bmpFile.read(_buffer, size);
bmpFile.close();
if (readBytes != size) {
_loaded = false;
return false;
}
_loaded = true;
return true;
}
byte* BMPimage::line(uint16_t n){
if (_loaded) {
return (_buffer+n*_rowSize);
} else {
return NULL;
}
}
uint32_t BMPimage::pixelColor(uint16_t x, uint16_t y){
uint32_t pos;
byte b,g,r; //colors
if (! _loaded) {
return 0;
}
if ( (x>=_width) || (y>=_height) ) {
return 0;
}
pos=y*_rowSize + 3*x;
//get colors. Note that in BMP files, they go in BGR order
b= _buffer[pos++];
g= _buffer[pos++];
r= _buffer[pos];
return (r<<16|g<<8|b);
}

View File

@@ -0,0 +1,50 @@
#ifndef _BMPIMAGE_H
#define _BMPIMAGE_H
#include "Arduino.h"
#include "wled.h"
/*
* This class describes a bitmap image. Each object refers to a bmp file on
* filesystem fatfs.
* To initialize, call init(), passign to it name of a bitmap file
* at the root of fatfs filesystem:
*
* BMPimage myImage;
* myImage.init("logo.bmp");
*
* For performance reasons, before actually usign the image, you need to load
* it from filesystem to RAM:
* myImage.load();
* All load() operations use the same reserved buffer in RAM, so you can only
* have one file loaded at a time. Before loading a new file, always unload the
* previous one:
* myImage.unload();
*/
class BMPimage {
public:
int height() {return _height; }
int width() {return _width; }
int rowSize() {return _rowSize;}
bool isLoaded() {return _loaded; }
bool load();
void unload() {_loaded=false; }
byte * line(uint16_t n);
uint32_t pixelColor(uint16_t x,uint16_t y);
bool init(const char* fn);
void clear();
char * getFilename() {return filename;};
private:
char filename[WLED_MAX_SEGNAME_LEN+1]="";
int _width=0;
int _height=0;
int _rowSize=0;
int _imageOffset=0;
bool _loaded=false;
bool _valid=false;
};
extern byte * _buffer;
#endif

View File

@@ -1,7 +1,5 @@
{
"name:": "pov_display",
"build": { "libArchive": false},
"dependencies": {
"bitbank2/PNGdec":"^1.0.3"
}
"platforms": ["espressif32"]
}

View File

@@ -0,0 +1,47 @@
#include "pov.h"
POV::POV() {}
void POV::showLine(const byte * line, uint16_t size){
uint16_t i, pos;
uint8_t r, g, b;
if (!line) {
// All-black frame on null input
for (i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, CRGB::Black);
}
strip.show();
lastLineUpdate = micros();
return;
}
for (i = 0; i < SEGLEN; i++) {
if (i < size) {
pos = 3 * i;
// using bgr order
b = line[pos++];
g = line[pos++];
r = line[pos];
SEGMENT.setPixelColor(i, CRGB(r, g, b));
} else {
SEGMENT.setPixelColor(i, CRGB::Black);
}
}
strip.show();
lastLineUpdate = micros();
}
bool POV::loadImage(const char * filename){
if(!image.init(filename)) return false;
if(!image.load()) return false;
currentLine=0;
return true;
}
int16_t POV::showNextLine(){
if (!image.isLoaded()) return 0;
//move to next line
showLine(image.line(currentLine), image.width());
currentLine++;
if (currentLine == image.height()) {currentLine=0;}
return currentLine;
}

View File

@@ -0,0 +1,42 @@
#ifndef _POV_H
#define _POV_H
#include "bmpimage.h"
class POV {
public:
POV();
/* Shows one line. line should be pointer to array which holds pixel colors
* (3 bytes per pixel, in BGR order). Note: 3, not 4!!!
* size should be size of array (number of pixels, not number of bytes)
*/
void showLine(const byte * line, uint16_t size);
/* Reads from file an image and making it current image */
bool loadImage(const char * filename);
/* Show next line of active image
Retunrs the index of next line to be shown (not yet shown!)
If it retunrs 0, it means we have completed showing the image and
next call will start again
*/
int16_t showNextLine();
//time since strip was last updated, in micro sec
uint32_t timeSinceUpdate() {return (micros()-lastLineUpdate);}
BMPimage * currentImage() {return &image;}
char * getFilename() {return image.getFilename();}
private:
BMPimage image;
int16_t currentLine=0; //next line to be shown
uint32_t lastLineUpdate=0; //time in microseconds
};
#endif

View File

@@ -1,88 +1,75 @@
#include "wled.h"
#include <PNGdec.h>
#include "pov.h"
void * openFile(const char *filename, int32_t *size) {
f = WLED_FS.open(filename);
*size = f.size();
return &f;
}
static const char _data_FX_MODE_POV_IMAGE[] PROGMEM = "POV Image@!;;;;";
void closeFile(void *handle) {
if (f) f.close();
}
int32_t readFile(PNGFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
int32_t iBytesRead;
iBytesRead = iLen;
File *f = static_cast<File *>(pFile->fHandle);
// Note: If you read a file all the way to the last byte, seek() stops working
if ((pFile->iSize - pFile->iPos) < iLen)
iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
if (iBytesRead <= 0)
return 0;
iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
pFile->iPos = f->position();
return iBytesRead;
}
int32_t seekFile(PNGFILE *pFile, int32_t iPosition)
{
int i = micros();
File *f = static_cast<File *>(pFile->fHandle);
f->seek(iPosition);
pFile->iPos = (int32_t)f->position();
i = micros() - i;
return pFile->iPos;
}
void draw(PNGDRAW *pDraw) {
uint16_t usPixels[SEGLEN];
png.getLineAsRGB565(pDraw, usPixels, PNG_RGB565_LITTLE_ENDIAN, 0xffffffff);
for(int x=0; x < SEGLEN; x++) {
uint16_t color = usPixels[x];
byte r = ((color >> 11) & 0x1F);
byte g = ((color >> 5) & 0x3F);
byte b = (color & 0x1F);
SEGMENT.setPixelColor(x, RGBW32(r,g,b,0));
}
strip.show();
}
static POV s_pov;
uint16_t mode_pov_image(void) {
const char * filepath = SEGMENT.name;
int rc = png.open(filepath, openFile, closeFile, readFile, seekFile, draw);
if (rc == PNG_SUCCESS) {
rc = png.decode(NULL, 0);
png.close();
return FRAMETIME;
}
Segment& mainseg = strip.getMainSegment();
const char* segName = mainseg.name;
if (!segName) {
return FRAMETIME;
}
// Only proceed for files ending with .bmp (case-insensitive)
size_t segLen = strlen(segName);
if (segLen < 4) return FRAMETIME;
const char* ext = segName + (segLen - 4);
// compare case-insensitive to ".bmp"
if (!((ext[0]=='.') &&
(ext[1]=='b' || ext[1]=='B') &&
(ext[2]=='m' || ext[2]=='M') &&
(ext[3]=='p' || ext[3]=='P'))) {
return FRAMETIME;
}
const char* current = s_pov.getFilename();
if (current && strcmp(segName, current) == 0) {
s_pov.showNextLine();
return FRAMETIME;
}
static unsigned long s_lastLoadAttemptMs = 0;
unsigned long nowMs = millis();
// Retry at most twice per second if the image is not yet loaded.
if (nowMs - s_lastLoadAttemptMs < 500) return FRAMETIME;
s_lastLoadAttemptMs = nowMs;
s_pov.loadImage(segName);
return FRAMETIME;
}
class PovDisplayUsermod : public Usermod
{
public:
static const char _data_FX_MODE_POV_IMAGE[] PROGMEM = "POV Image@!;;;1";
class PovDisplayUsermod : public Usermod {
protected:
bool enabled = false; //WLEDMM
const char *_name; //WLEDMM
bool initDone = false; //WLEDMM
unsigned long lastTime = 0; //WLEDMM
public:
PNG png;
File f;
PovDisplayUsermod(const char *name, bool enabled)
: enabled(enabled) , _name(name) {}
void setup() override {
strip.addEffect(255, &mode_pov_image, _data_FX_MODE_POV_IMAGE);
//initDone removed (unused)
}
void setup() {
strip.addEffect(255, &mode_pov_image, _data_FX_MODE_POV_IMAGE);
void loop() override {
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!enabled || strip.isUpdating()) return;
// do your magic here
if (millis() - lastTime > 1000) {
lastTime = millis();
}
}
void loop() {
}
uint16_t getId()
{
return USERMOD_ID_POV_DISPLAY;
}
void connected() {}
uint16_t getId() override {
return USERMOD_ID_POV_DISPLAY;
}
};
static PovDisplayUsermod pov_display;
REGISTER_USERMOD(pov_display);
static PovDisplayUsermod pov_display("POV Display", false);
REGISTER_USERMOD(pov_display);

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 KiB

View File

@@ -7528,9 +7528,9 @@ uint16_t mode_2Ddistortionwaves() {
byte valueG = gdistort + ((a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 ))<<1);
byte valueB = bdistort + ((a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 ))<<1);
valueR = gamma8(cos8_t(valueR));
valueG = gamma8(cos8_t(valueG));
valueB = gamma8(cos8_t(valueB));
valueR = cos8_t(valueR);
valueG = cos8_t(valueG);
valueB = cos8_t(valueB);
if(SEGMENT.palette == 0) {
// use RGB values (original color mode)

116
wled00/FX_fcn.cpp Executable file → Normal file
View File

@@ -282,6 +282,7 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) {
_t->_oldSegment = new(std::nothrow) Segment(*this); // store/copy current segment settings
_t->_start = millis(); // restart countdown
_t->_dur = dur;
_t->_prevPaletteBlends = 0;
if (_t->_oldSegment) {
_t->_oldSegment->palette = _t->_palette; // restore original palette and colors (from start of transition)
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i];
@@ -368,6 +369,7 @@ void Segment::beginDraw(uint16_t prog) {
// minimum blend time is 100ms maximum is 65535ms
#ifndef WLED_SAVE_RAM
unsigned noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
if(noOfBlends > 255) noOfBlends = 255; // safety check
for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, Segment::_currentPalette, 48);
Segment::_currentPalette = _t->_palT; // copy transitioning/temporary palette
#else
@@ -1085,27 +1087,14 @@ void Segment::blur(uint8_t blur_amount, bool smear) const {
/*
* Put a value 0 to 255 in to get a color value.
* The colours are a transition r -> g -> b -> back to r
* Inspired by the Adafruit examples.
* Rotates the color in HSV space, where pos is H. (0=0deg, 256=360deg)
*/
uint32_t Segment::color_wheel(uint8_t pos) const {
if (palette) return color_from_palette(pos, false, false, 0); // never wrap palette
if (palette) return color_from_palette(pos, false, false, 0); // only wrap if "always wrap" is set
uint8_t w = W(getCurrentColor(0));
pos = 255 - pos;
if (useRainbowWheel) {
CRGB rgb;
hsv2rgb_rainbow(CHSV(pos, 255, 255), rgb);
return RGBW32(rgb.r, rgb.g, rgb.b, w);
} else {
if (pos < 85) {
return RGBW32((255 - pos * 3), 0, (pos * 3), w);
} else if (pos < 170) {
pos -= 85;
return RGBW32(0, (pos * 3), (255 - pos * 3), w);
} else {
pos -= 170;
return RGBW32((pos * 3), (255 - pos * 3), 0, w);
}
}
uint32_t rgb;
hsv2rgb(CHSV32(static_cast<uint16_t>(pos << 8), 255, 255), rgb);
return rgb | (w << 24); // add white channel
}
/*
@@ -1205,8 +1194,9 @@ void WS2812FX::finalizeInit() {
if (busEnd > _length) _length = busEnd;
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
bus->begin();
bus->setBrightness(bri);
bus->setBrightness(scaledBri(bri));
}
BusManager::initializeABL(); // init brightness limiter
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
Segment::maxWidth = _length;
@@ -1308,7 +1298,7 @@ static uint8_t _add (uint8_t a, uint8_t b) { unsigned t = a + b; return t
static uint8_t _subtract (uint8_t a, uint8_t b) { return b > a ? (b - a) : 0; }
static uint8_t _difference(uint8_t a, uint8_t b) { return b > a ? (b - a) : (a - b); }
static uint8_t _average (uint8_t a, uint8_t b) { return (a + b) >> 1; }
#ifdef CONFIG_IDF_TARGET_ESP32C3
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3)
static uint8_t _multiply (uint8_t a, uint8_t b) { return ((a * b) + 255) >> 8; } // faster than division on C3 but slightly less accurate
#else
static uint8_t _multiply (uint8_t a, uint8_t b) { return (a * b) / 255; } // origianl uses a & b in range [0,1]
@@ -1319,10 +1309,10 @@ static uint8_t _darken (uint8_t a, uint8_t b) { return a < b ? a : b; }
static uint8_t _screen (uint8_t a, uint8_t b) { return 255 - _multiply(~a,~b); } // 255 - (255-a)*(255-b)/255
static uint8_t _overlay (uint8_t a, uint8_t b) { return b < 128 ? 2 * _multiply(a,b) : (255 - 2 * _multiply(~a,~b)); }
static uint8_t _hardlight (uint8_t a, uint8_t b) { return a < 128 ? 2 * _multiply(a,b) : (255 - 2 * _multiply(~a,~b)); }
#ifdef CONFIG_IDF_TARGET_ESP32C3
static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a) + 255) >> 8) + 2 * a * b + 255) >> 8; } // Pegtop's formula (1 - 2a)b^2 + 2ab
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3)
static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a))) + ((2 * a * b + 256) << 8)) >> 16; } // Pegtop's formula (1 - 2a)b^2
#else
static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) / 255 + 2 * a * b) / 255; } // Pegtop's formula (1 - 2a)b^2 + 2ab
static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) + 255 * 2 * a * b) / (255 * 255); } // Pegtop's formula (1 - 2a)b^2 + 2ab
#endif
static uint8_t _dodge (uint8_t a, uint8_t b) { return _divide(~a,b); }
static uint8_t _burn (uint8_t a, uint8_t b) { return ~_divide(a,~b); }
@@ -1353,11 +1343,6 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
uint8_t opacity = topSegment.currentBri(); // returns transitioned opacity for style FADE
uint8_t cct = topSegment.currentCCT();
if (length == 1) {
// Can't blend only a single pixel, prevents crash when bus init fails
return;
}
Segment::setClippingRect(0, 0); // disable clipping by default
const unsigned dw = (blendingStyle==BLEND_STYLE_OUTSIDE_IN ? progInv : progress) * width / 0xFFFFU + 1;
@@ -1566,66 +1551,6 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
Segment::setClippingRect(0, 0); // disable clipping for overlays
}
// To disable brightness limiter we either set output max current to 0 or single LED current to 0
static uint8_t estimateCurrentAndLimitBri(uint8_t brightness, uint32_t *pixels) {
unsigned milliAmpsMax = BusManager::ablMilliampsMax();
if (milliAmpsMax > 0) {
unsigned milliAmpsTotal = 0;
unsigned avgMilliAmpsPerLED = 0;
unsigned lengthDigital = 0;
bool useWackyWS2815PowerModel = false;
for (size_t i = 0; i < BusManager::getNumBusses(); i++) {
const Bus *bus = BusManager::getBus(i);
if (!(bus && bus->isDigital() && bus->isOk())) continue;
unsigned maPL = bus->getLEDCurrent();
if (maPL == 0 || bus->getMaxCurrent() > 0) continue; // skip buses with 0 mA per LED or max current per bus defined (PP-ABL)
if (maPL == 255) {
useWackyWS2815PowerModel = true;
maPL = 12; // WS2815 uses 12mA per channel
}
avgMilliAmpsPerLED += maPL * bus->getLength();
lengthDigital += bus->getLength();
// sum up the usage of each LED on digital bus
uint32_t busPowerSum = 0;
for (unsigned j = 0; j < bus->getLength(); j++) {
uint32_t c = pixels[j + bus->getStart()];
byte r = R(c), g = G(c), b = B(c), w = W(c);
if (useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
busPowerSum += (max(max(r,g),b)) * 3;
} else {
busPowerSum += (r + g + b + w);
}
}
// RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
if (bus->hasWhite()) {
busPowerSum *= 3;
busPowerSum >>= 2; //same as /= 4
}
// powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
milliAmpsTotal += (busPowerSum * maPL * brightness) / (765*255);
}
if (lengthDigital > 0) {
avgMilliAmpsPerLED /= lengthDigital;
if (milliAmpsMax > MA_FOR_ESP && avgMilliAmpsPerLED > 0) { //0 mA per LED and too low numbers turn off calculation
unsigned powerBudget = (milliAmpsMax - MA_FOR_ESP); //80/120mA for ESP power
if (powerBudget > lengthDigital) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= lengthDigital;
} else {
powerBudget = 0;
}
if (milliAmpsTotal > powerBudget) {
//scale brightness down to stay in current limit
unsigned scaleB = powerBudget * 255 / milliAmpsTotal;
brightness = ((brightness * scaleB) >> 8) + 1;
}
}
}
}
return brightness;
}
void WS2812FX::show() {
if (!_pixels) return; // no pixels allocated, nothing to show
@@ -1653,10 +1578,6 @@ void WS2812FX::show() {
show_callback callback = _callback;
if (callback) callback(); // will call setPixelColor or setRealtimePixelColor
// determine ABL brightness
uint8_t newBri = estimateCurrentAndLimitBri(_brightness, _pixels);
if (newBri != _brightness) BusManager::setBrightness(newBri);
// paint actual pixels
int oldCCT = Bus::getCCT(); // store original CCT value (since it is global)
// when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1)
@@ -1667,7 +1588,11 @@ void WS2812FX::show() {
if (_pixelCCT) { // cctFromRgb already exluded at allocation
if (i == 0 || _pixelCCT[i-1] != _pixelCCT[i]) BusManager::setSegmentCCT(_pixelCCT[i], correctWB);
}
BusManager::setPixelColor(getMappedPixelIndex(i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32(_pixels[i]));
uint32_t c = _pixels[i]; // need a copy, do not modify _pixels directly (no byte access allowed on ESP32)
if(c > 0 && !(realtimeMode && arlsDisableGammaCorrection))
c = gamma32(c); // apply gamma correction if enabled note: applying gamma after brightness has too much color loss
BusManager::setPixelColor(getMappedPixelIndex(i), c);
}
Bus::setCCT(oldCCT); // restore old CCT for ABL adjustments
@@ -1679,9 +1604,6 @@ void WS2812FX::show() {
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
BusManager::show();
// restore brightness for next frame
if (newBri != _brightness) BusManager::setBrightness(_brightness);
if (diff > 0) { // skip calculation if no time has passed
size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math
_cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2) / (FPS_CALC_AVG + 1); // "+FPS_CALC_AVG/2" for proper rounding
@@ -1746,7 +1668,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
if (_brightness == 0) { //unfreeze all segments on power off
for (const Segment &seg : _segments) seg.freeze = false; // freeze is mutable
}
BusManager::setBrightness(b);
BusManager::setBrightness(scaledBri(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

View File

@@ -1118,7 +1118,7 @@ bool initParticleSystem2D(ParticleSystem2D *&PartSys, uint32_t requestedsources,
allocsuccess = true;
break; // allocation succeeded
}
numparticles /= 2; // cut number of particles in half and try again
numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned
PSPRINTLN(F("PS 2D alloc failed, trying with less particles..."));
}
if (!allocsuccess) {
@@ -1815,7 +1815,7 @@ bool initParticleSystem1D(ParticleSystem1D *&PartSys, const uint32_t requestedso
allocsuccess = true;
break; // allocation succeeded
}
numparticles /= 2; // cut number of particles in half and try again
numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned
PSPRINTLN(F("PS 1D alloc failed, trying with less particles..."));
}
if (!allocsuccess) {

View File

@@ -22,6 +22,7 @@
#include "core_esp8266_waveform.h"
#endif
#include "const.h"
#include "colors.h"
#include "pin_manager.h"
#include "bus_manager.h"
#include "bus_wrapper.h"
@@ -144,6 +145,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }
if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; }
_frequencykHz = 0U;
_colorSum = 0;
_pins[0] = bc.pins[0];
if (is2Pin(bc.type)) {
if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
@@ -186,80 +188,62 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
//Stay safe with high amperage and have a reasonable safety margin!
//I am NOT to be held liable for burned down garages or houses!
// To disable brightness limiter we either set output max current to 0 or single LED current to 0
uint8_t BusDigital::estimateCurrentAndLimitBri() const {
bool useWackyWS2815PowerModel = false;
byte actualMilliampsPerLed = _milliAmpsPerLed;
if (_milliAmpsMax < MA_FOR_ESP/BusManager::getNumBusses() || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
return _bri;
}
// note on ABL implementation:
// ABL is set up in finalizeInit()
// scaled color channels are summed in BusDigital::setPixelColor()
// the used current is estimated and limited in BusManager::show()
// if limit is set too low, brightness is limited to 1 to at least show some light
// to disable brightness limiter for a bus, set LED current to 0
void BusDigital::estimateCurrent() {
uint32_t actualMilliampsPerLed = _milliAmpsPerLed;
if (_milliAmpsPerLed == 255) {
useWackyWS2815PowerModel = true;
// use wacky WS2815 power model, see WLED issue #549
_colorSum *= 3; // sum is sum of max value for each color, need to multiply by three to account for clrUnitsPerChannel being 3*255
actualMilliampsPerLed = 12; // from testing an actual strip
}
// _colorSum has all the values of color channels summed, max would be getLength()*(3*255 + (255 if hasWhite()): convert to milliAmps
uint32_t clrUnitsPerChannel = hasWhite() ? 4*255 : 3*255;
_milliAmpsTotal = ((uint64_t)_colorSum * actualMilliampsPerLed) / clrUnitsPerChannel + getLength(); // add 1mA standby current per LED to total (WS2812: ~0.7mA, WS2815: ~2mA)
}
unsigned powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power
if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= getLength();
} else {
powerBudget = 0;
}
void BusDigital::applyBriLimit(uint8_t newBri) {
// a newBri of 0 means calculate per-bus brightness limit
if (newBri == 0) {
if (_milliAmpsLimit == 0 || _milliAmpsTotal == 0) return; // ABL not used for this bus
newBri = 255;
uint32_t busPowerSum = 0;
for (unsigned i = 0; i < getLength(); i++) { //sum up the usage of each LED
uint32_t c = getPixelColor(i); // always returns original or restored color without brightness scaling
byte r = R(c), g = G(c), b = B(c), w = W(c);
if (useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
busPowerSum += (max(max(r,g),b)) * 3;
if (_milliAmpsLimit > getLength()) { // each LED uses about 1mA in standby
if (_milliAmpsTotal > _milliAmpsLimit) {
// scale brightness down to stay in current limit
newBri = ((uint32_t)_milliAmpsLimit * 255) / _milliAmpsTotal + 1; // +1 to avoid 0 brightness
_milliAmpsTotal = _milliAmpsLimit;
}
} else {
busPowerSum += (r + g + b + w);
newBri = 1; // limit too low, set brightness to 1, this will dim down all colors to minimum since we use video scaling
_milliAmpsTotal = getLength(); // estimate bus current as minimum
}
}
if (hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3;
busPowerSum >>= 2; //same as /= 4
}
// powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
BusDigital::_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * _bri) / (765*255);
uint8_t newBri = _bri;
if (BusDigital::_milliAmpsTotal > powerBudget) {
//scale brightness down to stay in current limit
unsigned scaleB = powerBudget * 255 / BusDigital::_milliAmpsTotal;
newBri = (_bri * scaleB) / 256 + 1;
BusDigital::_milliAmpsTotal = powerBudget;
//_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * newBri) / (765*255);
}
return newBri;
}
void BusDigital::show() {
BusDigital::_milliAmpsTotal = 0;
if (!_valid) return;
uint8_t cctWW = 0, cctCW = 0;
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere())
if (newBri < _bri) {
PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
if (newBri < 255) {
uint8_t cctWW = 0, cctCW = 0;
unsigned hwLen = _len;
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
for (unsigned i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri);
if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); // this will unfortunately corrupt (segment) CCT data on every bus
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder); // need to revert color order for correct color scaling and CCT calc in case white is swapped
uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, i, co);
c = color_fade(c, newBri, true); // apply additional dimming note: using inline version is a bit faster but overhead of getPixelColor() dominates the speed impact by far
if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, i, c, co, (cctCW<<8) | cctWW); // repaint all pixels with new brightness
}
}
_colorSum = 0; // reset for next frame
}
void BusDigital::show() {
if (!_valid) return;
PolyBus::show(_busPtr, _iType, _skip); // faster if buffer consistency is not important (no skipped LEDs)
// restore bus brightness to its original value
// this is done right after show, so this is only OK if LED updates are completed before show() returns
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, _bri);
}
bool BusDigital::canShow() const {
@@ -267,12 +251,6 @@ bool BusDigital::canShow() const {
return PolyBus::canShow(_busPtr, _iType);
}
void BusDigital::setBrightness(uint8_t b) {
if (_bri == b) return;
Bus::setBrightness(b);
PolyBus::setBrightness(_busPtr, _iType, b);
}
//If LEDs are skipped, it is possible to use the first as a status LED.
//TODO only show if no new show due in the next 50ms
void BusDigital::setStatusPixel(uint32_t c) {
@@ -286,13 +264,25 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
if (!_valid) return;
if (hasWhite()) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
c = color_fade(c, _bri, true); // apply brightness
if (BusManager::_useABL) {
// if using ABL, sum all color channels to estimate current and limit brightness in show()
uint8_t r = R(c), g = G(c), b = B(c);
if (_milliAmpsPerLed < 255) { // normal ABL
_colorSum += r + g + b + W(c);
} else { // wacky WS2815 power model, ignore white channel, use max of RGB (issue #549)
_colorSum += ((r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b));
}
}
if (_reversed) pix = _len - pix -1;
pix += _skip;
unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
const uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
unsigned pOld = pix;
pix = IC_INDEX_WS2812_1CH_3X(pix);
uint32_t cOld = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, pix, co),_bri);
uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co);
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
@@ -309,17 +299,17 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
}
// returns original color if global buffering is enabled, else returns lossly restored color from bus
// returns lossly restored color from bus
uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
if (!_valid) return 0;
if (_reversed) pix = _len - pix -1;
pix += _skip;
const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
const uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
unsigned r = R(c);
unsigned g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
unsigned b = _reversed ? G(c) : B(c);
uint8_t r = R(c);
uint8_t g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
uint8_t b = _reversed ? G(c) : B(c);
switch (pix % 3) { // get only the single channel
case 0: c = RGBW32(g, g, g, g); break;
case 1: c = RGBW32(r, r, r, r); break;
@@ -471,10 +461,7 @@ void BusPwm::setPixelColor(unsigned pix, uint32_t c) {
if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
}
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
uint8_t r = R(c), g = G(c), b = B(c), w = W(c);
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
@@ -649,10 +636,7 @@ BusOnOff::BusOnOff(const BusConfig &bc)
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);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
uint8_t r = R(c), g = G(c), b = B(c), w = W(c);
_data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
}
@@ -964,13 +948,13 @@ void BusManager::off() {
#ifdef ESP32_DATA_IDLE_HIGH
esp32RMTInvertIdle();
#endif
_gMilliAmpsUsed = 0; // reset, assume no LED idle current if relay is off
}
void BusManager::show() {
_gMilliAmpsUsed = 0;
applyABL(); // apply brightness limit, updates _gMilliAmpsUsed
for (auto &bus : busses) {
bus->show();
_gMilliAmpsUsed += bus->getUsedCurrent();
}
}
@@ -1003,6 +987,85 @@ bool BusManager::canAllShow() {
return true;
}
void BusManager::initializeABL() {
_useABL = false; // reset
if (_gMilliAmpsMax > 0) {
// check global brightness limit
for (auto &bus : busses) {
if (bus->isDigital() && bus->getLEDCurrent() > 0) {
_useABL = true; // at least one bus has valid LED current
return;
}
}
} else {
// check per bus brightness limit
unsigned numABLbuses = 0;
for (auto &bus : busses) {
if (bus->isDigital() && bus->getLEDCurrent() > 0 && bus->getMaxCurrent() > 0)
numABLbuses++; // count ABL enabled buses
}
if (numABLbuses > 0) {
_useABL = true; // at least one bus has ABL set
uint32_t ESPshare = MA_FOR_ESP / numABLbuses; // share of ESP current per ABL bus
for (auto &bus : busses) {
if (bus->isDigital()) {
BusDigital &busd = static_cast<BusDigital&>(*bus);
uint32_t busLength = busd.getLength();
uint32_t busDemand = busLength * busd.getLEDCurrent();
uint32_t busMax = busd.getMaxCurrent();
if (busMax > ESPshare) busMax -= ESPshare;
if (busMax < busLength) busMax = busLength; // give each LED 1mA, ABL will dim down to minimum
if (busDemand == 0) busMax = 0; // no LED current set, disable ABL for this bus
busd.setCurrentLimit(busMax);
}
}
}
}
}
void BusManager::applyABL() {
if (_useABL) {
unsigned milliAmpsSum = 0; // use temporary variable to always return a valid _gMilliAmpsUsed to UI
unsigned totalLEDs = 0;
for (auto &bus : busses) {
if (bus->isDigital() && bus->isOk()) {
BusDigital &busd = static_cast<BusDigital&>(*bus);
busd.estimateCurrent(); // sets _milliAmpsTotal, current is estimated for all buses even if they have the limit set to 0
if (_gMilliAmpsMax == 0)
busd.applyBriLimit(0); // apply per bus ABL limit, updates _milliAmpsTotal if limit reached
milliAmpsSum += busd.getUsedCurrent();
totalLEDs += busd.getLength(); // sum total number of LEDs for global Limit
}
}
// check global current limit and apply global ABL limit, total current is summed above
if (_gMilliAmpsMax > 0) {
uint8_t newBri = 255;
uint32_t globalMax = _gMilliAmpsMax > MA_FOR_ESP ? _gMilliAmpsMax - MA_FOR_ESP : 1; // subtract ESP current consumption, fully limit if too low
if (globalMax > totalLEDs) { // check if budget is larger than standby current
if (milliAmpsSum > globalMax) {
newBri = globalMax * 255 / milliAmpsSum + 1; // scale brightness down to stay in current limit, +1 to avoid 0 brightness
milliAmpsSum = globalMax; // update total used current
}
} else {
newBri = 1; // limit too low, set brightness to minimum
milliAmpsSum = totalLEDs; // estimate total used current as minimum
}
// apply brightness limit to each bus, if its 255 it will only reset _colorSum
for (auto &bus : busses) {
if (bus->isDigital() && bus->isOk()) {
BusDigital &busd = static_cast<BusDigital&>(*bus);
if (busd.getLEDCurrent() > 0) // skip buses with LED current set to 0
busd.applyBriLimit(newBri);
}
}
}
_gMilliAmpsUsed = milliAmpsSum;
}
else
_gMilliAmpsUsed = 0; // reset, we have no current estimation without ABL
}
ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; }
@@ -1018,3 +1081,4 @@ uint16_t BusDigital::_milliAmpsTotal = 0;
std::vector<std::unique_ptr<Bus>> BusManager::busses;
uint16_t BusManager::_gMilliAmpsUsed = 0;
uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT;
bool BusManager::_useABL = false;

View File

@@ -238,7 +238,6 @@ class BusDigital : public Bus {
void show() override;
bool canShow() const override;
void setBrightness(uint8_t b) override;
void setStatusPixel(uint32_t c) override;
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override;
void setColorOrder(uint8_t colorOrder) override;
@@ -250,6 +249,9 @@ 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 setCurrentLimit(uint16_t milliAmps) { _milliAmpsLimit = milliAmps; }
void estimateCurrent(); // estimate used current from summed colors
void applyBriLimit(uint8_t newBri);
size_t getBusSize() const override;
void begin() override;
void cleanup();
@@ -262,8 +264,10 @@ class BusDigital : public Bus {
uint8_t _pins[2];
uint8_t _iType;
uint16_t _frequencykHz;
uint8_t _milliAmpsPerLed;
uint16_t _milliAmpsMax;
uint8_t _milliAmpsPerLed;
uint16_t _milliAmpsLimit;
uint32_t _colorSum; // total color value for the bus, updated in setPixelColor(), used to estimate current
void *_busPtr;
static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show()
@@ -278,8 +282,6 @@ class BusDigital : public Bus {
}
return c;
}
uint8_t estimateCurrentAndLimitBri() const;
};
@@ -422,8 +424,8 @@ 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
// milliamps used by ESP (for power estimation)
// 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)
@@ -438,6 +440,7 @@ namespace BusManager {
//extern std::vector<Bus*> busses;
extern uint16_t _gMilliAmpsUsed;
extern uint16_t _gMilliAmpsMax;
extern bool _useABL;
#ifdef ESP32_DATA_IDLE_HIGH
void esp32RMTInvertIdle() ;
@@ -453,6 +456,8 @@ namespace BusManager {
//inline uint16_t ablMilliampsMax() { unsigned sum = 0; for (auto &bus : busses) sum += bus->getMaxCurrent(); return sum; }
inline uint16_t ablMilliampsMax() { return _gMilliAmpsMax; } // used for compatibility reasons (and enabling virtual global ABL)
inline void setMilliampsMax(uint16_t max) { _gMilliAmpsMax = max;}
void initializeABL(); // setup automatic brightness limiter parameters, call once after buses are initialized
void applyABL(); // apply automatic brightness limiter, global or per bus
void useParallelOutput(); // workaround for inaccessible PolyBus
bool hasParallelOutput(); // workaround for inaccessible PolyBus

View File

@@ -3,7 +3,7 @@
#define BusWrapper_h
//#define NPB_CONF_4STEP_CADENCE
#include "NeoPixelBusLg.h"
#include "NeoPixelBus.h"
//Hardware SPI Pins
#define P_8266_HS_MOSI 13
@@ -141,65 +141,65 @@
/*** ESP8266 Neopixel methods ***/
#ifdef ESP8266
//RGB
#define B_8266_U0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
#define B_8266_U0_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1
#define B_8266_U1_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2
#define B_8266_DM_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16)
//RGBW
#define B_8266_U0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio1
#define B_8266_U1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio2
#define B_8266_DM_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin)
#define B_8266_U0_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method> //4 chan, esp8266, gpio1
#define B_8266_U1_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method> //4 chan, esp8266, gpio2
#define B_8266_DM_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin)
//400Kbps
#define B_8266_U0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin)
#define B_8266_U0_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod> //3 chan, esp8266, bb (any pin)
//TM1814 (RGBW)
#define B_8266_U0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method, NeoGammaNullMethod>
#define B_8266_U1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method, NeoGammaNullMethod>
#define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod>
#define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod>
#define B_8266_U0_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method>
#define B_8266_U1_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method>
#define B_8266_DM_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method>
#define B_8266_BB_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method>
//TM1829 (RGB)
#define B_8266_U0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
#define B_8266_U1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
#define B_8266_DM_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
#define B_8266_BB_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
#define B_8266_U0_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method>
#define B_8266_U1_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method>
#define B_8266_DM_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266DmaTm1829Method>
#define B_8266_BB_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266BitBangTm1829Method>
//UCS8903
#define B_8266_U0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
#define B_8266_U0_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1
#define B_8266_U1_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2
#define B_8266_DM_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266Dma800KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16)
//UCS8904 RGBW
#define B_8266_U0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio1
#define B_8266_U1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio2
#define B_8266_DM_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin)
#define B_8266_U0_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266Uart0Ws2813Method> //4 chan, esp8266, gpio1
#define B_8266_U1_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266Uart1Ws2813Method> //4 chan, esp8266, gpio2
#define B_8266_DM_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266Dma800KbpsMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin)
//APA106
#define B_8266_U0_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart0Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart1Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266DmaApa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
#define B_8266_U0_APA106_3 NeoPixelBus<NeoRbgFeature, NeoEsp8266Uart0Apa106Method> //3 chan, esp8266, gpio1
#define B_8266_U1_APA106_3 NeoPixelBus<NeoRbgFeature, NeoEsp8266Uart1Apa106Method> //3 chan, esp8266, gpio2
#define B_8266_DM_APA106_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266DmaApa106Method> //3 chan, esp8266, gpio3
#define B_8266_BB_APA106_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBangApa106Method> //3 chan, esp8266, bb (any pin but 16)
//FW1906 GRBCW
#define B_8266_U0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //esp8266, gpio1
#define B_8266_U1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //esp8266, gpio2
#define B_8266_DM_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //esp8266, gpio3
#define B_8266_BB_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //esp8266, bb
#define B_8266_U0_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266Uart0Ws2813Method> //esp8266, gpio1
#define B_8266_U1_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266Uart1Ws2813Method> //esp8266, gpio2
#define B_8266_DM_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266Dma800KbpsMethod> //esp8266, gpio3
#define B_8266_BB_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266BitBang800KbpsMethod> //esp8266, bb
//WS2805 GRBCW
#define B_8266_U0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart0Ws2805Method, NeoGammaNullMethod> //esp8266, gpio1
#define B_8266_U1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method, NeoGammaNullMethod> //esp8266, gpio2
#define B_8266_DM_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method, NeoGammaNullMethod> //esp8266, gpio3
#define B_8266_BB_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method, NeoGammaNullMethod> //esp8266, bb
#define B_8266_U0_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266Uart0Ws2805Method> //esp8266, gpio1
#define B_8266_U1_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method> //esp8266, gpio2
#define B_8266_DM_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method> //esp8266, gpio3
#define B_8266_BB_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method> //esp8266, bb
//TM1914 (RGB)
#define B_8266_U0_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart0Tm1914Method, NeoGammaNullMethod>
#define B_8266_U1_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart1Tm1914Method, NeoGammaNullMethod>
#define B_8266_DM_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266DmaTm1914Method, NeoGammaNullMethod>
#define B_8266_BB_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266BitBangTm1914Method, NeoGammaNullMethod>
#define B_8266_U0_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266Uart0Tm1914Method>
#define B_8266_U1_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266Uart1Tm1914Method>
#define B_8266_DM_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266DmaTm1914Method>
#define B_8266_BB_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266BitBangTm1914Method>
//Sm16825 (RGBWC)
#define B_8266_U0_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod>
#define B_8266_U1_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod>
#define B_8266_DM_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod>
#define B_8266_BB_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266BitBangWs2813Method, NeoGammaNullMethod>
#define B_8266_U0_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266Uart0Ws2813Method>
#define B_8266_U1_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266Uart1Ws2813Method>
#define B_8266_DM_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266Dma800KbpsMethod>
#define B_8266_BB_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266BitBangWs2813Method>
#endif
/*** ESP32 Neopixel methods ***/
@@ -245,84 +245,84 @@
#endif
//RGB
#define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> // ESP32, S2, S3, C3
//#define B_32_IN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2sNWs2812xMethod, NeoGammaNullMethod> // ESP32 (dynamic I2S selection)
#define B_32_I2_NEO_3 NeoPixelBusLg<NeoGrbFeature, X1Ws2812xMethod, NeoGammaNullMethod> // ESP32, S2, S3 (automatic I2S selection, see typedef above)
#define B_32_IP_NEO_3 NeoPixelBusLg<NeoGrbFeature, X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S (ESP32, S2, S3)
#define B_32_RN_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod> // ESP32, S2, S3, C3
//#define B_32_IN_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp32I2sNWs2812xMethod> // ESP32 (dynamic I2S selection)
#define B_32_I2_NEO_3 NeoPixelBus<NeoGrbFeature, X1Ws2812xMethod> // ESP32, S2, S3 (automatic I2S selection, see typedef above)
#define B_32_IP_NEO_3 NeoPixelBus<NeoGrbFeature, X8Ws2812xMethod> // parallel I2S (ESP32, S2, S3)
//RGBW
#define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNSk6812Method, NeoGammaNullMethod>
#define B_32_I2_NEO_4 NeoPixelBusLg<NeoGrbwFeature, X1Sk6812Method, NeoGammaNullMethod>
#define B_32_IP_NEO_4 NeoPixelBusLg<NeoGrbwFeature, X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp32RmtNSk6812Method>
#define B_32_I2_NEO_4 NeoPixelBus<NeoGrbwFeature, X1Sk6812Method>
#define B_32_IP_NEO_4 NeoPixelBus<NeoGrbwFeature, X8Sk6812Method> // parallel I2S
//400Kbps
#define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod>
#define B_32_I2_400_3 NeoPixelBusLg<NeoGrbFeature, X1400KbpsMethod, NeoGammaNullMethod>
#define B_32_IP_400_3 NeoPixelBusLg<NeoGrbFeature, X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod>
#define B_32_I2_400_3 NeoPixelBus<NeoGrbFeature, X1400KbpsMethod>
#define B_32_IP_400_3 NeoPixelBus<NeoGrbFeature, X8400KbpsMethod> // parallel I2S
//TM1814 (RGBW)
#define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod>
#define B_32_I2_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, X1Tm1814Method, NeoGammaNullMethod>
#define B_32_IP_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method>
#define B_32_I2_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, X1Tm1814Method>
#define B_32_IP_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, X8Tm1814Method> // parallel I2S
//TM1829 (RGB)
#define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod>
#define B_32_I2_TM2_3 NeoPixelBusLg<NeoBrgFeature, X1Tm1829Method, NeoGammaNullMethod>
#define B_32_IP_TM2_3 NeoPixelBusLg<NeoBrgFeature, X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp32RmtNTm1829Method>
#define B_32_I2_TM2_3 NeoPixelBus<NeoBrgFeature, X1Tm1829Method>
#define B_32_IP_TM2_3 NeoPixelBus<NeoBrgFeature, X8Tm1829Method> // parallel I2S
//UCS8903
#define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#define B_32_I2_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, X1800KbpsMethod, NeoGammaNullMethod>
#define B_32_IP_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod>
#define B_32_I2_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, X1800KbpsMethod>
#define B_32_IP_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, X8800KbpsMethod> // parallel I2S
//UCS8904
#define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#define B_32_I2_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, X1800KbpsMethod, NeoGammaNullMethod>
#define B_32_IP_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
#define B_32_RN_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod>
#define B_32_I2_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, X1800KbpsMethod>
#define B_32_IP_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, X8800KbpsMethod>// parallel I2S
//APA106
#define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod>
#define B_32_I2_APA106_3 NeoPixelBusLg<NeoGrbFeature, X1Apa106Method, NeoGammaNullMethod>
#define B_32_IP_APA106_3 NeoPixelBusLg<NeoGrbFeature, X8Apa106Method, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_APA106_3 NeoPixelBus<NeoGrbFeature, NeoEsp32RmtNApa106Method>
#define B_32_I2_APA106_3 NeoPixelBus<NeoGrbFeature, X1Apa106Method>
#define B_32_IP_APA106_3 NeoPixelBus<NeoGrbFeature, X8Apa106Method> // parallel I2S
//FW1906 GRBCW
#define B_32_RN_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#define B_32_I2_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, X1800KbpsMethod, NeoGammaNullMethod>
#define B_32_IP_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod>
#define B_32_I2_FW6_5 NeoPixelBus<NeoGrbcwxFeature, X1800KbpsMethod>
#define B_32_IP_FW6_5 NeoPixelBus<NeoGrbcwxFeature, X8800KbpsMethod> // parallel I2S
//WS2805 RGBWC
#define B_32_RN_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method, NeoGammaNullMethod>
#define B_32_I2_2805_5 NeoPixelBusLg<NeoGrbwwFeature, X1Ws2805Method, NeoGammaNullMethod>
#define B_32_IP_2805_5 NeoPixelBusLg<NeoGrbwwFeature, X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method>
#define B_32_I2_2805_5 NeoPixelBus<NeoGrbwwFeature, X1Ws2805Method>
#define B_32_IP_2805_5 NeoPixelBus<NeoGrbwwFeature, X8Ws2805Method> // parallel I2S
//TM1914 (RGB)
#define B_32_RN_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method, NeoGammaNullMethod>
#define B_32_I2_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, X1Tm1914Method, NeoGammaNullMethod>
#define B_32_IP_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, X8Tm1914Method, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_TM1914_3 NeoPixelBus<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method>
#define B_32_I2_TM1914_3 NeoPixelBus<NeoGrbTm1914Feature, X1Tm1914Method>
#define B_32_IP_TM1914_3 NeoPixelBus<NeoGrbTm1914Feature, X8Tm1914Method> // parallel I2S
//Sm16825 (RGBWC)
#define B_32_RN_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#define B_32_I2_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, X1Ws2812xMethod, NeoGammaNullMethod>
#define B_32_IP_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
#define B_32_RN_SM16825_5 NeoPixelBus<NeoRgbcwSm16825eFeature, NeoEsp32RmtNWs2812xMethod>
#define B_32_I2_SM16825_5 NeoPixelBus<NeoRgbcwSm16825eFeature, X1Ws2812xMethod>
#define B_32_IP_SM16825_5 NeoPixelBus<NeoRgbcwSm16825eFeature, X8Ws2812xMethod> // parallel I2S
#endif
//APA102
#ifdef WLED_USE_ETHERNET
// fix for #2542 (by @BlackBird77)
#define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarEsp32HspiHzMethod, NeoGammaNullMethod> //hardware HSPI (was DotStarEsp32DmaHspi5MhzMethod in NPB @ 2.6.9)
#define B_HS_DOT_3 NeoPixelBus<DotStarBgrFeature, DotStarEsp32HspiHzMethod> //hardware HSPI (was DotStarEsp32DmaHspi5MhzMethod in NPB @ 2.6.9)
#else
#define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarSpiHzMethod, NeoGammaNullMethod> //hardware VSPI
#define B_HS_DOT_3 NeoPixelBus<DotStarBgrFeature, DotStarSpiHzMethod> //hardware VSPI
#endif
#define B_SS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarMethod, NeoGammaNullMethod> //soft SPI
#define B_SS_DOT_3 NeoPixelBus<DotStarBgrFeature, DotStarMethod> //soft SPI
//LPD8806
#define B_HS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806SpiHzMethod, NeoGammaNullMethod>
#define B_SS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806Method, NeoGammaNullMethod>
#define B_HS_LPD_3 NeoPixelBus<Lpd8806GrbFeature, Lpd8806SpiHzMethod>
#define B_SS_LPD_3 NeoPixelBus<Lpd8806GrbFeature, Lpd8806Method>
//LPD6803
#define B_HS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803SpiHzMethod, NeoGammaNullMethod>
#define B_SS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803Method, NeoGammaNullMethod>
#define B_HS_LPO_3 NeoPixelBus<Lpd6803GrbFeature, Lpd6803SpiHzMethod>
#define B_SS_LPO_3 NeoPixelBus<Lpd6803GrbFeature, Lpd6803Method>
//WS2801
#ifdef WLED_USE_ETHERNET
#define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801MethodBase<TwoWireHspiImple<SpiSpeedHz>>, NeoGammaNullMethod>
#define B_HS_WS1_3 NeoPixelBus<NeoRbgFeature, Ws2801MethodBase<TwoWireHspiImple<SpiSpeedHz>>>
#else
#define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801SpiHzMethod, NeoGammaNullMethod>
#define B_HS_WS1_3 NeoPixelBus<NeoRbgFeature, Ws2801SpiHzMethod>
#endif
#define B_SS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801Method, NeoGammaNullMethod>
#define B_SS_WS1_3 NeoPixelBus<NeoRbgFeature, Ws2801Method>
//P9813
#define B_HS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813SpiHzMethod, NeoGammaNullMethod>
#define B_SS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813Method, NeoGammaNullMethod>
#define B_HS_P98_3 NeoPixelBus<P9813BgrFeature, P9813SpiHzMethod>
#define B_SS_P98_3 NeoPixelBus<P9813BgrFeature, P9813Method>
// 48bit & 64bit to 24bit & 32bit RGB(W) conversion
#define toRGBW32(c) (RGBW32((c>>40)&0xFF, (c>>24)&0xFF, (c>>8)&0xFF, (c>>56)&0xFF))
@@ -896,102 +896,6 @@ class PolyBus {
}
}
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_APA106_3: (static_cast<B_8266_U0_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_FW6_5: (static_cast<B_8266_U0_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_FW6_5: (static_cast<B_8266_U1_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_FW6_5: (static_cast<B_8266_DM_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_FW6_5: (static_cast<B_8266_BB_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_2805_5: (static_cast<B_8266_U0_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_SM16825_5: (static_cast<B_8266_U0_SM16825_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_SM16825_5: (static_cast<B_8266_U1_SM16825_5*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_SM16825_5: (static_cast<B_8266_DM_SM16825_5*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_SM16825_5: (static_cast<B_8266_BB_SM16825_5*>(busPtr))->SetLuminance(b); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
// RMT buses
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->SetLuminance(b); break;
// I2S1 bus or paralell buses
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast<B_32_IP_NEO_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast<B_32_IP_NEO_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_32_I2_400_3: if (_useParallelI2S) (static_cast<B_32_IP_400_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_400_3*>(busPtr))->SetLuminance(b); break;
case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast<B_32_IP_TM1_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast<B_32_IP_TM2_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast<B_32_IP_UCS_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast<B_32_IP_UCS_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast<B_32_IP_APA106_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_APA106_3*>(busPtr))->SetLuminance(b); break;
case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast<B_32_IP_FW6_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_FW6_5*>(busPtr))->SetLuminance(b); break;
case I_32_I2_2805_5: if (_useParallelI2S) (static_cast<B_32_IP_2805_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_2805_5*>(busPtr))->SetLuminance(b); break;
case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast<B_32_IP_TM1914_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast<B_32_IP_SM16825_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_SM16825_5*>(busPtr))->SetLuminance(b); break;
#endif
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); break;
}
}
[[gnu::hot]] static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
RgbwColor col(0,0,0,0);
switch (busType) {

View File

@@ -519,7 +519,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(briMultiplier, light[F("scale-bri")]);
CJSON(paletteBlend, light[F("pal-mode")]);
CJSON(strip.autoSegments, light[F("aseg")]);
CJSON(useRainbowWheel, light[F("rw")]);
CJSON(gammaCorrectVal, light["gc"]["val"]); // default 2.2
float light_gc_bri = light["gc"]["bri"];
@@ -773,9 +772,32 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
return (doc["sv"] | true);
}
static const char s_cfg_json[] PROGMEM = "/cfg.json";
bool backupConfig() {
return backupFile(s_cfg_json);
}
bool restoreConfig() {
return restoreFile(s_cfg_json);
}
bool verifyConfig() {
return validateJsonFile(s_cfg_json);
}
// rename config file and reboot
// if the cfg file doesn't exist, such as after a reset, do nothing
void resetConfig() {
if (WLED_FS.exists(s_cfg_json)) {
DEBUG_PRINTLN(F("Reset config"));
char backupname[32];
snprintf_P(backupname, sizeof(backupname), PSTR("/rst.%s"), &s_cfg_json[1]);
WLED_FS.rename(s_cfg_json, backupname);
doReboot = true;
}
}
bool deserializeConfigFromFS() {
[[maybe_unused]] bool success = deserializeConfigSec();
#ifdef WLED_ADD_EEPROM_SUPPORT
@@ -801,6 +823,7 @@ bool deserializeConfigFromFS() {
void serializeConfigToFS() {
serializeConfigSec();
backupConfig(); // backup before writing new config
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
@@ -1042,7 +1065,6 @@ void serializeConfig(JsonObject root) {
light[F("scale-bri")] = briMultiplier;
light[F("pal-mode")] = paletteBlend;
light[F("aseg")] = strip.autoSegments;
light[F("rw")] = useRainbowWheel;
JsonObject light_gc = light.createNestedObject("gc");
light_gc["bri"] = (gammaCorrectBri) ? gammaCorrectVal : 1.0f; // keep compatibility

View File

@@ -8,7 +8,7 @@
* 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, uint8_t blend) {
uint32_t IRAM_ATTR 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
const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; // mask for R and B channels or W and G if negated (poorman's SIMD; https://github.com/wled/WLED/pull/4568#discussion_r1986587221)
uint32_t rb1 = color1 & TWO_CHANNEL_MASK; // extract R & B channels from color1
@@ -64,26 +64,26 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR)
* fades color toward black
* if using "video" method the resulting color will never become black unless it is already black
*/
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
{
uint32_t IRAM_ATTR color_fade(uint32_t c1, uint8_t amount, bool video) {
if (c1 == 0 || amount == 0) return 0; // black or no change
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 scale = amount; // 32bit for faster calculation
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;
if (!video) amount++; // add one for correct scaling using bitshifts
else {
// video scaling: make sure colors do not dim to zero if they started non-zero unless they distort the hue
uint8_t r = byte(c1>>16), g = byte(c1>>8), b = byte(c1), w = byte(c1>>24); // extract r, g, b, w channels
uint8_t maxc = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); // determine dominant channel for hue preservation
uint8_t quarterMax = maxc >> 2; // note: using half of max results in color artefacts
addRemains = r && r > quarterMax ? 0x00010000 : 0;
addRemains |= g && g > quarterMax ? 0x00000100 : 0;
addRemains |= b && b > quarterMax ? 0x00000001 : 0;
addRemains |= w ? 0x01000000 : 0;
}
const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF;
uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * scale) >> 8) & TWO_CHANNEL_MASK; // scale red and blue
uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * scale) & ~TWO_CHANNEL_MASK; // scale white and green
scaledcolor = (rb | wg) + addRemains;
return scaledcolor;
uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * amount) >> 8) & TWO_CHANNEL_MASK; // scale red and blue
uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * amount) & ~TWO_CHANNEL_MASK; // scale white and green
return (rb | wg) + addRemains;
}
/*
@@ -92,7 +92,7 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
note: inputs are 32bit to speed up the function, useful input value ranges are 0-255
*/
uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten) {
if(rgb == 0 | hueShift + lighten + brighten == 0) return rgb; // black or no change
if (rgb == 0 | hueShift + lighten + brighten == 0) return rgb; // black or no change
CHSV32 hsv;
rgb2hsv(rgb, hsv); //convert to HSV
hsv.h += (hueShift << 8); // shift hue (hue is 16 bits)
@@ -104,8 +104,7 @@ uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_
}
// 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)
{
uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) {
if (blendType == LINEARBLEND_NOWRAP) {
index = (index * 0xF0) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping
}
@@ -120,16 +119,16 @@ uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t
else ++entry;
unsigned f2 = (lo4 << 4);
unsigned f1 = 256 - f2;
red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; // note: using color_blend() is 20% slower
red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; // note: using color_blend() is slower
green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8;
blue1 = (blue1 * f1 + (unsigned)entry->b * 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
// actually color_fade(c1, brightness)
// actually same as color_fade(), using color_fade() is slower
uint32_t scale = brightness + 1; // adjust for rounding (bitshift)
red1 = (red1 * scale) >> 8; // note: using color_fade() is 30% slower
red1 = (red1 * scale) >> 8;
green1 = (green1 * scale) >> 8;
blue1 = (blue1 * scale) >> 8;
blue1 = (blue1 * scale) >> 8;
}
return RGBW32(red1,green1,blue1,0);
}
@@ -589,10 +588,13 @@ uint8_t NeoGammaWLEDMethod::gammaT_inv[256];
void NeoGammaWLEDMethod::calcGammaTable(float gamma)
{
float gamma_inv = 1.0f / gamma; // inverse gamma
for (size_t i = 0; i < 256; i++) {
for (size_t i = 1; i < 256; i++) {
gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f);
gammaT_inv[i] = (int)(powf((float)i / 255.0f, gamma_inv) * 255.0f + 0.5f);
gammaT_inv[i] = (int)(powf(((float)i - 0.5f) / 255.0f, gamma_inv) * 255.0f + 0.5f);
//DEBUG_PRINTF_P(PSTR("gammaT[%d] = %d gammaT_inv[%d] = %d\n"), i, gammaT[i], i, gammaT_inv[i]);
}
gammaT[0] = 0;
gammaT_inv[0] = 0;
}
uint8_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct(uint8_t value)
@@ -601,21 +603,6 @@ uint8_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct(uint8_t value)
return gammaT[value];
}
// used for color gamma correction
uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct32(uint32_t color)
{
if (!gammaCorrectCol) return color;
uint8_t w = W(color);
uint8_t r = R(color);
uint8_t g = G(color);
uint8_t b = B(color);
w = gammaT[w];
r = gammaT[r];
g = gammaT[g];
b = gammaT[b];
return RGBW32(r, g, b, w);
}
uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::inverseGamma32(uint32_t color)
{
if (!gammaCorrectCol) return color;

144
wled00/colors.h Normal file
View File

@@ -0,0 +1,144 @@
#pragma once
#ifndef WLED_COLORS_H
#define WLED_COLORS_H
/*
* Color structs and color utility functions
*/
#include <vector>
#include "FastLED.h"
#define ColorFromPalette ColorFromPaletteWLED // override fastled version
// CRGBW can be used to manipulate 32bit colors faster. However: if it is passed to functions, it adds overhead compared to a uint32_t color
// use with caution and pay attention to flash size. Usually converting a uint32_t to CRGBW to extract r, g, b, w values is slower than using bitshifts
// it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB
struct CRGBW {
union {
uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB)
struct {
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t w;
};
uint8_t raw[4]; // Access as an array in the order B, G, R, W
};
// Default constructor
inline CRGBW() __attribute__((always_inline)) = default;
// Constructor from a 32-bit color (0xWWRRGGBB)
constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {}
// Constructor with r, g, b, w values
constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : b(blue), g(green), r(red), w(white) {}
// Constructor from CRGB
constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {}
// Access as an array
inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; }
// Assignment from 32-bit color
inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; }
// Assignment from r, g, b, w
inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { b = rgb.b; g = rgb.g; r = rgb.r; w = 0; return *this; }
// Conversion operator to uint32_t
inline operator uint32_t() const __attribute__((always_inline)) {
return color32;
}
/*
// Conversion operator to CRGB
inline operator CRGB() const __attribute__((always_inline)) {
return CRGB(r, g, b);
}
CRGBW& scale32 (uint8_t scaledown) // 32bit math
{
if (color32 == 0) return *this; // 2 extra instructions, worth it if called a lot on black (which probably is true) adding check if scaledown is zero adds much more overhead as its 8bit
uint32_t scale = scaledown + 1;
uint32_t rb = (((color32 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue
uint32_t wg = (((color32 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green
color32 = rb | wg;
return *this;
}*/
};
struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions
union {
struct {
uint16_t h; // hue
uint8_t s; // saturation
uint8_t v; // value
};
uint32_t raw; // 32bit access
};
inline CHSV32() __attribute__((always_inline)) = default; // default constructor
/// Allow construction from hue, saturation, and value
/// @param ih input hue
/// @param is input saturation
/// @param iv input value
inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v
: h(ih), s(is), v(iv) {}
inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v
: h((uint16_t)ih << 8), s(is), v(iv) {}
inline CHSV32(const CHSV& chsv) __attribute__((always_inline)) // constructor from CHSV
: h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {}
inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV
};
extern bool gammaCorrectCol;
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
class NeoGammaWLEDMethod {
public:
[[gnu::hot]] static uint8_t Correct(uint8_t value); // apply Gamma to single channel
[[gnu::hot]] static uint32_t inverseGamma32(uint32_t color); // apply inverse Gamma to RGBW32 color
static void calcGammaTable(float gamma); // re-calculates & fills gamma tables
static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; } // get value from Gamma table (WLED specific, not used by NPB)
static inline uint8_t rawInverseGamma8(uint8_t val) { return gammaT_inv[val]; } // get value from inverse Gamma table (WLED specific, not used by NPB)
static inline uint32_t Correct32(uint32_t color) { // apply Gamma to RGBW32 color (WLED specific, not used by NPB)
if (!gammaCorrectCol) return color; // no gamma correction
uint8_t w = byte(color>>24), r = byte(color>>16), g = byte(color>>8), b = byte(color); // extract r, g, b, w channels
w = gammaT[w]; r = gammaT[r]; g = gammaT[g]; b = gammaT[b];
return (uint32_t(w) << 24) | (uint32_t(r) << 16) | (uint32_t(g) << 8) | uint32_t(b);
}
private:
static uint8_t gammaT[];
static uint8_t gammaT_inv[];
};
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
#define gamma32inv(c) NeoGammaWLEDMethod::inverseGamma32(c)
#define gamma8inv(c) NeoGammaWLEDMethod::rawInverseGamma8(c)
[[gnu::hot, gnu::pure]] uint32_t color_blend(uint32_t c1, uint32_t c2 , uint8_t blend);
inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); };
[[gnu::hot, gnu::pure]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false);
[[gnu::hot, gnu::pure]] uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten);
[[gnu::hot, gnu::pure]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND);
CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette);
CRGBPalette16 generateRandomPalette();
void loadCustomPalettes();
extern std::vector<CRGBPalette16> customPalettes;
inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb);
void rgb2hsv(const uint32_t rgb, CHSV32& hsv);
inline CHSV rgb2hsv(const CRGB c) { CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); } // CRGB to hsv
void colorKtoRGB(uint16_t kelvin, byte* rgb);
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO
void colorRGBtoXY(const byte* rgb, float* xy); // only defined if huesync disabled TODO
void colorFromDecOrHexString(byte* rgb, const char* in);
bool colorFromHexString(byte* rgb, const char* in);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
[[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video = false);
#endif

View File

@@ -908,7 +908,6 @@ Swap: <select id="xw${s}" name="XW${s}">
<option value="3">None (not recommended)</option>
</select><br>
Use harmonic <i>Random Cycle</i> palette: <input type="checkbox" name="TH"><br>
Use &quot;rainbow&quot; color wheel: <input type="checkbox" name="RW"><br>
Target refresh rate: <input type="number" class="s" min="0" max="250" name="FR" oninput="UI()" required> FPS
<div id="fpsNone" class="warn" style="display: none;">&#9888; Unlimited FPS Mode is experimental &#9888;<br></div>
<div id="fpsHigh" class="warn" style="display: none;">&#9888; High FPS Mode is experimental.<br></div>

View File

@@ -76,7 +76,7 @@
A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2024 Christian Schwinne <br>
<i>Licensed under the <a href="https://github.com/wled-dev/WLED/blob/main/LICENSE" target="_blank">EUPL v1.2 license</a></i><br><br>
Server message: <span class="sip"> Response error! </span><hr>
Installed version: <span class="sip">WLED ##VERSION##</span><hr>
<div id="toast"></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>

View File

@@ -27,7 +27,7 @@
<body onload="GetV()">
<h2>WLED Software Update</h2>
<form method='POST' action='./update' id='upd' enctype='multipart/form-data' onsubmit="toggle('upd')">
Installed version: <span class="sip">##VERSION##</span><br>
Installed version: <span class="sip">WLED ##VERSION##</span><br>
Download the latest binary: <a href="https://github.com/wled-dev/WLED/releases" target="_blank"
style="vertical-align: text-bottom; display: inline-flex;">
<img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a><br>

View File

@@ -191,7 +191,7 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
// only change brightness if value changed
if (bri != e131_data[dataOffset]) {
bri = e131_data[dataOffset];
strip.setBrightness(scaledBri(bri), false);
strip.setBrightness(bri, false);
stateUpdated(CALL_MODE_WS_SEND);
}
return;

View File

@@ -24,6 +24,10 @@ void handleIO();
void IRAM_ATTR touchButtonISR();
//cfg.cpp
bool backupConfig();
bool restoreConfig();
bool verifyConfig();
void resetConfig();
bool deserializeConfig(JsonObject doc, bool fromFS = false);
bool deserializeConfigFromFS();
bool deserializeConfigSec();
@@ -69,133 +73,6 @@ typedef struct WiFiConfig {
}
} wifi_config;
//colors.cpp
#define ColorFromPalette ColorFromPaletteWLED // override fastled version
// CRGBW can be used to manipulate 32bit colors faster. However: if it is passed to functions, it adds overhead compared to a uint32_t color
// use with caution and pay attention to flash size. Usually converting a uint32_t to CRGBW to extract r, g, b, w values is slower than using bitshifts
// it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB
struct CRGBW {
union {
uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB)
struct {
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t w;
};
uint8_t raw[4]; // Access as an array in the order B, G, R, W
};
// Default constructor
inline CRGBW() __attribute__((always_inline)) = default;
// Constructor from a 32-bit color (0xWWRRGGBB)
constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {}
// Constructor with r, g, b, w values
constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : b(blue), g(green), r(red), w(white) {}
// Constructor from CRGB
constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {}
// Access as an array
inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; }
// Assignment from 32-bit color
inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; }
// Assignment from r, g, b, w
inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { b = rgb.b; g = rgb.g; r = rgb.r; w = 0; return *this; }
// Conversion operator to uint32_t
inline operator uint32_t() const __attribute__((always_inline)) {
return color32;
}
/*
// Conversion operator to CRGB
inline operator CRGB() const __attribute__((always_inline)) {
return CRGB(r, g, b);
}
CRGBW& scale32 (uint8_t scaledown) // 32bit math
{
if (color32 == 0) return *this; // 2 extra instructions, worth it if called a lot on black (which probably is true) adding check if scaledown is zero adds much more overhead as its 8bit
uint32_t scale = scaledown + 1;
uint32_t rb = (((color32 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue
uint32_t wg = (((color32 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green
color32 = rb | wg;
return *this;
}*/
};
struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions
union {
struct {
uint16_t h; // hue
uint8_t s; // saturation
uint8_t v; // value
};
uint32_t raw; // 32bit access
};
inline CHSV32() __attribute__((always_inline)) = default; // default constructor
/// Allow construction from hue, saturation, and value
/// @param ih input hue
/// @param is input saturation
/// @param iv input value
inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v
: h(ih), s(is), v(iv) {}
inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v
: h((uint16_t)ih << 8), s(is), v(iv) {}
inline CHSV32(const CHSV& chsv) __attribute__((always_inline)) // constructor from CHSV
: h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {}
inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV
};
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
class NeoGammaWLEDMethod {
public:
[[gnu::hot]] static uint8_t Correct(uint8_t value); // apply Gamma to single channel
[[gnu::hot]] static uint32_t Correct32(uint32_t color); // apply Gamma to RGBW32 color (WLED specific, not used by NPB)
[[gnu::hot]] static uint32_t inverseGamma32(uint32_t color); // apply inverse Gamma to RGBW32 color
static void calcGammaTable(float gamma); // re-calculates & fills gamma tables
static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; } // get value from Gamma table (WLED specific, not used by NPB)
static inline uint8_t rawInverseGamma8(uint8_t val) { return gammaT_inv[val]; } // get value from inverse Gamma table (WLED specific, not used by NPB)
private:
static uint8_t gammaT[];
static uint8_t gammaT_inv[];
};
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
#define gamma32inv(c) NeoGammaWLEDMethod::inverseGamma32(c)
#define gamma8inv(c) NeoGammaWLEDMethod::rawInverseGamma8(c)
[[gnu::hot, gnu::pure]] uint32_t color_blend(uint32_t c1, uint32_t c2 , uint8_t blend);
inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); };
[[gnu::hot, gnu::pure]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false);
[[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false);
[[gnu::hot, gnu::pure]] uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten);
[[gnu::hot, gnu::pure]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND);
CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette);
CRGBPalette16 generateRandomPalette();
void loadCustomPalettes();
extern std::vector<CRGBPalette16> customPalettes;
inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb);
void rgb2hsv(const uint32_t rgb, CHSV32& hsv);
inline CHSV rgb2hsv(const CRGB c) { CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); } // CRGB to hsv
void colorKtoRGB(uint16_t kelvin, byte* rgb);
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO
void colorRGBtoXY(const byte* rgb, float* xy); // only defined if huesync disabled TODO
void colorFromDecOrHexString(byte* rgb, const char* in);
bool colorFromHexString(byte* rgb, const char* in);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
//dmx_output.cpp
void initDMXOutput();
void handleDMXOutput();
@@ -223,6 +100,11 @@ inline bool writeObjectToFileUsingId(const String &file, uint16_t id, const Json
inline bool writeObjectToFile(const String &file, const char* key, const JsonDocument* content) { return writeObjectToFile(file.c_str(), key, content); };
inline bool readObjectFromFileUsingId(const String &file, uint16_t id, JsonDocument* dest, const JsonDocument* filter = nullptr) { return readObjectFromFileUsingId(file.c_str(), id, dest); };
inline bool readObjectFromFile(const String &file, const char* key, JsonDocument* dest, const JsonDocument* filter = nullptr) { return readObjectFromFile(file.c_str(), key, dest); };
bool copyFile(const char* src_path, const char* dst_path);
bool backupFile(const char* filename);
bool restoreFile(const char* filename);
bool validateJsonFile(const char* filename);
void dumpFilesToSerial();
//hue.cpp
void handleHue();
@@ -481,6 +363,11 @@ namespace UsermodManager {
// Register usermods by building a static list via a linker section
#define REGISTER_USERMOD(x) Usermod* const um_##x __attribute__((__section__(".dtors.tbl.usermods.1"), used)) = &x
//usermod.cpp
void userSetup();
void userConnected();
void userLoop();
//util.cpp
#ifdef ESP8266
#define HW_RND_REGISTER RANDOM_REG32
@@ -575,6 +462,10 @@ extern "C" {
#define d_free free
#endif
void handleBootLoop(); // detect and handle bootloops
#ifndef ESP8266
void bootloopCheckOTA(); // swap boot image if bootloop is detected instead of restoring config
#endif
// RAII guard class for the JSON Buffer lock
// Modeled after std::lock_guard
class JSONBufferGuard {

View File

@@ -439,3 +439,156 @@ bool handleFileRead(AsyncWebServerRequest* request, String path){
}
return false;
}
// copy a file, delete destination file if incomplete to prevent corrupted files
bool copyFile(const char* src_path, const char* dst_path) {
DEBUG_PRINTF("copyFile from %s to %s\n", src_path, dst_path);
if(!WLED_FS.exists(src_path)) {
DEBUG_PRINTLN(F("file not found"));
return false;
}
bool success = true; // is set to false on error
File src = WLED_FS.open(src_path, "r");
File dst = WLED_FS.open(dst_path, "w");
if (src && dst) {
uint8_t buf[128]; // copy file in 128-byte blocks
while (src.available() > 0) {
size_t bytesRead = src.read(buf, sizeof(buf));
if (bytesRead == 0) {
success = false;
break; // error, no data read
}
size_t bytesWritten = dst.write(buf, bytesRead);
if (bytesWritten != bytesRead) {
success = false;
break; // error, not all data written
}
}
} else {
success = false; // error, could not open files
}
if(src) src.close();
if(dst) dst.close();
if (!success) {
DEBUG_PRINTLN(F("copy failed"));
WLED_FS.remove(dst_path); // delete incomplete file
}
return success;
}
// compare two files, return true if identical
bool compareFiles(const char* path1, const char* path2) {
DEBUG_PRINTF("compareFile %s and %s\n", path1, path2);
if (!WLED_FS.exists(path1) || !WLED_FS.exists(path2)) {
DEBUG_PRINTLN(F("file not found"));
return false;
}
bool identical = true; // set to false on mismatch
File f1 = WLED_FS.open(path1, "r");
File f2 = WLED_FS.open(path2, "r");
if (f1 && f2) {
uint8_t buf1[128], buf2[128];
while (f1.available() > 0 || f2.available() > 0) {
size_t len1 = f1.read(buf1, sizeof(buf1));
size_t len2 = f2.read(buf2, sizeof(buf2));
if (len1 != len2) {
identical = false;
break; // files differ in size or read failed
}
if (memcmp(buf1, buf2, len1) != 0) {
identical = false;
break; // files differ in content
}
}
} else {
identical = false; // error opening files
}
if (f1) f1.close();
if (f2) f2.close();
return identical;
}
static const char s_backup_fmt[] PROGMEM = "/bkp.%s";
bool backupFile(const char* filename) {
DEBUG_PRINTF("backup %s \n", filename);
if (!validateJsonFile(filename)) {
DEBUG_PRINTLN(F("broken file"));
return false;
}
char backupname[32];
snprintf_P(backupname, sizeof(backupname), s_backup_fmt, filename + 1); // skip leading '/' in filename
if (copyFile(filename, backupname)) {
DEBUG_PRINTLN(F("backup ok"));
return true;
}
DEBUG_PRINTLN(F("backup failed"));
return false;
}
bool restoreFile(const char* filename) {
DEBUG_PRINTF("restore %s \n", filename);
char backupname[32];
snprintf_P(backupname, sizeof(backupname), s_backup_fmt, filename + 1); // skip leading '/' in filename
if (!WLED_FS.exists(backupname)) {
DEBUG_PRINTLN(F("no backup found"));
return false;
}
if (!validateJsonFile(backupname)) {
DEBUG_PRINTLN(F("broken backup"));
return false;
}
if (copyFile(backupname, filename)) {
DEBUG_PRINTLN(F("restore ok"));
return true;
}
DEBUG_PRINTLN(F("restore failed"));
return false;
}
bool validateJsonFile(const char* filename) {
if (!WLED_FS.exists(filename)) return false;
File file = WLED_FS.open(filename, "r");
if (!file) return false;
StaticJsonDocument<0> doc, filter; // https://arduinojson.org/v6/how-to/validate-json/
bool result = deserializeJson(doc, file, DeserializationOption::Filter(filter)) == DeserializationError::Ok;
file.close();
if (!result) {
DEBUG_PRINTF_P(PSTR("Invalid JSON file %s\n"), filename);
} else {
DEBUG_PRINTF_P(PSTR("Valid JSON file %s\n"), filename);
}
return result;
}
// print contents of all files in root dir to Serial except wsec files
void dumpFilesToSerial() {
File rootdir = WLED_FS.open("/", "r");
File rootfile = rootdir.openNextFile();
while (rootfile) {
size_t len = strlen(rootfile.name());
// skip files starting with "wsec" and dont end in .json
if (strncmp(rootfile.name(), "wsec", 4) != 0 && len >= 6 && strcmp(rootfile.name() + len - 5, ".json") == 0) {
Serial.println(rootfile.name());
while (rootfile.available()) {
Serial.write(rootfile.read());
}
Serial.println();
Serial.println();
}
rootfile.close();
rootfile = rootdir.openNextFile();
}
}

View File

@@ -58,7 +58,7 @@ void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t
// set multiple pixels if upscaling
for (int16_t i = 0; i < (activeSeg->width()+(gifWidth-1)) / gifWidth; i++) {
for (int16_t j = 0; j < (activeSeg->height()+(gifHeight-1)) / gifHeight; j++) {
activeSeg->setPixelColorXY(outX + i, outY + j, gamma8(red), gamma8(green), gamma8(blue));
activeSeg->setPixelColorXY(outX + i, outY + j, red, green, blue);
}
}
}

View File

@@ -312,7 +312,7 @@ static bool deserializeSegment(JsonObject elem, byte it, byte presetId = 0)
jsonTransitionOnce = true;
if (seg.isInTransition()) seg.startTransition(0); // setting transition time to 0 will stop transition in next frame
strip.setTransition(0);
strip.setBrightness(scaledBri(bri), true);
strip.setBrightness(bri, true);
// freeze and init to black
if (!seg.freeze) {

View File

@@ -57,7 +57,7 @@ void toggleOnOff()
//scales the brightness with the briMultiplier factor
byte scaledBri(byte in)
{
unsigned val = ((uint16_t)in*briMultiplier)/100;
unsigned val = ((unsigned)in*briMultiplier)/100;
if (val > 255) val = 255;
return (byte)val;
}
@@ -68,7 +68,7 @@ void applyBri() {
if (realtimeOverride || !(realtimeMode && arlsForceMaxBri))
{
//DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld);
strip.setBrightness(scaledBri(briT));
strip.setBrightness(briT);
}
}

View File

@@ -351,7 +351,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
t = request->arg(F("TP")).toInt();
randomPaletteChangeTime = MIN(255,MAX(1,t));
useHarmonicRandomPalette = request->hasArg(F("TH"));
useRainbowWheel = request->hasArg(F("RW"));
nightlightTargetBri = request->arg(F("TB")).toInt();
t = request->arg(F("TL")).toInt();

View File

@@ -424,7 +424,7 @@ void realtimeLock(uint32_t timeoutMs, byte md)
}
// if strip is off (bri==0) and not already in RTM
if (briT == 0) {
strip.setBrightness(scaledBri(briLast), true);
strip.setBrightness(briLast, true);
}
}
@@ -434,14 +434,14 @@ void realtimeLock(uint32_t timeoutMs, byte md)
realtimeMode = md;
if (realtimeOverride) return;
if (arlsForceMaxBri) strip.setBrightness(scaledBri(255), true);
if (arlsForceMaxBri) strip.setBrightness(255, true);
if (briT > 0 && md == REALTIME_MODE_GENERIC) strip.show();
}
void exitRealtime() {
if (!realtimeMode) return;
if (realtimeOverride == REALTIME_OVERRIDE_ONCE) realtimeOverride = REALTIME_OVERRIDE_NONE;
strip.setBrightness(scaledBri(bri), true);
strip.setBrightness(bri, true);
realtimeTimeout = 0; // cancel realtime mode immediately
realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately
realtimeIP[0] = 0;

29
wled00/usermod.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include "wled.h"
/*
* This v1 usermod file allows you to add own functionality to WLED more easily
* See: https://github.com/wled-dev/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!
*/
//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()
{
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
}

View File

@@ -1,6 +1,16 @@
#include "wled.h"
#include "fcn_declare.h"
#include "const.h"
#ifdef ESP8266
#include "user_interface.h" // for bootloop detection
#else
#include <Update.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp32/rtc.h" // for bootloop detection
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
#include "soc/rtc.h"
#endif
#endif
//helper to get int value at a position in string
@@ -706,6 +716,132 @@ void *realloc_malloc(void *ptr, size_t size) {
}
#endif
// bootloop detection and handling
// checks if the ESP reboots multiple times due to a crash or watchdog timeout
// if a bootloop is detected: restore settings from backup, then reset settings, then switch boot image (and repeat)
#define BOOTLOOP_THRESHOLD 5 // number of consecutive crashes to trigger bootloop detection
#define BOOTLOOP_ACTION_RESTORE 0 // default action: restore config from /bak.cfg.json
#define BOOTLOOP_ACTION_RESET 1 // if restore does not work, reset config (rename /cfg.json to /rst.cfg.json)
#define BOOTLOOP_ACTION_OTA 2 // swap the boot partition
#define BOOTLOOP_ACTION_DUMP 3 // nothing seems to help, dump files to serial and reboot (until hardware reset)
#ifdef ESP8266
#define BOOTLOOP_INTERVAL_TICKS (5 * 160000) // time limit between crashes: ~5 seconds in RTC ticks
#define BOOT_TIME_IDX 0 // index in RTC memory for boot time
#define CRASH_COUNTER_IDX 1 // index in RTC memory for crash counter
#define ACTIONT_TRACKER_IDX 2 // index in RTC memory for boot action
#else
#define BOOTLOOP_INTERVAL_TICKS 5000 // time limit between crashes: ~5 seconds in milliseconds
// variables in RTC_NOINIT memory persist between reboots (but not on hardware reset)
RTC_NOINIT_ATTR static uint32_t bl_last_boottime;
RTC_NOINIT_ATTR static uint32_t bl_crashcounter;
RTC_NOINIT_ATTR static uint32_t bl_actiontracker;
void bootloopCheckOTA() { bl_actiontracker = BOOTLOOP_ACTION_OTA; } // swap boot image if bootloop is detected instead of restoring config
#endif
// detect bootloop by checking the reset reason and the time since last boot
static bool detectBootLoop() {
#if !defined(ESP8266)
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
uint32_t rtctime = esp_rtc_get_time_us() / 1000; // convert to milliseconds
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
uint64_t rtc_ticks = rtc_time_get();
uint32_t rtctime = rtc_time_slowclk_to_us(rtc_ticks, rtc_clk_slow_freq_get_hz()) / 1000; // convert to milliseconds
#endif
esp_reset_reason_t reason = esp_reset_reason();
if (!(reason == ESP_RST_PANIC || reason == ESP_RST_WDT || reason == ESP_RST_INT_WDT || reason == ESP_RST_TASK_WDT)) {
// no crash detected, init variables
bl_crashcounter = 0;
bl_last_boottime = rtctime;
if(reason != ESP_RST_SW)
bl_actiontracker = BOOTLOOP_ACTION_RESTORE; // init action tracker if not an intentional reboot (e.g. from OTA or bootloop handler)
} else if (reason == ESP_RST_BROWNOUT) {
// crash due to brownout can't be detected unless using flash memory to store bootloop variables
// this is a simpler way to preemtively revert the config in case current brownout is caused by a bad choice of settings
DEBUG_PRINTLN(F("brownout detected"));
//restoreConfig(); // TODO: blindly restoring config if brownout detected is a bad idea, need a better way (if at all)
} else {
uint32_t rebootinterval = rtctime - bl_last_boottime;
bl_last_boottime = rtctime; // store current runtime for next reboot
if (rebootinterval < BOOTLOOP_INTERVAL_TICKS) {
bl_crashcounter++;
if (bl_crashcounter >= BOOTLOOP_THRESHOLD) {
DEBUG_PRINTLN(F("!BOOTLOOP DETECTED!"));
bl_crashcounter = 0;
return true;
}
}
}
#else // ESP8266
rst_info* resetreason = system_get_rst_info();
uint32_t bl_last_boottime;
uint32_t bl_crashcounter;
uint32_t bl_actiontracker;
uint32_t rtctime = system_get_rtc_time();
if (!(resetreason->reason == REASON_EXCEPTION_RST || resetreason->reason == REASON_WDT_RST)) {
// no crash detected, init variables
bl_crashcounter = 0;
ESP.rtcUserMemoryWrite(BOOT_TIME_IDX, &rtctime, sizeof(uint32_t));
ESP.rtcUserMemoryWrite(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t));
if(resetreason->reason != REASON_SOFT_RESTART) {
bl_actiontracker = BOOTLOOP_ACTION_RESTORE; // init action tracker if not an intentional reboot (e.g. from OTA or bootloop handler)
ESP.rtcUserMemoryWrite(ACTIONT_TRACKER_IDX, &bl_actiontracker, sizeof(uint32_t));
}
} else {
// system has crashed
ESP.rtcUserMemoryRead(BOOT_TIME_IDX, &bl_last_boottime, sizeof(uint32_t));
ESP.rtcUserMemoryRead(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t));
uint32_t rebootinterval = rtctime - bl_last_boottime;
ESP.rtcUserMemoryWrite(BOOT_TIME_IDX, &rtctime, sizeof(uint32_t)); // store current ticks for next reboot
if (rebootinterval < BOOTLOOP_INTERVAL_TICKS) {
bl_crashcounter++;
ESP.rtcUserMemoryWrite(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t));
if (bl_crashcounter >= BOOTLOOP_THRESHOLD) {
DEBUG_PRINTLN(F("BOOTLOOP DETECTED"));
bl_crashcounter = 0;
ESP.rtcUserMemoryWrite(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t));
return true;
}
}
}
#endif
return false; // no bootloop detected
}
void handleBootLoop() {
DEBUG_PRINTLN(F("checking for bootloop"));
if (!detectBootLoop()) return; // no bootloop detected
#ifdef ESP8266
uint32_t bl_actiontracker;
ESP.rtcUserMemoryRead(ACTIONT_TRACKER_IDX, &bl_actiontracker, sizeof(uint32_t));
#endif
if (bl_actiontracker == BOOTLOOP_ACTION_RESTORE) {
restoreConfig(); // note: if this fails, could reset immediately. instead just let things play out and save a few lines of code
bl_actiontracker = BOOTLOOP_ACTION_RESET; // reset config if it keeps bootlooping
} else if (bl_actiontracker == BOOTLOOP_ACTION_RESET) {
resetConfig();
bl_actiontracker = BOOTLOOP_ACTION_OTA; // swap boot partition if it keeps bootlooping. On ESP8266 this is the same as BOOTLOOP_ACTION_NONE
}
#ifndef ESP8266
else if (bl_actiontracker == BOOTLOOP_ACTION_OTA) {
if(Update.canRollBack()) {
DEBUG_PRINTLN(F("Swapping boot partition..."));
Update.rollBack(); // swap boot partition
}
bl_actiontracker = BOOTLOOP_ACTION_DUMP; // out of options
}
#endif
else
dumpFilesToSerial();
#ifdef ESP8266
ESP.rtcUserMemoryWrite(ACTIONT_TRACKER_IDX, &bl_actiontracker, sizeof(uint32_t));
#endif
ESP.restart(); // restart cleanly and don't wait for another crash
}
/*
* Fixed point integer based Perlin noise functions by @dedehai
* Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness

View File

@@ -77,6 +77,7 @@ void WLED::loop()
#ifdef WLED_DEBUG
unsigned long usermodMillis = millis();
#endif
userLoop();
UsermodManager::loop();
#ifdef WLED_DEBUG
usermodMillis = millis() - usermodMillis;
@@ -189,12 +190,10 @@ void WLED::loop()
doInitBusses = false;
DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
BusManager::removeAll();
strip.finalizeInit(); // will create buses and also load default ledmap if present
BusManager::setBrightness(bri); // fix re-initialised bus' brightness #4005
if (aligned) strip.makeAutoSegments();
else strip.fixInvalidSegments();
BusManager::setBrightness(bri); // fix re-initialised bus' brightness
BusManager::setBrightness(scaledBri(bri)); // fix re-initialised bus' brightness #4005 and #4824
configNeedsWrite = true;
}
if (loadLedmap >= 0) {
@@ -409,6 +408,9 @@ void WLED::setup()
DEBUGFS_PRINTLN(F("FS failed!"));
errorFlag = ERR_FS_BEGIN;
}
handleBootLoop(); // check for bootloop and take action (requires WLED_FS)
#ifdef WLED_ADD_EEPROM_SUPPORT
else deEEP();
#else
@@ -424,6 +426,11 @@ void WLED::setup()
WLED_SET_AP_SSID(); // otherwise it is empty on first boot until config is saved
multiWiFi.push_back(WiFiConfig(CLIENT_SSID,CLIENT_PASS)); // initialise vector with default WiFi
if(!verifyConfig()) {
if(!restoreConfig()) {
resetConfig();
}
}
DEBUG_PRINTLN(F("Reading config"));
bool needsCfgSave = deserializeConfigFromFS();
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
@@ -441,6 +448,7 @@ void WLED::setup()
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
DEBUG_PRINTLN(F("Usermods setup"));
userSetup();
UsermodManager::setup();
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
@@ -844,6 +852,7 @@ void WLED::handleConnection()
if (improvActive > 1) sendImprovIPRPCResult(ImprovRPCType::Command_Wifi);
}
initInterfaces();
userConnected();
UsermodManager::connected();
lastMqttReconnectAttempt = 0; // force immediate update

View File

@@ -194,6 +194,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#include "fcn_declare.h"
#include "NodeStruct.h"
#include "pin_manager.h"
#include "colors.h"
#include "bus_manager.h"
#include "FX.h"
@@ -625,7 +626,6 @@ WLED_GLOBAL unsigned long transitionStartTime;
WLED_GLOBAL bool jsonTransitionOnce _INIT(false); // flag to override transitionDelay (playlist, JSON API: "live" & "seg":{"i"} & "tt")
WLED_GLOBAL uint8_t randomPaletteChangeTime _INIT(5); // amount of time [s] between random palette changes (min: 1s, max: 255s)
WLED_GLOBAL bool useHarmonicRandomPalette _INIT(true); // use *harmonic* random palette generation (nicer looking) or truly random
WLED_GLOBAL bool useRainbowWheel _INIT(false); // use "rainbow" color wheel instead of "spectrum" color wheel
// nightlight
WLED_GLOBAL bool nightlightActive _INIT(false);

View File

@@ -411,6 +411,9 @@ void initServer()
serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254);
} else {
serveMessage(request, 200, F("Update successful!"), FPSTR(s_rebooting), 131);
#ifndef ESP8266
bootloopCheckOTA(); // let the bootloop-checker know there was an OTA update
#endif
doReboot = true;
}
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool isFinal){
@@ -429,8 +432,9 @@ void initServer()
UsermodManager::onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
lastEditTime = millis(); // make sure PIN does not lock during update
strip.suspend();
#ifdef ESP8266
backupConfig(); // backup current config in case the update ends badly
strip.resetSegments(); // free as much memory as you can
#ifdef ESP8266
Update.runAsync(true);
#endif
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);

View File

@@ -26,7 +26,8 @@ void XML_response(Print& dest)
);
}
static void extractPin(Print& settingsScript, const JsonObject &obj, const char *key) {
static void extractPin(Print& settingsScript, const JsonObject &obj, const char *key)
{
if (obj[key].is<JsonArray>()) {
JsonArray pins = obj[key].as<JsonArray>();
for (JsonVariant pv : pins) {
@@ -37,6 +38,22 @@ static void extractPin(Print& settingsScript, const JsonObject &obj, const char
}
}
void fillWLEDVersion(char *buf, size_t len)
{
if (!buf || len == 0) return;
snprintf_P(buf,len,PSTR("WLED %s (%d)<br>\\\"%s\\\"<br>(Processor: %s)"),
versionString,
VERSION,
releaseString,
#if defined(ARDUINO_ARCH_ESP32)
ESP.getChipModel()
#else
"ESP8266"
#endif
);
}
// print used pins by scanning JsonObject (1 level deep)
static void fillUMPins(Print& settingsScript, const JsonObject &mods)
{
@@ -72,7 +89,8 @@ static void fillUMPins(Print& settingsScript, const JsonObject &mods)
}
}
void appendGPIOinfo(Print& settingsScript) {
void appendGPIOinfo(Print& settingsScript)
{
settingsScript.print(F("d.um_p=[-1")); // has to have 1 element
if (i2c_sda > -1 && i2c_scl > -1) {
settingsScript.printf_P(PSTR(",%d,%d"), i2c_sda, i2c_scl);
@@ -382,7 +400,6 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("TL"),nightlightDelayMinsDefault);
printSetFormValue(settingsScript,PSTR("TW"),nightlightMode);
printSetFormIndex(settingsScript,PSTR("PB"),paletteBlend);
printSetFormCheckbox(settingsScript,PSTR("RW"),useRainbowWheel);
printSetFormValue(settingsScript,PSTR("RL"),rlyPin);
printSetFormCheckbox(settingsScript,PSTR("RM"),rlyMde);
printSetFormCheckbox(settingsScript,PSTR("RO"),rlyOpenDrain);
@@ -595,7 +612,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormCheckbox(settingsScript,PSTR("AO"),aOtaEnabled);
printSetFormCheckbox(settingsScript,PSTR("SU"),otaSameSubnet);
char tmp_buf[128];
snprintf_P(tmp_buf,sizeof(tmp_buf),PSTR("WLED %s (build %d)"),versionString,VERSION);
fillWLEDVersion(tmp_buf,sizeof(tmp_buf));
printSetClassElementHTML(settingsScript,PSTR("sip"),0,tmp_buf);
settingsScript.printf_P(PSTR("sd=\"%s\";"), serverDescription);
//hide settings if not compiled
@@ -657,16 +674,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
if (subPage == SUBPAGE_UPDATE) // update
{
char tmp_buf[128];
snprintf_P(tmp_buf,sizeof(tmp_buf),PSTR("WLED %s<br>%s<br>(%s build %d)"),
versionString,
releaseString,
#if defined(ARDUINO_ARCH_ESP32)
ESP.getChipModel(),
#else
"esp8266",
#endif
VERSION);
fillWLEDVersion(tmp_buf,sizeof(tmp_buf));
printSetClassElementHTML(settingsScript,PSTR("sip"),0,tmp_buf);
#ifndef ARDUINO_ARCH_ESP32
settingsScript.print(F("toggle('rev');")); // hide revert button on ESP8266