Compare commits

..

189 Commits

Author SHA1 Message Date
Will Tatam
6678ad22d5 Temp disbale failing parts of usermod needing MM features 2025-01-06 11:46:56 +00:00
Will Tatam
2cb8e32236 Copy current MoonModules version to AC 2025-01-06 11:32:02 +00:00
netmindz
27e98147ef Swap from dev to alpha for our current work in progress builds whilst working towards the next version 2025-01-05 18:49:51 +00:00
Frank
48958cc638 Merge pull request #4439 from dosipod/Upstream_PRs
Update readme.md for rgb-rotary-encoder usermod
2025-01-04 17:49:15 +01:00
Damian Schneider
ae4de2782a DeepSleep usermod (#4190)
* DeepSleep Usermod

- sleep delay is now 1 by default, disabling sleep at powerup
- renamed bootup variable to powerup
- using delay counter for proper bootup
- changed power-up and bootup logic
- added fallback to always power-on at boot except at powerup
- fixed bug in settings page
2025-01-04 08:07:11 +01:00
Damian Schneider
6a1d3de75b Fix output glitches when playlist changes preset (#4442)
same issue as with https://github.com/Aircoookie/WLED/pull/4386
waiting on bus to finish updating before file access fixes the glitches.
this issue is only present on S2 and C3, not on ESP8266 or dual-core ESPs, the fix is only applied for these two.
2024-12-30 12:58:38 +01:00
AlDIY
3fc8c7d560 Update readme.md
Updated the readme to use lennarthennigs/ESP Rotary@^2.1.1 as the old lib fail to compile
2024-12-29 15:46:49 +03:00
Damian Schneider
88738327fd Merge pull request #4419 from DedeHai/PWM_brightness_fix
BUGFIX PWM brightness discontinuity (fixes #4396)
2024-12-23 15:24:23 +01:00
Damian Schneider
0ad65f4748 fixed CIE brightness calculation for PWM outputs 2024-12-23 14:57:22 +01:00
Frank
9dad436f72 Merge pull request #4414 from shafingazi/main
fixing a typo in LED Preferences
2024-12-22 11:23:16 +01:00
shafingazi
ebdc38fff2 Merge pull request #1 from shafingazi/led-typo-fix
fixed typo in LED Preferences
2024-12-21 21:19:35 -08:00
shafingazi
97bbe6f305 fixed typo in LED Preferences
Changed "poweing" to "powering" within a text block of LED Preferences.
2024-12-21 21:18:57 -08:00
Frank
2f0dbef1e5 Merge pull request #4413 from wled-install/patch-2
Update usermods_list.cpp:  usermod_BH1750.h --> usermod_bh1750.h
2024-12-21 23:21:53 +01:00
wled-install
1711286ef0 Update usermods_list.cpp 2024-12-21 23:05:13 +01:00
Will Tatam
099d3f7b41 version bump 2024-12-20 18:15:12 +00:00
Damian Schneider
5f77478841 Replace PRNG with hardware RNG (#4225)
Both ESP8266 and ESP32 have a hardware random register. This update makes use of that. It is slightly faster than the fastled variants but mostly it is truly random, even when the timing limitations stated in the datasheet are disregarded. Also saves a bit on code size.

- Replaced all random8() and random16() calls with new hw_random() versions
- Not replaced in FX where PRNG is required
2024-12-20 19:12:29 +01:00
Damian Schneider
07cc3aa5c0 FX improvements and cleanup (#4145)
Improvements & merges of FX

- Scrolling Text: Gradient Palette support added
- Waving Cell: Improved with higher temporal resolution (smoother at lower speeds) and added additional mode setting and optional blurring
- Julia: added blur option
- Squared Swirl: added fade option

- Added smearing option to:
    - DNA
    - DNA Spiral
    - Drift
    - Drift Rose
    - Crazy Bees
    - Ripple
    - Colored Bursts
    - Frizzles
    - Lissajous
    - Sindots
    - Spaceships

- Added palette support to:
    - Crazy Bees
    - Polar Lights
    - Drift Rose

- Changed default palette handling (no more special treatment for some FX)
- Merged puddles and puddlepeak
- Merged Gravcenter, Gravcentric, Gravfreq and Gravimeter (saves 1.2k of flash)
- Merged meteor and meteor smooth
- Renamed police_base into mode_two_dots as that was just an alias
- Added 'Traffic Light' palette (originally defined in Polar Lights FX)
-  Firenoise: removed local palette, use fire palette -> slight change in looks (+bugfix)
- Some code cleanup (removed unused / commented stuff)
- Moved dev info for AR to the top so ist easier to find as a reference, also added link to KB there
2024-12-20 14:13:53 +01:00
Damian Schneider
ff26f54bfd Merge pull request #4138 from DedeHai/0_15__speed_improvements
Speed improvements in many core functions
2024-12-20 09:45:56 +01:00
Damian Schneider
3323d2ed37 another merge fix 2024-12-20 09:37:41 +01:00
Damian Schneider
7b9b3f1ee2 merge fix 2024-12-20 09:12:20 +01:00
Damian Schneider
cae98451e3 Merge remote-tracking branch 'upstream/main' into 0_15__speed_improvements 2024-12-20 09:12:01 +01:00
Damian Schneider
83da7569f5 code consolidation in drawCircle() (#4302)
- saves about 600bytes of flash
- speed tradoff: drawing is now a tiny bit slower but insignificant in my tests
2024-12-19 20:19:42 +01:00
Damian Schneider
26397ee8ad Optimization: color_blend() (#4245)
Removing the bool saves on code size and makes the function a tiny bit faster. Also this is a cleaner solution IMHO.

-updated blend function to optimized 8bit calculation
- efficient color blend calculation in fews operations possible
- omitting min / max checks makes it faster on average
- using 8bit for "blend" variable does not significantly influence the resulting color, just transition points are slightly shifted but yield very good results (and better than the original 16bit version using the old fastled math with improper rounding)
- updated drawCircle and drawLine to use 8bit directly instead of 16bit with a shift
2024-12-19 18:20:56 +01:00
Damian Schneider
217d2aeb7f Merge pull request #4407 from DedeHai/fairy_FX_fix
BUGFIX for Fairy & Fairytwinkle FX
2024-12-19 09:24:27 +01:00
Damian Schneider
e57c701837 fix for repeating glitch
glitch appeared every 65s due to missing uint16_t overflow.
2024-12-19 09:21:57 +01:00
Blaž Kristan
b4aa8376de Idle current bugfix (#4402) 2024-12-17 18:59:53 +01:00
Frank
d4976ac47a Merge pull request #4398 from Aircoookie/4395-platformio_override
update platformio override example file (solves #4395)
2024-12-16 17:52:33 +01:00
Frank
b8a96d9a77 workaround for elekstube_ips compile error
the trick is to pin TFT_eSPI version 2.5.33
2024-12-16 15:46:59 +01:00
Frank
f75d582eee Merge branch 'main' into 4395-platformio_override 2024-12-16 15:27:12 +01:00
netmindz
18e0ec9a55 PRs to main now 2024-12-16 13:21:07 +00:00
Will Tatam
68b80cdadc Merge branch '0_15' 2024-12-16 13:12:38 +00:00
Frank
3d3c475d1b define board_build.partitions and build_unflags in standard envs
to ensure that build_unflags and board_build.partitions are always having a useful default value. Values can be overridden in custom buildenvs.

saves us a few lines lin platformio_override.sample.ini.
2024-12-15 22:20:52 +01:00
Frank
6668e72351 fix typo 2024-12-15 21:35:13 +01:00
Frank
b72695f01a added example environment for pico-D4 boards
this one is actually for https://github.com/srg74/WLED-ESP32-pico

Its a simple example how to configure WLED for a custom board with build-in microphone and special purpose pins.
2024-12-15 21:22:19 +01:00
Frank
65f98c1f30 esp32: use "extends"
directly inherits default platform from esp32
2024-12-15 21:07:01 +01:00
Frank
c6fd4c51cf fix outdated build options
* DEFAULT_LED_PIN --> DATA_PINS
* DEFAULT_LED_COUNT --> PIXEL_COUNTS
* DEFAULT_LED_TYPE --> LED_TYPES
* USERMOD_AUDIOREACTIVE --> ${esp32.AR_build_flags}
2024-12-15 21:02:45 +01:00
Frank
3c11c8441f fix errors in pltformio_override.ini (solves #4395)
* WLED_tasmota_1M not existing
* always use ${esp32.default_partitions}
* fix build error in wemos_shield_esp32
2024-12-15 20:54:16 +01:00
Damian Schneider
3261c5b071 replaced repeated progress() calculation calls with a variable (#4256)
progress() is called in setPixelColor(), calculating the transition progress for each pixel. Replaced that call with an inline function to get the new segment variable.
The progress is updated in service() when handleTransition() is called.
The new variable is in a spot where padding is added, so this should not use more RAM.
Result: over 10% increase in FPS on 16x16 matrix

* removed IRAM_ATTR: `updateTransitionProgress()` is called only once per frame, no need to put it in RAM.
* changed transitionprogress to static, private variable, this is now more aligned with other variables using the same logic
* added inline: the function is only used in one place
2024-12-15 13:46:18 +01:00
Soeren Willrodt
c8625c70dd Merge pull request #4397 from lost-hope/fix_internal_temp_usermod_main 2024-12-14 20:22:56 +01:00
Soeren Willrodt
396e9d0c39 Fix "preset not found issue"
this comit fixes the issue reported by user orbitinstasis
2024-12-14 19:58:30 +01:00
netmindz
2c58a87982 Merge pull request #4356 from blazoncek/json-cycle
Proper fix for #3605 & #4346
2024-12-12 15:53:03 +00:00
Will Tatam
a705ae5278 Merge branch '0_15' 2024-12-10 22:12:02 +00:00
netmindz
a426e93011 Merge pull request #4359 from blazoncek/reconnect
WiFi reconnect bugfix
2024-12-09 23:05:12 +00:00
Blaž Kristan
1c220d25ca WiFi reconnect bugfix
- additional debug info
2024-12-07 16:47:50 +01:00
Blaž Kristan
039858dca2 Incorrect limit fix 2024-12-07 10:14:11 +01:00
Blaž Kristan
4758b5efe8 Proper fix for #3605 & #4346
- allow incrementing/decrementing as specified in API
2024-12-07 10:11:25 +01:00
Frank
36e065ab4d Merge pull request #4336 from willmmiles/4280-limiter-not-saving
settings_leds: always initialize current limiter field (partly solves #4280 )
2024-12-05 13:40:14 +01:00
Damian Schneider
076497e14d Fix update for #4193 (twinkle fox & cat)
- previous fix worked but there was still an overflow after some time passed. there were still missing roll-overs apparently: reverting these two variables back to 16bit/8bit should fix it for good.
2024-12-05 06:54:30 +01:00
Will Tatam
e8d9891d13 set version to dev 2024-12-04 18:57:32 +00:00
Damian Schneider
4902d7fb9e Fix for #4193 (twinkle fox & cat) 2024-12-04 17:10:44 +01:00
netmindz
a873ca6a3e Merge pull request #4244 from MoonModules/framerate_ac015
Improved framerate control code - strip.show(), strip.service()
2024-12-04 07:48:19 +00:00
Will Miles
a86cb27cfe settings_leds: Init current limiter
Supersedes previous approach; this should use less space and be more robust to future changes.
2024-12-03 21:48:08 +00:00
Will Miles
d620930f10 settings_leds: Remove unused variables
Remove a couple of leftover variables from previous revisions.
2024-12-02 21:52:41 +00:00
Will Miles
8db8ecfef3 settings_leds: Fix quotes on LA value 2024-12-02 21:50:29 +00:00
Frank
a0f99393f5 Merge pull request #4341 from Aircoookie/hotfix_4335
Hotfix for #4335 - solves missed pixel problem in blends effect
2024-12-02 21:42:21 +01:00
Frank
ae8c3b02d0 blends FX - hotfix for black pixels
fixing an off-by-one error to solve #4335
2024-12-02 21:35:48 +01:00
netmindz
99427c2ef7 Merge pull request #4210 from 1Prototype1/dist-cpal
Added Distribute Horizontally for cpal
2024-12-01 19:09:34 +00:00
netmindz
334f16c0b6 Merge pull request #4203 from maxi4329/minor-webui-enhancements
minor webui enhancements
2024-12-01 19:03:36 +00:00
netmindz
7a80a772c1 Merge pull request #4270 from DedeHai/palette_code_crunch
Replaced single palette cases with an array to consolidate code
2024-12-01 19:02:28 +00:00
Will Miles
8b1d712e1e settings_leds: Initialize current limiter field
When adding a new bus, the numeric current limit field was not being
initialized; this was causing it to save 0 when saved instead of the
default 55mA value.
2024-12-01 13:00:37 -05:00
Will Miles
a121f5b61b Merge pull request #4286 from blazoncek/xml-bugfix
xml comma bugfix + few string optimisations
2024-11-28 09:48:29 -05:00
Frank
251062170e Merge pull request #4324 from dosipod/0_15
Update settings_sec.htm [Fix the  warning text and Backup presets button misalignment]
2024-11-28 12:22:46 +01:00
Damian Schneider
92e59af4d8 Merge pull request #4181 from DedeHai/0_15_trig_math
Added integer based `sin()/cos()` functions, changed all trig functions to wled_math
2024-11-27 22:27:56 +01:00
Damian Schneider
fa4c23b76e minor improvement 2024-11-27 22:23:48 +01:00
Damian Schneider
a0a46850f5 Merge remote-tracking branch 'upstream/0_15' into 0_15_trig_math 2024-11-27 22:22:50 +01:00
Damian Schneider
4cd0563a93 changed 90° offset to hex, fixed potential bug in FX using sin/cos with incrementing number
- sin/cos calls with incrementing numbers can lead to bad outcomes, the functions (_approx or original sinf/cosf) return bad values for very large float inputs
2024-11-27 21:53:32 +01:00
netmindz
b83f0f461c Merge pull request #4263 from DedeHai/palette_FX_fix
fixed palette FX to more closely match original 1D version
2024-11-27 19:48:00 +00:00
netmindz
3668ede0ff Merge pull request #4327 from willmmiles/4312-missing-clock
Fix missing clock setting introduce by #4312
2024-11-27 19:35:19 +00:00
netmindz
945584384a Merge pull request #4328 from blazoncek/clarify
Fix for #4321 - unclear use of comma operator
2024-11-27 19:31:53 +00:00
Blaž Kristan
b1dd27b516 Fix for #4321 2024-11-27 16:08:10 +01:00
Will Miles
dcba1aad10 PolyBus: Clarify use of clock_kHz
While not used by most bus types, it's not an optional parameter.
2024-11-27 09:05:10 -05:00
Will Miles
acc8b9cdbc BusDigital::begin: Pass clock rate argument
Fixes bug introduced by #4312.
2024-11-27 09:00:25 -05:00
Will Tatam
6cbdd825eb Revert "Cache always misses for tags, so save space and do not save back"
This reverts commit cc55f6015d.
2024-11-27 10:59:23 +00:00
Will Tatam
cc55f6015d Cache always misses for tags, so save space and do not save back 2024-11-27 10:56:53 +00:00
Will Tatam
95718ab6ec Dedicated release workflow 2024-11-27 10:36:18 +00:00
Will Tatam
feab27295d workflow refactor to used shared build file 2024-11-27 10:36:18 +00:00
Will Tatam
ca176c7549 rename workflow ready for workflow refactor 2024-11-27 10:36:18 +00:00
netmindz
f8a7a0d6e8 Merge pull request #4326 from blazoncek/com-fix
Fix for #4300 - removal of color order override
2024-11-27 10:01:22 +00:00
Damian Schneider
0a05611e1d more improvements to setPixelColor
- code is a bit cleaner and faster as well
- chaning array access to pointer access in bus_manager makes it a few instructions faster
- changed getNumberOfPins and getNumberOfChannels to return 32bit values, saving the unnecessary 8bit conversion
2024-11-26 20:59:36 +01:00
Damian Schneider
1a8aaa3b26 Speed improvements, commented legacy _t trig functions
- speed improvement: by default M_TWOPI is treated as a double float
- directly calling sin16_t in cos_approx() saves a lot of overhead
2024-11-26 20:32:39 +01:00
Blaž Kristan
cd1c13b4b1 Fix for #4300 2024-11-26 19:35:15 +01:00
AlDIY
d87c5035dd Update settings_sec.htm 2024-11-26 19:33:22 +03:00
netmindz
77967731d5 Merge pull request #4227 from ingDIY/pr_branch
fixed refs to removed usermod USERMOD_FOUR_LINE_DISPLAY
2024-11-26 09:15:31 +00:00
netmindz
0b54034470 Merge pull request #4284 from blazoncek/search-keep
Keep selected FX visible while searching
2024-11-26 09:10:14 +00:00
netmindz
8ad2583785 Merge pull request #4309 from netmindz/release-name-fix
Fix release name macro expansion
2024-11-25 23:00:56 +00:00
Will Tatam
dd533a9ab4 Update sample WLED_RELEASE_NAME 2024-11-25 23:00:31 +00:00
Frank
5b989adebc Allow TV Simulator on single LED segments
I've checked the code - nothing preventing the effect to run with SEGLEN=1
2024-11-25 15:27:53 +01:00
Will Tatam
20f8d3c8a9 Builds without WLED_RELEASE_NAME should be called Custom 2024-11-24 15:26:30 +00:00
Will Tatam
0c77dbb7ea cleanup WLED_RELEASE_NAME only if present 2024-11-24 13:55:23 +00:00
netmindz
2448e2ae3b Merge pull request #4312 from willmmiles/mixed-led-crash
Defer calling begin() on buses
2024-11-24 13:45:26 +00:00
Will Miles
d53d7aa2e2 Defer calling begin() on digital buses
NeoPixelBus requires that all parallel I2S bus members be constructed
before any of them call Begin().  Implement this by deferring the
call to the end of bus construction.

Fixes #4301.
2024-11-23 12:34:06 -05:00
Will Tatam
0be1df7ee8 Stip \" from WLED_RELEASE_NAME 2024-11-23 16:46:41 +00:00
Will Tatam
6aef0e145c Remove TOSTRING for releaseString and add quotes to WLED_RELEASE_NAME 2024-11-23 16:31:07 +00:00
Damian Schneider
32dc54ce72 reverted rotation scale, offset only on static rotation, inverted shift direction
- inverting the shift direction in signed int is computationally safe as it is cast into an uint8_t and it matches the original FX in 1D
2024-11-22 08:43:45 +01:00
Frank
548736f432 SparkFunDMX fix for possible array bounds violation in DMX.write
Align with code in espdmx.cpp
2024-11-21 22:50:55 +01:00
Frank
6790f8af08 Same MIN_FRAME_DELAY=3 for -C3 and -S2 2024-11-21 22:16:03 +01:00
netmindz
89d587e7dd Merge pull request #4296 from WouterGritter/mqtt-fix-settings-input-maxlength
Introduce printSetInputMaxlength to properly set an inputs maxlength …
2024-11-21 18:25:26 +00:00
Wouter Gritter
5c8b2ebf7a maxlength -> maxLength to fix this attribute not being modified correctly 2024-11-21 12:04:01 +01:00
Wouter Gritter
5ac8ba9bae Revert "Introduce printSetInputMaxlength to properly set an inputs maxlength variable"
This reverts commit 49fb16e2c6.
2024-11-21 12:02:55 +01:00
Wouter Gritter
49fb16e2c6 Introduce printSetInputMaxlength to properly set an inputs maxlength variable 2024-11-21 10:52:22 +01:00
Frank
545bfa6ef9 Merge pull request #4289 from willmmiles/exploding_fireworks_overrun
Fix array overflow in exploding_fireworks
2024-11-18 22:18:19 +01:00
Blaž Kristan
c596b5a17d Bugfix 2024-11-17 14:31:23 +01:00
Blaž Kristan
84dd26c1b7 Some more optimisations. 2024-11-17 10:47:09 +01:00
Will Miles
a765903a41 Fix array overflow in exploding_fireworks
Attempt to allocate enough room for the "minimum" sparks; and ensure
that we never overrun the allocated array size.

Fixes #4120
2024-11-16 17:10:30 -05:00
Blaž Kristan
25ab381916 Merge pull request #46 from w00000dy/search-keep-mod
Move selected to the bottom in search result
2024-11-16 22:54:11 +01:00
Woody
4d3df5d98f Fix default/solid not being first 2024-11-16 20:12:29 +01:00
Woody
6fe2024542 move selected to the bottom in search result 2024-11-16 19:53:10 +01:00
maxi4329
7f69a0bc5e removed obsolete code as of #4267 2024-11-16 12:37:24 +01:00
maxi4329
5b829adedb Merge branch '0_15' of https://github.com/maxi4329/WLED into minor-webui-enhancements 2024-11-16 12:24:55 +01:00
netmindz
4cd4c13b2d Merge pull request #4267 from w00000dy/space
Fix problem with spaces
2024-11-16 07:14:34 +00:00
Blaž Kristan
86be5df475 xml comma bugfix + few optimisations 2024-11-15 19:03:46 +01:00
Blaž Kristan
8c5e0cd4e9 Keep selected FX while searching 2024-11-15 16:01:58 +01:00
netmindz
a9b0b8adc8 Merge pull request #4229 from maxi4329/snodejsversion
specified required nodejs version
2024-11-13 09:20:27 +00:00
Will Miles
ba01cb82f7 Merge pull request #4271 from Aircoookie/4269-crashes-when-using-http-api-within-mqtt
Fix for #4269
2024-11-11 19:53:06 -05:00
Damian Schneider
d437027f26 Replaced single palette cases with an array to consolidate code
- all palettes are defined in palettes.h
- access to fastled palettes as an array to remove the switch cases
- palette createn in json.cpp in a loop instead of repeaded calls to save flash
2024-11-10 22:48:26 +01:00
Frank
9a564ee204 readme.md - link to multi-strip KB page 2024-11-10 14:45:55 +01:00
Woody
5de86d3d91 fix problem with spaces 2024-11-09 23:55:09 +01:00
Damian Schneider
536444f9d1 fixed palette FX to more closely match original 1D version
- rotation scale is now exactly 180° (divide slider input by 255 instead of 256)
- removed shift offset: offset is now zero at slider 0, to hit 128 on touch input devices is really hard
- added a 90° shift to input rotation, enabling to rotate from 0 to 180° instead of +90 to -90 (which is not useful in 1D)
- changed default settings values to more closely match the old 1D effect
2024-11-09 16:38:18 +01:00
Blaž Kristan
5c2bac4b9d Merge branch '0_15' into 0_15__speed_improvements 2024-11-09 10:53:27 +01:00
Blaž Kristan
ef1e24cec2 Bugfix & code reduction
- correctly clear segment spacing change
- renamed Segment::setUp() to Segment::setGeometry()
- removed WS2812FX::setSegment()
- removed obsolete/unfunctional word clock usermod .cpp file
2024-11-09 10:42:49 +01:00
Frank
001e2ad287 adjust audioreactive for the new FRAME_DELAY logic
minor
2024-11-08 19:35:42 +01:00
Frank
0404ec9881 changes in response to feedback from @willmmiles
* MIN_SHOW_DELAY -> MIN_FRAME_DELAY
* allow up to 250 for target FPS
* minor cleanup
* added specific MIN_FRAME_DELAY for -S2
2024-11-07 23:15:39 +01:00
netmindz
6ff5c88ebf List ESP32 first 2024-11-07 08:17:08 +00:00
Frank
1e761c31bd simpler hight FPS warning
* removed "use 0 for unlimited"
* added "high FPS mode is experimental" warning
* added "backup first!" warning
* added anchor #backup to sec page
2024-11-06 22:09:33 +01:00
Frank
ab7b2d729e use class="warn" for unlimited mode message 2024-11-05 12:24:20 +01:00
Damian Schneider
271a07a7d6 Merge pull request #4250 from DedeHai/FPS_calc_averaging
Fixed & improved FPS calculation
2024-11-05 08:52:42 +01:00
Frank
029293a086 simplify sheduler logic
* _frametime ensures that effects are not serviced too often
*  MIN_SHOW_DELAY is the minimum allowed FRAMETIME that can be requested by effects
2024-11-04 20:11:10 +01:00
Frank
cf1630a94a 0 FPS = unlimited 2024-11-04 19:49:43 +01:00
Damian Schneider
4634ace74e Added define for bitshift, removed dithering
dithering is not really needed, the FPS_MULTIPLIER is a much better option.
2024-11-04 19:33:42 +01:00
Damian Schneider
3733715184 bugfix
bitshift was still set from testing, forgot to update
2024-11-04 17:38:45 +01:00
Damian Schneider
bf37ac53a3 improved FPS calc resolution, added averaging & multiplier compileflags
Fixed point calculation for improved accuracy, dithering in debug builds only.
Averaging and optional multiplier can be set as compile flags, example for speed testing with long averaging and a 10x multiplier:

-D FPS_CALC_AVG=200
-D FPS_MULTIPLIER=10

The calculation resolution is limited (9.7bit fixed point) so values larger than 200 can hit resolution limit and get stuck before reaching the final value.

If WLED_DEBUG is defined, dithering is added to the returned value so sub-frame accuracy is possible in post-processingwithout enabling the multiplier.
2024-11-04 08:10:05 +01:00
Frank
50934e6840 adressing some review comments
* keep FRAMETIME_FIXED as a fixed value
* remove WLED_FPS_SLOW and FRAMETIME_FIXED_SLOW
* explicit test "(_targetFps != FPS_UNLIMITED)" for debug messages
* don't modify _lastServiceShow in show()
* test for "fps == FPS_UNLIMITED" explicitly, so we could pick a different
 magic number later
2024-11-02 18:16:51 +01:00
Frank
891ea48e11 Merge remote-tracking branch 'upstream/0_15' into framerate_ac015 2024-11-02 17:56:24 +01:00
Frank
451cd4c74a Improved framerate control in strip.show(), strip.service()
* separate fps calculation (strip.show) from framerate control (strio.service)
* improved condition for early exit in strip.show
* make MIN_SHOW_DELAY depend on target fps
* strip.show consideres complete time for effect calculation + show; old code wrongly used the time between completion of last show and start of next effect drawing, causing unexpected slowdown
* add "unlimited FPS mode" for testing
* increase warning limits for "slow strip" and "slow effects"
2024-11-01 23:19:38 +01:00
ingDIY
fa053b7e60 Update platformio_override.sample.ini
fixed back U8g2 version
2024-10-29 01:14:02 +01:00
maxi4329
d05c358fd2 version changed to 20 2024-10-27 11:13:56 +01:00
maxi4329
7c6bc5c421 indened formating 2024-10-26 21:07:17 +02:00
maxi4329
798c398f23 specified required nodejs ver 2024-10-26 20:33:38 +02:00
ingDIY
dcfdca6351 cleaned up old references to USE_ALT_DISPlAY that aren't used anymore in the code/source files; fixed readme.md documents and updated instructions; removed referencies to old usermods; fixed invalid filenames; removed referencies to old usermods; checked cycle options; splitted and fixed platformio_override.sample.ini; 2024-10-25 12:14:37 +02:00
1Prototype1
4fa8a3898a Added Distribute for cpal
Added a button to distribute the color markers in palette equally
2024-10-19 00:30:24 +05:30
maxi4329
caa997fff1 removed onkeydown tried to find a replacement for the nbsp 2024-10-18 18:43:41 +02:00
maxi4329
bd68b977d5 minor webui enhancements 2024-10-17 18:07:01 +02:00
Damian Schneider
98a6907976 cleanup and improvement to sin_appros()
-replaced all PI references with M_PI version
-there is no need to do the angle-modulo in float, casting it to an integer does the same BUT it has to be cast to an `int` first, see comment.
2024-10-16 19:53:58 +02:00
Damian Schneider
5e29f2c1b7 fixed atan2_t
approximation was incorrect, now doing it right.
also removed hypotf() from octopus, saving a little flash.
2024-10-15 20:11:33 +02:00
Blaž Kristan
210191b251 Fix for realtime drawing on main segment 2024-10-07 20:19:07 +02:00
Blaž Kristan
be64930ebb Indentation and shadowed variable. 2024-10-07 16:50:51 +02:00
Damian Schneider
f301296f1e added rounding to sin8_t
thx to @softhack007
2024-10-06 21:27:58 +02:00
Blaž Kristan
eb5ad232a0 Minor tweaks and whitespace 2024-10-05 23:31:31 +02:00
Damian Schneider
7b855c851d Added integer based sin()/cos() functions, changed all trig functions to wled_math
- `sin16_t() / cos16_t()` are faster and more accurate than fastled versions
- `sin_approx() / cos_approx()` are float wrappers for `sin16_t() / cos16_t()` and are accurate enough to replace `sinf()/cosf()`
- `atan2()` is used only in octopus to calculate center offset, new approximated version saves flash
- `tan(), atan(), asin(), acos(), floor(), fmod()` are used only for sunrise/sunset calculation, using wled_math version saves flash
- `beatsinx()` replacements are to make use of new `sin16_t()/sin8_t()` functions to reduce flash size
- Extensively tested surnise/sunset calculation: deviation is 1min. max
- Tested some of the relevant FX and found no visual difference: Julia, 2D Drift, Drift Rose, Ghost rider, Rotozoomer, Palette, Arc 1D expansion
- total flash savings: 7.4k
2024-10-05 12:32:41 +02:00
Damian Schneider
ca062140f3 removed todo. 2024-10-03 19:39:39 +00:00
Damian Schneider
a15c391e6c Improvement to setPixelColorXY and some flash optimisations
- changes to `setPixelColorXY` give an extra FPS, some checks and the loops are only done when needed, additional function call is still faster (force inlining it gives negligible speed boost but eats more flash)
- commented out the unused `boxBlur` function
- code size improvemnts (also faster) in `moveX()` and `moveY()` by only copying whats required and avoiding code duplications
- consolidated the `blur()` functions by enabling asymmetrical blur2D() to replace `blurRow` and `blurCol`
- compiler warning fixes (explicit unsigned casts)
2024-10-03 21:19:34 +02:00
Blaz Kristan
ba3a61f623 Reduced code size by:
- removing WS2812FX::setMode()
- removing WS2812FX::setColor()
- removing floating point in transition
- color handling modification in set.cpp
- replaced uint8_t with unsigned in function parameters
- inlined WS2812FX::isUpdating()
- (MAY BE BREAKING) alexa & smartnest update
2024-10-02 20:14:25 +02:00
Blaz Kristan
ee380c5377 Replace uint16_t with unsigned for segment data
swap if statements in color_fade
2024-09-30 16:35:40 +02:00
Blaz Kristan
0ae73296cf Update comment 2024-09-29 15:19:37 +02:00
Blaz Kristan
8e78fb4caa Merge branch '0_15' into 0_15__speed_improvements 2024-09-29 14:31:00 +02:00
Blaz Kristan
336da25463 Private global _colorScaled 2024-09-29 14:14:07 +02:00
Damian Schneider
ffbc8c5f70 Reverting addition of bool unScale, added new improvements and fixes
- Added pre-calculation for segment brightness: stored in _segBri. The impact on FPS is not huge but measurable (~1-2FPS in my test conditions)
- Removed `bool unScaled` from `setPixelColor()` function again (it has no/minimal impact on speed but huge impact on flash usage: +850 bytes)
- Removed negative checking in `setPixelColorXY()` and replaced it with a local typecast to unsigned, saves a few instructions (tested and working)
- Changed int8_t to int in `moveX()` and `moveY()`
- Removed a few functions from IRAM as they are now not called for every pixel but only once per segment update
- Removed a `virtualWidth()` call from `ripple_base()`
- Bugfix in `mode_colortwinkle()`
2024-09-29 13:55:00 +02:00
Blaz Kristan
9114867578 Fix compiler error 2024-09-28 18:48:43 +02:00
Blaz Kristan
c842994df5 Pre-calculate virtual
- move SEGCOLOR() to Segment class
- add SEG_H, SEG_W macros
- try to speed up virtualXxxxx()
- compile warning fixes
2024-09-28 18:14:43 +02:00
Damian Schneider
202901b09f bugfix, ESP32 compiler requires the color order to be identical 2024-09-28 15:38:41 +02:00
Damian Schneider
7c0fe1285a updated setPixelColor() and getPixelColor() functions
uint16_t to unsigned to make it consisten throughout the hand-down.
colorFromPaletteWLED now returns uint32_t which saves the conversion to CRGB and back to uint32_t (in most uses at least).
also added (preliminary) CRGBW struct. I tried to use it in place of uint32_t colors but it adds a lot of overhead when passing the struct so reverted to uint32_t in most places.
updated a few FX to use the CRGBW struct and also cleaned some code to improve flash useage.
2024-09-28 15:26:14 +02:00
Damian Schneider
a76a895f1d bugfix 2024-09-27 06:17:26 +02:00
Damian Schneider
b404458369 fixed one forgotten replacement of rgb2hsv_approximate 2024-09-26 18:29:31 +02:00
Damian Schneider
c44b9f8659 Merge remote-tracking branch 'upstream/0_15' into 0_15__speed_improvements 2024-09-26 18:07:33 +02:00
Damian Schneider
bef1ac2668 Added HSV2RGB and RGB2HSV functions for higher accuracy conversions
- also added a struct to handle HSV with 16bit hue better (including some conversions, can be extended easily)
- the functions are optimized for speed and flash use. They are faster and more accurate than what fastled offers (and use much less flash).
- replaced colorHStoRGB() with a call to the new hsv2rgb() function, saving even more flash (new function is untested!)
- the 16bit hue calculations result in an almost perfect conversion from RGB to HSV and back, the maximum error was 1/255 in the cases I tested.
2024-09-26 18:06:41 +02:00
Blaz Kristan
906f8fc2e7 Fix C3 compiler issue. 2024-09-25 18:49:10 +02:00
Blaz Kristan
33cf82a982 Indentations and a few optimisations
Restore addPixelColor() behaviour.
2024-09-23 18:03:17 +02:00
Damian Schneider
0a5400263b removed IRAM_ATTR from inlined function
when the function is inlined into a IRAM_ATTR function, it will also reside in IRAM. Forced inlining is recommended by Espressif if I understand this correctly: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/hardware-abstraction.html
2024-09-22 13:52:56 +02:00
Damian Schneider
17d59d3337 adding initialization to vStrip, added comment on padding bytes 2024-09-22 09:02:42 +02:00
Damian Schneider
a88436c620 revert removal of adding with saturation, renamed 'fast' to 'saturate'
- blurring now uses desaturated adding: it is faster most of the times and blurring adds scaled colors so should rarely (ever?) saturate, I saw no visual difference in tests.
- formatting
2024-09-19 08:49:18 +02:00
Damian Schneider
696290527a cleanup and improved color_add()
- optimized color_add() again: now it is as fast with preserved ratio scaling than the "fast" variant was before (if no scaling is needed, it is even faster). plus it saves 250 bytes of flash
- bugfix in `color_fade()`
- removed a lot of whitespaces
2024-09-18 22:10:27 +02:00
Damian Schneider
686866c6f9 Merge remote-tracking branch 'upstream/0_15' into 0_15__speed_improvements 2024-09-18 08:10:45 +02:00
Blaz Kristan
1ff667b7ef AWS library fix 2024-09-15 10:59:50 +02:00
Damian Schneider
f3137eb0a9 updated Segment::color_from_palette
- gamma correction only where needed
- paletteIndex should be uint8_t (it is only used as that)
note: integrating the new `ColorFromPaletteWLED()` into this would require  a whole lot of code rewrite and would result in more color conversions from 32bit to CRGB. It would be really useful only if CRGB is replaced with native 32bit colors.
2024-09-14 14:49:36 +02:00
Damian Schneider
0e5bd4ed74 remove test printout 2024-09-14 14:11:29 +02:00
Damian Schneider
6a37f25c5d memory improvement: dropped static gamma table
- there already is a method to calculate the table on the fly, there is no need to store it in flash, it can just be calculated at bootup (or cfg change)
2024-09-14 14:10:46 +02:00
Damian Schneider
2afff05014 minor tweak (break instead of continue in setPixelColorXY) 2024-09-14 11:45:27 +02:00
Damian Schneider
d45b4ad134 fixes and consistency 2024-09-13 19:01:54 +02:00
Damian Schneider
ec938f254c removed old code 2024-09-12 21:25:08 +02:00
Damian Schneider
09428dcade inlined getMappedPixelIndex, improved color_add, bugfix in colorFromPalette
inlining getMappedPixelIndex gets rid of function entry instructions (hopefully) so it should be faster.
also added the 'multi color math' trick to color_add function (it will not make much difference but code shrinks by a few bytes)
2024-09-12 16:34:55 +02:00
Damian Schneider
b07658b460 improved Segment::setPixelColorXY a tiny bit
uses less flash so it should be faster (did not notice any FPS difference though)
also cleaned code in ColorFromPaletteWLED (it is not faster, same amount of code)
2024-09-12 14:09:09 +02:00
Damian Schneider
992d11be10 Improvements in get/set PixelColor()
-calculations for virtual strips are done on each call, which is unnecessary. moved them into the if statement.
2024-09-12 08:28:30 +02:00
Damian Schneider
feac45fd0a improvement in color_add
its not faster but cleaner (and uses less flash)
2024-09-12 07:45:49 +02:00
Damian Schneider
934176818f more improvements to color_scale() now even faster.
tested and working, also tested video
2024-09-12 06:43:20 +02:00
Damian Schneider
c3f472fbcb some improvements to consider
no real difference in FPS but code is faster.
also 160bytes smaller, meaning it is actually faster
2024-09-11 21:41:42 +02:00
Frank
2cfd2e1410 Update CONTRIBUTING.md - typo 2024-09-10 20:36:18 +02:00
Frank
058e66c7fc Update CONTRIBUTING.md - adding a hint to avoid force-pushing 2024-09-10 20:32:40 +02:00
Blaz Kristan
dc90a4ed42 Mirroring bugfix. 2024-09-04 23:43:13 +02:00
Frank
ef80abd885 8266 compatibility builds for older chips (another attempt t o solve #3690 and #3685)
some users have reported that releases after 0.14.0 are not working reliably. So we add a few "compat" for 8266 that try to reproduce the buildenv of 0.14.0 as much as possible.

* platform and platform_packages from 0.14.0
* not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48
* due to smaller IRAM, we had to move some functions back from IRAM to normal flash (may cause slowdown)
2024-09-04 23:42:58 +02:00
62 changed files with 4985 additions and 3225 deletions

80
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: WLED Build
# Only included into other workflows
on:
workflow_call:
jobs:
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/release/*.bin
build_output/release/*_ESP02*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm test

28
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: WLED Release CI
on:
push:
tags:
- '*'
jobs:
wled_build:
uses: ./.github/workflows/build.yml
release:
name: Create Release
runs-on: ubuntu-latest
needs: wled_build
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
*.bin.gz

View File

@@ -1,94 +1,11 @@
name: WLED CI
on: [push, pull_request]
on:
push:
branches:
- '*'
pull_request:
jobs:
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/release/*.bin
build_output/release/*_ESP02*.bin.gz
release:
name: Create Release
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm test
wled_build:
uses: ./.github/workflows/build.yml

View File

@@ -14,7 +14,7 @@ A good description helps us to review and understand your proposed changes. For
### Target branch for pull requests
Please make all PRs against the `0_15` branch.
Please make all PRs against the `main` branch.
### Updating your code
While the PR is open - and under review by maintainers - you may be asked to modify your PR source code.
@@ -105,4 +105,4 @@ Good:
There is no hard character limit for a comment within a line,
though as a rule of thumb consider wrapping after 120 characters.
Inline comments are OK if they describe that line only and are not exceedingly wide.
Inline comments are OK if they describe that line only and are not exceedingly wide.

9
package-lock.json generated
View File

@@ -1,18 +1,21 @@
{
"name": "wled",
"version": "0.15.0-b7",
"version": "0.16.0-dev",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wled",
"version": "0.15.0-b7",
"version": "0.16.0-dev",
"license": "ISC",
"dependencies": {
"clean-css": "^5.3.3",
"html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1",
"nodemon": "^3.0.2"
"nodemon": "^3.1.7"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@jridgewell/gen-mapping": {

View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.15.0-b7",
"version": "0.16.0-alpha",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
@@ -27,5 +27,8 @@
"html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1",
"nodemon": "^3.1.7"
},
"engines": {
"node": ">=20.0.0"
}
}

View File

@@ -19,8 +19,9 @@ def _create_dirs(dirs=["map", "release", "firmware"]):
os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True)
def create_release(source):
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name:
release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name_def:
release_name = release_name_def.replace("\\\"", "")
version = _get_cpp_define_value(env, "WLED_VERSION")
release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin")
release_gz_file = release_file + ".gz"

View File

@@ -176,6 +176,7 @@ lib_deps =
extra_scripts = ${scripts_defaults.extra_scripts}
[esp8266]
build_unflags = ${common.build_unflags}
build_flags =
-DESP8266
-DFP_IN_IROM
@@ -242,6 +243,7 @@ lib_deps_compat =
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
platform = espressif32@3.5.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
@@ -263,6 +265,7 @@ lib_deps =
AR_build_flags = -D USERMOD_AUDIOREACTIVE
-D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
AR_lib_deps = kosme/arduinoFFT @ 2.0.1
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
@@ -272,6 +275,7 @@ AR_lib_deps = kosme/arduinoFFT @ 2.0.1
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
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
@@ -280,11 +284,13 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32s2]
;; generic definitions for all ESP32-S2 boards
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2
@@ -298,11 +304,13 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32c3]
;; generic definitions for all ESP32-C3 boards
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
@@ -315,11 +323,13 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32s3]
;; generic definitions for all ESP32-S3 boards
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-DESP32
-DARDUINO_ARCH_ESP32
@@ -333,6 +343,7 @@ build_flags = -g
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
# ------------------------------------------------------------------------------
@@ -345,7 +356,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
@@ -354,13 +365,13 @@ extends = env:nodemcuv2
;; using platform version and build options from WLED 0.14.0
platform = ${esp8266.platform_compat}
platform_packages = ${esp8266.platform_packages_compat}
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP8266_compat #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D
;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9
[env:nodemcuv2_160]
extends = env:nodemcuv2
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D
-D USERMOD_AUDIOREACTIVE
[env:esp8266_2m]
@@ -369,7 +380,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\"
lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m_compat]
@@ -377,12 +388,12 @@ extends = env:esp8266_2m
;; using platform version and build options from WLED 0.14.0
platform = ${esp8266.platform_compat}
platform_packages = ${esp8266.platform_packages_compat}
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP02_compat #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D
[env:esp8266_2m_160]
extends = env:esp8266_2m
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02_160
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\"
-D USERMOD_AUDIOREACTIVE
[env:esp01_1m_full]
@@ -391,7 +402,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
lib_deps = ${esp8266.lib_deps}
@@ -400,12 +411,12 @@ extends = env:esp01_1m_full
;; using platform version and build options from WLED 0.14.0
platform = ${esp8266.platform_compat}
platform_packages = ${esp8266.platform_packages_compat}
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP01_compat -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
[env:esp01_1m_full_160]
extends = env:esp01_1m_full
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA
-D USERMOD_AUDIOREACTIVE
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
@@ -414,7 +425,7 @@ board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps}
@@ -426,7 +437,7 @@ board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_8M #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
@@ -442,7 +453,7 @@ board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_16M #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
@@ -458,7 +469,7 @@ board_build.flash_mode = dio
;platform = ${esp32.platform}
;platform_packages = ${esp32.platform_packages}
;build_unflags = ${common.build_unflags}
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET
; ${esp32.AR_build_flags}
;lib_deps = ${esp32.lib_deps}
; ${esp32.AR_lib_deps}
@@ -473,7 +484,7 @@ platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
${esp32.AR_build_flags}
lib_deps = ${esp32.lib_deps}
@@ -489,7 +500,7 @@ board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_build.partitions = ${esp32.extended_partitions}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_WROVER
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\"
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
-D DATA_PINS=25
${esp32.AR_build_flags}
@@ -503,7 +514,7 @@ platform_packages = ${esp32c3.platform_packages}
framework = arduino
board = esp32-c3-devkitm-1
board_build.partitions = ${esp32.default_partitions}
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\"
-D WLED_WATCHDOG_TIMEOUT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
@@ -520,7 +531,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_16MB_opi
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\"
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -543,7 +554,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_opi
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\"
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -565,7 +576,7 @@ board = esp32s3camlcd ;; this is the only standard board with "opi_opi"
board_build.arduino.memory_type = opi_opi
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_WROOM-2
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\"
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -590,7 +601,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_4M_qspi
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
-DLOLIN_WIFI_FIX ; seems to work much better with this
@@ -611,7 +622,7 @@ board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = qio
board_build.f_flash = 80000000L
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\"
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0

View File

@@ -5,7 +5,7 @@
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = WLED_tasmota_1M # define as many as you need
default_envs = WLED_generic8266_1M, esp32dev_V4_dio80 # put the name(s) of your own build environment here. You can define as many as you need
#----------
# SAMPLE
@@ -28,8 +28,8 @@ lib_deps = ${esp8266.lib_deps}
; robtillaart/SHT85@~0.3.3
; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
; ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE
; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following
; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
@@ -37,7 +37,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
;
; Set a release name that may be used to distinguish required binary for flashing
; -D WLED_RELEASE_NAME=ESP32_MULTI_USREMODS
; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
;
; disable specific features
; -D WLED_DISABLE_OTA
@@ -111,7 +111,6 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
;
; Use 4 Line Display usermod with SPI display
; -D USERMOD_FOUR_LINE_DISPLAY
; -D USE_ALT_DISPlAY # mandatory
; -DFLD_SPI_DEFAULT
; -D FLD_TYPE=SSD1306_SPI64
; -D FLD_PIN_CLOCKSPI=14
@@ -142,7 +141,8 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
;
; Use Audioreactive usermod and configure I2S microphone
; -D USERMOD_AUDIOREACTIVE
; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT
; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps}
; -D AUDIOPIN=-1
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
; -D I2S_SDPIN=36
@@ -158,17 +158,22 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D USERMOD_POV_DISPLAY
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
; -D STATUSLED=16
;
;
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
; -D SERVERNAME="\"WLED\""
;
;
; set the number of LEDs
; -D DEFAULT_LED_COUNT=30
; -D PIXEL_COUNTS=30
; or this for multiple outputs
; -D PIXEL_COUNTS=30,30
;
; set the default LED type
; -D DEFAULT_LED_TYPE=22 # see const.h (TYPE_xxxx)
; -D LED_TYPES=22 # see const.h (TYPE_xxxx)
; or this for multiple outputs
; -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB
;
; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
;
; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs
; -D ABL_MILLIAMPS_DEFAULT=850
@@ -177,9 +182,6 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; enable IR by setting remote type
; -D IRTYPE=0 # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
;
; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
;
; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue # needed only for classic ESP32 rev.1
;
@@ -237,14 +239,13 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLE
lib_deps = ${esp8266.lib_deps}
[env:esp32dev_qio80]
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags} ;; optional - includes USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
@@ -252,26 +253,25 @@ board_build.flash_mode = qio
;; experimental ESP32 env using ESP-IDF V4.4.x
;; Warning: this build environment is not stable!!
;; please erase your device before installing.
extends = esp32_idf_V4 # based on newer "esp-idf V4" platform environment
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32_idf_V4.default_partitions}
board_build.partitions = ${esp32.default_partitions} ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = dio
[env:esp32s2_saola]
extends = esp32s2
board = esp32-s2-saola-1
platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
framework = arduino
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.flash_mode = qio
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags}
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1
@@ -308,7 +308,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed.
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_debug]
@@ -363,36 +363,48 @@ board_upload.flash_size = 2MB
board_upload.maximum_size = 2097152
[env:wemos_shield_esp32]
extends = esp32 ;; use default esp32 platform
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags}
-D WLED_RELEASE_NAME=\"ESP32_wemos_shield\"
-D DATA_PINS=16
-D RLYPIN=19
-D BTNPIN=17
-D IRPIN=18
-D UWLED_USE_MY_CONFIG
-UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
-D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
-D USERMOD_AUDIOREACTIVE
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5
olikraus/U8g2 @ ^2.28.8
https://github.com/blazoncek/arduinoFFT.git
OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE
olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
board_build.partitions = ${esp32.default_partitions}
[env:m5atom]
board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39
[env:esp32_pico-D4]
extends = esp32 ;; use default esp32 platform
board = pico32 ;; pico32-D4 is different from the standard esp32dev
;; hardware details from https://github.com/srg74/WLED-ESP32-pico
build_flags = ${common.build_flags} ${esp32.build_flags}
-D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"'
-D WLED_DISABLE_ADALIGHT ;; no serial-to-USB chip on this board - better to disable serial protocols
-D DATA_PINS=2,18 ;; LED pins
-D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR
${esp32.AR_build_flags} ;; include USERMOD_AUDIOREACTIVE
-D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default
;; Audioreactive settings for on-board microphone (ICS-43432)
-D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14
-D SR_SQUELCH=5 -D SR_GAIN=30
lib_deps = ${esp32.lib_deps}
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
[env:m5atom]
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39
[env:sp501e]
board = esp_wroom_02
@@ -415,7 +427,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
lib_deps = ${esp8266.lib_deps}
[env:Athom_15w_RGBCW] ;15w bulb
@@ -425,7 +437,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data
@@ -491,9 +503,8 @@ lib_deps = ${esp8266.lib_deps}
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
extends = esp32 ;; use default esp32 platform
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC
@@ -501,7 +512,7 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU
-D DATA_PINS=12
-D RLYPIN=27
-D BTNPIN=34
-D DEFAULT_LED_COUNT=6
-D PIXEL_COUNTS=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
@@ -517,5 +528,4 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU
monitor_filters = esp32_exception_decoder
lib_deps =
${esp32.lib_deps}
TFT_eSPI @ ^2.3.70
board_build.partitions = ${esp32.default_partitions}
TFT_eSPI @ 2.5.33 ;; this is the last version that compiles with the WLED default framework - newer versions require platform = espressif32 @ ^6.3.2

View File

@@ -12,7 +12,7 @@
# Welcome to my project WLED! ✨
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
## ⚙️ Features
- WS2812FX library with more than 100 special effects
@@ -21,7 +21,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- Segments to set different effects and colors to user defined parts of the LED string
- Settings page - configuration via the network
- Access Point and station mode - automatic failsafe AP
- Up to 10 LED outputs per instance
- [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance
- Support for RGBW strips
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
- Presets can be used to automatically execute API calls

View File

@@ -101,6 +101,7 @@ function adoptVersionAndRepo(html) {
async function minify(str, type = "plain") {
const options = {
collapseWhitespace: true,
conservativeCollapse: true, // preserve spaces in text
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
minifyCSS: true,

View File

@@ -102,9 +102,9 @@ private:
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
uint32_t ms = time.ms % 1000;
uint8_t b0 = (cos8(ms * 64 / 1000) - 128) * 2;
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
uint8_t b1 = (sin8(ms * 64 / 1000) - 128) * 2;
uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2;
setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
}

View File

@@ -50,7 +50,7 @@ public:
#else // ESP32 ESP32S3 and ESP32C3
temperature = roundf(temperatureRead() * 10) / 10;
#endif
if(presetToActivate != 0){
// Check if temperature has exceeded the activation threshold
if (temperature >= activationThreshold) {
// Update the state flag if not already set
@@ -58,7 +58,7 @@ public:
isAboveThreshold = true;
}
// Check if a 'high temperature' preset is configured and it's not already active
if (presetToActivate != 0 && currentPreset != presetToActivate) {
if (currentPreset != presetToActivate) {
// If a playlist is active, store it for reactivation later
if (currentPlaylist > 0) {
previousPlaylist = currentPlaylist;
@@ -101,6 +101,7 @@ public:
}
}
}
}
#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,18 @@
#pragma once
/*
@title MoonModules WLED - audioreactive usermod
@file audio_source.h
@repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED
@Authors https://github.com/MoonModules/WLED/commits/mdev/
@Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details)
@license Licensed under the EUPL-1.2 or later
*/
#ifdef ARDUINO_ARCH_ESP32
#include <Wire.h>
#include "wled.h"
#include <driver/i2s.h>
#include <driver/adc.h>
@@ -15,6 +28,9 @@
#define SRate_t int
#endif
constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not change! I2S_NUM_1 possible but this has
// strong limitations -> no MCLK routing, no ADC support, no PDM support
//#include <driver/i2s_std.h>
//#include <driver/i2s_pdm.h>
//#include <driver/i2s_tdm.h>
@@ -22,7 +38,7 @@
// see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents
// and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
// there are two things in these MCUs that could lead to problems with audio processing:
// * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x)
// * single core, so FFT task might slow down other things like LED updates
@@ -49,6 +65,11 @@
// data type requested from the I2S driver - currently we always use 32bit
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
#if defined(WLED_ENABLE_HUB75MATRIX) && defined(CONFIG_IDF_TARGET_ESP32)
// this is bitter, but necessary to survive
#define I2S_USE_16BIT_SAMPLES
#endif
#ifdef I2S_USE_16BIT_SAMPLES
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
#define I2S_datatype int16_t
@@ -71,7 +92,7 @@
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
*/
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 6))
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 8)) // should be fixed in IDF 4.4.5, however arduino-esp32 2.0.14 - 2.0.17 did an "I2S rollback" to 4.4.4
// espressif bug: only_left has no sound, left and right are swapped
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)
@@ -105,6 +126,9 @@
#endif
// max number of samples for a single i2s_read --> size of global buffer.
#define I2S_SAMPLES_MAX 512 // same as samplesFFT
/* Interface class
AudioSource serves as base class for all microphone types
This enables accessing all microphones with one single interface
@@ -148,38 +172,61 @@ class AudioSource {
virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing
// Private constructor, to make sure it is not callable except from derived classes
AudioSource(SRate_t sampleRate, int blockSize, float sampleScale) :
AudioSource(SRate_t sampleRate, int blockSize, float sampleScale, bool i2sMaster) :
_sampleRate(sampleRate),
_blockSize(blockSize),
_initialized(false),
_i2sMaster(i2sMaster),
_sampleScale(sampleScale)
{};
SRate_t _sampleRate; // Microphone sampling rate
int _blockSize; // I2S block size
bool _initialized; // Gets set to true if initialization is successful
bool _i2sMaster; // when false, ESP32 will be in I2S SLAVE mode (for devices that only operate in MASTER mode). Only works in newer IDF >= 4.4.x
float _sampleScale; // pre-scaling factor for I2S samples
I2S_datatype newSampleBuffer[I2S_SAMPLES_MAX+4] = { 0 }; // global buffer for i2s_read
};
/* Basic I2S microphone source
All functions are marked virtual, so derived classes can replace them
WARNING: i2sMaster = false is experimental, and most likely will not work
*/
class I2SSource : public AudioSource {
public:
I2SSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
AudioSource(sampleRate, blockSize, sampleScale) {
I2SSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
AudioSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.mode = i2sMaster ? i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX) : i2s_mode_t(I2S_MODE_SLAVE | I2S_MODE_RX),
.sample_rate = _sampleRate,
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
.bits_per_sample = I2S_SAMPLE_RESOLUTION, // slave mode: may help to set this to 96000, as the other side (master) controls sample rates
.channel_format = I2S_MIC_CHANNEL,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
//.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
#ifdef WLEDMM_FASTPATH
#ifdef WLED_ENABLE_HUB75MATRIX
.intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL1, // HUB75 seems to get into trouble if we allocate a higher priority interrupt
.dma_buf_count = 18, // 100ms buffer (128 * dma_buf_count / sampleRate)
#else
#if CONFIG_IDF_TARGET_ESP32 && !defined(BOARD_HAS_PSRAM) // still need to test on boards with PSRAM
.intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // IRAM flag reduces missed samples
#else
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // seems to reduce noise
#endif
.dma_buf_count = 24, // 140ms buffer (128 * dma_buf_count / sampleRate)
#endif
#else
#ifdef WLED_ENABLE_HUB75MATRIX
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // HUB75 seems to get into trouble if we allocate a higher priority interrupt
#else
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
#endif
.dma_buf_count = 8,
#endif
.dma_buf_len = _blockSize,
.use_apll = 0,
//.fixed_mclk = 0,
.bits_per_chan = I2S_data_size,
#else
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
@@ -196,7 +243,7 @@ class I2SSource : public AudioSource {
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
if (!PinManager::allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
!PinManager::allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pins: ws=%d, sd=%d\n", i2swsPin, i2ssdPin);
ERRORSR_PRINTF("\nAR: Failed to allocate I2S pins: ws=%d, sd=%d\n", i2swsPin, i2ssdPin);
return;
}
}
@@ -204,7 +251,7 @@ class I2SSource : public AudioSource {
// i2ssckPin needs special treatment, since it might be unused on PDM mics
if (i2sckPin != I2S_PIN_NO_CHANGE) {
if (!PinManager::allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pins: sck=%d\n", i2sckPin);
ERRORSR_PRINTF("\nAR: Failed to allocate I2S pins: sck=%d\n", i2sckPin);
return;
}
} else {
@@ -226,10 +273,17 @@ class I2SSource : public AudioSource {
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided. PDM is not supported on ESP32-S2. PDM RX not supported on ESP32-C3
_config.channel_format =I2S_PDM_MIC_CHANNEL; // seems that PDM mono mode always uses left channel.
_config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality
//_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; // not needed
#endif
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
if ((_i2sMaster == false) && (_config.mode & I2S_MODE_SLAVE)) { // I2S slave mode (experimental).
// Seems we need to drive clocks in slave mode
_config.use_apll = true;
_config.fixed_mclk = 512 * int(_config.sample_rate);
}
if (mclkPin != I2S_PIN_NO_CHANGE) {
_config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality, and to avoid glitches.
// //_config.fixed_mclk = 512 * _sampleRate;
@@ -244,13 +298,28 @@ class I2SSource : public AudioSource {
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if (ESP.getChipRevision() == 0) _config.use_apll = false; // APLL is broken on ESP32 revision 0
#endif
#if defined(WLED_ENABLE_HUB75MATRIX)
_config.use_apll = false; // APLL needed for HUB75 DMA driver ?
#endif
#endif
if (_i2sMaster == false) {
DEBUG_PRINTLN(F("AR: Warning - i2S SLAVE mode is experimental!"));
if (_config.mode & I2S_MODE_PDM) {
// APLL does not work in DAC or PDM "Slave Mode": https://github.com/espressif/esp-idf/issues/1244, https://github.com/espressif/esp-idf/issues/2634
_config.use_apll = false;
_config.fixed_mclk = 0;
}
if ((_config.mode & I2S_MODE_MASTER) != 0) {
DEBUG_PRINTLN("AR: (oops) I2S SLAVE mode requested but not configured!");
}
}
// Reserve the master clock pin if provided
_mclkPin = mclkPin;
if (mclkPin != I2S_PIN_NO_CHANGE) {
if(!PinManager::allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pin: MCLK=%d\n", mclkPin);
ERRORSR_PRINTF("\nAR: Failed to allocate I2S pin: MCLK=%d\n", mclkPin);
return;
} else
_routeMclk(mclkPin);
@@ -268,32 +337,36 @@ class I2SSource : public AudioSource {
//DEBUGSR_PRINTF("[AR] I2S: SD=%d, WS=%d, SCK=%d, MCLK=%d\n", i2ssdPin, i2swsPin, i2sckPin, mclkPin);
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
esp_err_t err = i2s_driver_install(AR_I2S_PORT, &_config, 0, nullptr);
if (err != ESP_OK) {
DEBUGSR_PRINTF("AR: Failed to install i2s driver: %d\n", err);
ERRORSR_PRINTF("AR: Failed to install i2s driver: %d\n", err);
return;
}
DEBUGSR_PRINTF("AR: I2S#0 driver %s aPLL; fixed_mclk=%d.\n", _config.use_apll? "uses":"without", _config.fixed_mclk);
DEBUGSR_PRINTF("AR: %d bits, Sample scaling factor = %6.4f\n", _config.bits_per_sample, _sampleScale);
if (_config.mode & I2S_MODE_PDM) {
if(_config.mode & I2S_MODE_MASTER) {
if (_config.mode & I2S_MODE_PDM) {
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in PDM MASTER mode."));
} else {
} else {
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in MASTER mode."));
}
} else {
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in SLAVE mode."));
}
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
err = i2s_set_pin(AR_I2S_PORT, &_pinConfig);
if (err != ESP_OK) {
DEBUGSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err);
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
ERRORSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err);
i2s_driver_uninstall(AR_I2S_PORT); // uninstall already-installed driver
return;
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
err = i2s_set_clk(I2S_NUM_0, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed.
err = i2s_set_clk(AR_I2S_PORT, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed.
if (err != ESP_OK) {
DEBUGSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err);
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
ERRORSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err);
i2s_driver_uninstall(AR_I2S_PORT); // uninstall already-installed driver
return;
}
#endif
@@ -302,7 +375,7 @@ class I2SSource : public AudioSource {
virtual void deinitialize() {
_initialized = false;
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
esp_err_t err = i2s_driver_uninstall(AR_I2S_PORT);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
return;
@@ -318,17 +391,20 @@ class I2SSource : public AudioSource {
if (_initialized) {
esp_err_t err;
size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */
I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
memset(buffer, 0, sizeof(float) * num_samples); // clear output buffer
I2S_datatype *newSamples = newSampleBuffer; // use global input buffer
if (num_samples > I2S_SAMPLES_MAX) num_samples = I2S_SAMPLES_MAX; // protect the buffer from overflow
err = i2s_read(AR_I2S_PORT, (void *)newSamples, num_samples * sizeof(I2S_datatype), &bytes_read, portMAX_DELAY);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
return;
}
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s
if (bytes_read != sizeof(newSamples)) {
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
if (bytes_read != (num_samples * sizeof(I2S_datatype))) {
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", num_samples * sizeof(I2S_datatype), bytes_read);
return;
}
@@ -383,6 +459,10 @@ class I2SSource : public AudioSource {
*/
class ES7243 : public I2SSource {
private:
// I2C initialization functions for ES7243
void _es7243I2cBegin() {
Wire.setClock(100000);
}
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES7243_ADDR
@@ -398,6 +478,7 @@ class ES7243 : public I2SSource {
}
void _es7243InitAdc() {
_es7243I2cBegin();
_es7243I2cWrite(0x00, 0x01);
_es7243I2cWrite(0x06, 0x00);
_es7243I2cWrite(0x05, 0x1B);
@@ -407,17 +488,26 @@ class ES7243 : public I2SSource {
}
public:
ES7243(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
I2SSource(sampleRate, blockSize, sampleScale) {
ES7243(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN(F("ES7243:: initialize();"));
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
DEBUGSR_PRINTLN("ES7243:: initialize();");
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
// ERRORSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
// return;
// }
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
ERRORSR_PRINTF("\nAR: invalid ES7243 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
// return;
// }
// First route mclk, then configure ADC over I2C, then configure I2S
_es7243InitAdc();
@@ -435,14 +525,16 @@ public:
*/
class ES8388Source : public I2SSource {
private:
// I2C initialization functions for ES8388
void _es8388I2cBegin() {
Wire.setClock(100000);
}
void _es8388I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES8388_ADDR
Wire.beginTransmission(0x10);
#define ES8388_ADDR 0x10 // default address
#else
#ifndef ES8388_ADDR
#define ES8388_ADDR 0x10 // default address
#endif
Wire.beginTransmission(ES8388_ADDR);
#endif
Wire.write((uint8_t)reg);
Wire.write((uint8_t)val);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
@@ -459,6 +551,7 @@ class ES8388Source : public I2SSource {
// Registries are decimal, settings are binary as that's how everything is listed in the docs
// ...which makes it easier to reference the docs.
//
_es8388I2cBegin();
_es8388I2cWrite( 8,0b00000000); // I2S to slave
_es8388I2cWrite( 2,0b11110011); // Power down DEM and STM
_es8388I2cWrite(43,0b10000000); // Set same LRCK
@@ -524,16 +617,28 @@ class ES8388Source : public I2SSource {
public:
ES8388Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale) {
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN(F("ES8388Source:: initialize();"));
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
DEBUGSR_PRINTLN("ES8388Source:: initialize();");
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
// ERRORSR_PRINTF("\nAR: invalid I2S ES8388 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
// return;
// }
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
// Workaround: Set I2C pins here, which will also set them globally.
// Bug also exists in ES7243.
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
ERRORSR_PRINTF("\nAR: invalid ES8388 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
// return;
// }
// First route mclk, then configure ADC over I2C, then configure I2S
_es8388InitAdc();
@@ -546,6 +651,302 @@ class ES8388Source : public I2SSource {
};
/* ES8311 Sound Module
This is an I2S sound processing unit that requires initialization over
I2C before I2S data can be received.
*/
class ES8311Source : public I2SSource {
private:
// I2C initialization functions for es8311
void _es8311I2cBegin() {
Wire.setClock(100000);
}
void _es8311I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES8311_ADDR
#define ES8311_ADDR 0x18 // default address is... foggy
#endif
Wire.beginTransmission(ES8311_ADDR);
Wire.write((uint8_t)reg);
Wire.write((uint8_t)val);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
if (i2cErr != 0) {
DEBUGSR_PRINTF("AR: ES8311 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8311_ADDR, reg, val);
}
}
void _es8311InitAdc() {
//
// Currently only tested with the ESP32-P4 boards with the onboard mic.
// Datasheet with I2C commands: https://dl.xkwy2018.com/downloads/RK3588/01_Official%20Release/04_Product%20Line%20Branch_NVR/02_Key%20Device%20Specifications/ES8311%20DS.pdf
//
_es8311I2cBegin();
_es8311I2cWrite(0x00, 0b00011111); // RESET, default value
_es8311I2cWrite(0x45, 0b00000000); // GP, default value
_es8311I2cWrite(0x01, 0b00111010); // CLOCK MANAGER was 0b00110000 trying 0b00111010 (MCLK enable?)
_es8311I2cWrite(0x02, 0b00000000); // 22050hz calculated
_es8311I2cWrite(0x05, 0b00000000); // 22050hz calculated
_es8311I2cWrite(0x03, 0b00010000); // 22050hz calculated
_es8311I2cWrite(0x04, 0b00010000); // 22050hz calculated
_es8311I2cWrite(0x07, 0b00000000); // 22050hz calculated
_es8311I2cWrite(0x08, 0b11111111); // 22050hz calculated
_es8311I2cWrite(0x06, 0b11100011); // 22050hz calculated
_es8311I2cWrite(0x16, 0b00100100); // ADC was 0b00000011 trying 0b00100100 was good
_es8311I2cWrite(0x0B, 0b00000000); // SYSTEM at default
_es8311I2cWrite(0x0C, 0b00100000); // SYSTEM was 0b00001111 trying 0b00100000
_es8311I2cWrite(0x10, 0b00010011); // SYSTEM was 0b00011111 trying 0b00010011
_es8311I2cWrite(0x11, 0b01111100); // SYSTEM was 0b01111111 trying 0b01111100
_es8311I2cWrite(0x00, 0b11000000); // *** RESET (again - seems important?)
_es8311I2cWrite(0x01, 0b00111010); // *** CLOCK MANAGER was 0b00111111 trying 0b00111010 (again?? seems important)
_es8311I2cWrite(0x14, 0b00010000); // *** SYSTEM was 0b00011010 trying 0b00010000 (PGA gain)
_es8311I2cWrite(0x0A, 0b00001000); // *** SDP OUT, was 0b00001100 trying 0b00001000 (I2S 32-bit)
_es8311I2cWrite(0x0E, 0b00000010); // *** SYSTEM was 0b00000010 trying 0b00000010
_es8311I2cWrite(0x0F, 0b01000100); // SYSTEM was 0b01000100
_es8311I2cWrite(0x15, 0b00010000); // ADC soft ramp (disabled 0000xxxx)
_es8311I2cWrite(0x1B, 0b00000101); // ADC soft-mute was 0b00000101
_es8311I2cWrite(0x1C, 0b01100101); // ADC EQ and offset freeze at 0b01100101 (bad at 0b00101100)
_es8311I2cWrite(0x17, 0b10111111); // ADC volume was 0b11111111 trying ADC volume 0b10111111 = 0db (maxgain)
_es8311I2cWrite(0x18, 0b10000001); // ADC ALC enabled and AutoMute disabled.
// _es8311I2cWrite(0x19, 0b11110100); // ADC ALC max and min - not sure how best to use this, default seems fine
}
public:
ES8311Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN("es8311Source:: initialize();");
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
// ERRORSR_PRINTF("\nAR: invalid I2S es8311 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
// return;
// }
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
// Workaround: Set I2C pins here, which will also set them globally.
// Bug also exists in ES7243.
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
ERRORSR_PRINTF("\nAR: invalid es8311 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
// return;
// }
// First route mclk, then configure ADC over I2C, then configure I2S
_es8311InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}
void deinitialize() {
I2SSource::deinitialize();
}
};
class WM8978Source : public I2SSource {
private:
// I2C initialization functions for WM8978
void _wm8978I2cBegin() {
Wire.setClock(400000);
}
void _wm8978I2cWrite(uint8_t reg, uint16_t val) {
#ifndef WM8978_ADDR
#define WM8978_ADDR 0x1A
#endif
char buf[2];
buf[0] = (reg << 1) | ((val >> 8) & 0X01);
buf[1] = val & 0XFF;
Wire.beginTransmission(WM8978_ADDR);
Wire.write((const uint8_t*)buf, 2);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
if (i2cErr != 0) {
DEBUGSR_PRINTF("AR: WM8978 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, WM8978_ADDR, reg, val);
}
}
void _wm8978InitAdc() {
// https://www.mouser.com/datasheet/2/76/WM8978_v4.5-1141768.pdf
// Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring.
// Registries are decimal, settings are 9-bit binary as that's how everything is listed in the docs
// ...which makes it easier to reference the docs.
//
_wm8978I2cBegin();
_wm8978I2cWrite( 0,0b000000000); // Reset all settings
_wm8978I2cWrite( 1,0b000111110); // Power Management 1 - power off most things, but enable mic bias and I/O tie-off to help mitigate mic leakage.
_wm8978I2cWrite( 2,0b110111111); // Power Management 2 - enable output and amp stages (amps may lift signal but it works better on the ADCs)
_wm8978I2cWrite( 3,0b000001100); // Power Management 3 - enable L&R output mixers
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
_wm8978I2cWrite( 4,0b001010000); // Audio Interface - standard I2S, 24-bit
#else
_wm8978I2cWrite( 4,0b001001000); // Audio Interface - left-justified I2S, 24-bit
#endif
_wm8978I2cWrite( 6,0b000000000); // Clock generation control - use external mclk
_wm8978I2cWrite( 7,0b000000100); // Sets sample rate to ~24kHz (only used for internal calculations, not I2S)
_wm8978I2cWrite(14,0b010001000); // 128x ADC oversampling - high pass filter disabled as it kills the bass response
_wm8978I2cWrite(43,0b000110000); // Mute signal paths we don't use
_wm8978I2cWrite(44,0b100000000); // Disconnect microphones
_wm8978I2cWrite(45,0b111000000); // Mute signal paths we don't use
_wm8978I2cWrite(46,0b111000000); // Mute signal paths we don't use
_wm8978I2cWrite(47,0b001000000); // 0dB gain on left line-in
_wm8978I2cWrite(48,0b001000000); // 0dB gain on right line-in
_wm8978I2cWrite(49,0b000000011); // Mixer thermal shutdown enable and unused IOs to 30kΩ
_wm8978I2cWrite(50,0b000010110); // Output mixer enable only left bypass at 0dB gain
_wm8978I2cWrite(51,0b000010110); // Output mixer enable only right bypass at 0dB gain
_wm8978I2cWrite(52,0b110111001); // Left line-out enabled at 0dB gain
_wm8978I2cWrite(53,0b110111001); // Right line-out enabled at 0db gain
_wm8978I2cWrite(54,0b111000000); // Mute left speaker output
_wm8978I2cWrite(55,0b111000000); // Mute right speaker output
}
public:
WM8978Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN("WM8978Source:: initialize();");
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
// ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
// return;
// }
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
// Workaround: Set I2C pins here, which will also set them globally.
// Bug also exists in ES7243.
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
ERRORSR_PRINTF("\nAR: invalid WM8978 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
// return;
// }
// First route mclk, then configure ADC over I2C, then configure I2S
_wm8978InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}
void deinitialize() {
I2SSource::deinitialize();
}
};
class AC101Source : public I2SSource {
private:
// I2C initialization functions for WM8978
void _ac101I2cBegin() {
Wire.setClock(400000);
}
void _ac101I2cWrite(uint8_t reg_addr, uint16_t val) {
#ifndef AC101_ADDR
#define AC101_ADDR 0x1A
#endif
char send_buff[3];
send_buff[0] = reg_addr;
send_buff[1] = uint8_t((val >> 8) & 0xff);
send_buff[2] = uint8_t(val & 0xff);
Wire.beginTransmission(AC101_ADDR);
Wire.write((const uint8_t*)send_buff, 3);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
if (i2cErr != 0) {
DEBUGSR_PRINTF("AR: AC101 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, AC101_ADDR, reg_addr, val);
}
}
void _ac101InitAdc() {
// https://files.seeedstudio.com/wiki/ReSpeaker_6-Mics_Circular_Array_kit_for_Raspberry_Pi/reg/AC101_User_Manual_v1.1.pdf
// This supports mostly the older AI Thinkier AudioKit A1S that has an AC101 chip
// Newer versions use the ES3833 chip - which we also support.
_ac101I2cBegin();
#define CHIP_AUDIO_RS 0x00
#define SYSCLK_CTRL 0x03
#define MOD_CLK_ENA 0x04
#define MOD_RST_CTRL 0x05
#define I2S_SR_CTRL 0x06
#define I2S1LCK_CTRL 0x10
#define I2S1_SDOUT_CTRL 0x11
#define I2S1_MXR_SRC 0x13
#define ADC_DIG_CTRL 0x40
#define ADC_APC_CTRL 0x50
#define ADC_SRC 0x51
#define ADC_SRCBST_CTRL 0x52
#define OMIXER_DACA_CTRL 0x53
#define OMIXER_SR 0x54
#define HPOUT_CTRL 0x56
_ac101I2cWrite(CHIP_AUDIO_RS, 0x123); // I think anything written here is a reset as 0x123 is kinda suss.
delay(100);
_ac101I2cWrite(SYSCLK_CTRL, 0b0000100000001000); // System Clock is I2S MCLK
_ac101I2cWrite(MOD_CLK_ENA, 0b1000000000001000); // I2S and ADC Clock Enable
_ac101I2cWrite(MOD_RST_CTRL, 0b1000000000001000); // I2S and ADC Clock Enable
_ac101I2cWrite(I2S_SR_CTRL, 0b0100000000000000); // set to 22050hz just in case
_ac101I2cWrite(I2S1LCK_CTRL, 0b1000000000110000); // set I2S slave mode, 24-bit word size
_ac101I2cWrite(I2S1_SDOUT_CTRL, 0b1100000000000000); // I2S enable Left/Right channels
_ac101I2cWrite(I2S1_MXR_SRC, 0b0010001000000000); // I2S digital Mixer, ADC L/R data
_ac101I2cWrite(ADC_SRCBST_CTRL, 0b0000000000000100); // mute all boosts. last 3 bits are reserved/default
_ac101I2cWrite(OMIXER_SR, 0b0000010000001000); // Line L/R to output mixer
_ac101I2cWrite(ADC_SRC, 0b0000010000001000); // Line L/R to ADC
_ac101I2cWrite(ADC_DIG_CTRL, 0b1000000000000000); // Enable ADC
_ac101I2cWrite(ADC_APC_CTRL, 0b1011100100000000); // ADC L/R enabled, 0dB gain
_ac101I2cWrite(OMIXER_DACA_CTRL, 0b0011111110000000); // L/R Analog Output Mixer enabled, headphone DC offset default
_ac101I2cWrite(HPOUT_CTRL, 0b1111101111110001); // Headphone out from Analog Mixer stage, no reduction in volume
}
public:
AC101Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN("AC101Source:: initialize();");
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
// ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
// return;
// }
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
// Workaround: Set I2C pins here, which will also set them globally.
// Bug also exists in ES7243.
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
ERRORSR_PRINTF("\nAR: invalid AC101 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
return;
}
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
// return;
// }
// First route mclk, then configure ADC over I2C, then configure I2S
_ac101InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}
void deinitialize() {
I2SSource::deinitialize();
}
};
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
#warning this MCU does not support analog sound input
@@ -553,7 +954,7 @@ class ES8388Source : public I2SSource {
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// ADC over I2S is only availeable in "classic" ESP32
// ADC over I2S is only available in "classic" ESP32
/* ADC over I2S Microphone
This microphone is an ADC pin sampled via the I2S interval
@@ -563,7 +964,7 @@ class ES8388Source : public I2SSource {
class I2SAdcSource : public I2SSource {
public:
I2SAdcSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
I2SSource(sampleRate, blockSize, sampleScale) {
I2SSource(sampleRate, blockSize, sampleScale, true) {
_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
.sample_rate = _sampleRate,
@@ -587,18 +988,18 @@ class I2SAdcSource : public I2SSource {
AudioSourceType getType(void) {return(Type_I2SAdc);}
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN(F("I2SAdcSource:: initialize()."));
DEBUGSR_PRINTLN("I2SAdcSource:: initialize().");
_myADCchannel = 0x0F;
if(!PinManager::allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
ERRORSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
return;
}
_audioPin = audioPin;
// Determine Analog channel. Only Channels on ADC1 are supported
int8_t channel = digitalPinToAnalogChannel(_audioPin);
if (channel > 9) {
DEBUGSR_PRINTF("Incompatible GPIO used for analog audio input: %d\n", _audioPin);
if ((channel < 0) || (channel > 9)) { // channel == -1 means "not an ADC pin"
DEBUGSR_PRINTF("AR: Incompatible GPIO used for analog audio input: %d\n", _audioPin);
return;
} else {
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
@@ -608,16 +1009,16 @@ class I2SAdcSource : public I2SSource {
// Install Driver
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
ERRORSR_PRINTF("Failed to install i2s driver: %d\n", err);
return;
}
adc1_config_width(ADC_WIDTH_BIT_12); // ensure that ADC runs with 12bit resolution
// adc1_config_width(ADC_WIDTH_BIT_12); // ensure that ADC runs with 12bit resolution - should not be needed, because i2s_set_adc_mode does that any way
// Enable I2S mode of ADC
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
DEBUGSR_PRINTF("AR: Failed to set i2s adc mode: %d\n", err);
return;
}
@@ -754,17 +1155,17 @@ class I2SAdcSource : public I2SSource {
// a user recommended this: Try to set .communication_format to I2S_COMM_FORMAT_STAND_I2S and call i2s_set_clk() after i2s_set_pin().
class SPH0654 : public I2SSource {
public:
SPH0654(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
I2SSource(sampleRate, blockSize, sampleScale)
SPH0654(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster)
{}
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN(F("SPH0654:: initialize();"));
DEBUGSR_PRINTLN("SPH0654:: initialize();");
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// these registers are only existing in "classic" ESP32
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
REG_SET_BIT(I2S_TIMING_REG(AR_I2S_PORT), BIT(9));
REG_SET_BIT(I2S_CONF_REG(AR_I2S_PORT), I2S_RX_MSB_SHIFT);
#else
#warning FIX ME! Please.
#endif

View File

@@ -0,0 +1,84 @@
# Deep Sleep usermod
This usermod unleashes the low power capabilities of th ESP: when you power off your LEDs (using the UI power button or a macro) the ESP will be put into deep sleep mode, reducing power consumption to a minimum.
During deep sleep the ESP is shut down completely: no WiFi, no CPU, no outputs. The only way to wake it up is to use an external signal or a button. Once it wakes from deep sleep it reboots so ***make sure to use a boot-up preset.***
# A word of warning
When you disable the WLED option 'Turn LEDs on after power up/reset' and 'DelaySleep' is set to zero the ESP will go into deep sleep directly after power-up and only start WLED after it has been woken up.
If the ESP can not be awoken from deep sleep due to a wrong configuration it has to be factory reset, disabling sleep at power-up. There is no other way to wake it up.
# Power Consumption in deep sleep
The current drawn by the ESP in deep sleep mode depends on the type and is in the range of 5uA-20uA (as in micro Amperes):
- ESP32: 10uA
- ESP32 S3: 8uA
- ESP32 S2: 20uA
- ESP32 C3: 5uA
- ESP8266: 20uA (not supported in this usermod)
However, there is usually additional components on a controller that increase the value:
- Power LED: the power LED on a ESP board draws 500uA - 1mA
- LDO: the voltage regulator also draws idle current. Depending on the type used this can be around 50uA up to 10mA (LM1117). Special low power LDOs with very low idle currents do exist
- Digital LEDs: WS2812 for example draw a current of about 1mA per LED. To make good use of this usermod it is required to power them off using MOSFETs or a Relay
For lowest power consumption, remove the Power LED and make sure your board does not use an LM1117. On a ESP32 C3 Supermini with the power LED removed (no other modifications) powered through the 5V pin I measured a current draw of 50uA in deep sleep.
# Useable GPIOs
The GPIOs that can be used to wake the ESP from deep sleep are limited. Only pins connected to the internal RTC unit can be used:
- ESP32: GPIO 0, 2, 4, 12-15, 25-39
- ESP32 S3: GPIO 0-21
- ESP32 S2: GPIO 0-21
- ESP32 C3: GPIO 0-5
- ESP8266 is not supported in this usermod
You can however use the selected wake-up pin normally in WLED, it only gets activated as a wake-up pin when your LEDs are powered down.
# Limitations
To keep this usermod simple and easy to use, it is a very basic implementation of the low-power capabilities provided by the ESP. If you need more advanced control you are welcome to implement your own version based on this usermod.
## Usermod installation
Use `#define USERMOD_DEEP_SLEEP` in wled.h or `-D USERMOD_DEEP_SLEEP` in your platformio.ini. Settings can be changed in the usermod config UI.
### Define Settings
There are five parameters you can set:
- GPIO: the pin to use for wake-up
- WakeWhen High/Low: the pin state that triggers the wake-up
- Pull-up/down disable: enable or disable the internal pullup resistors during sleep (does not affect normal use while running)
- Wake after: if set larger than 0, ESP will automatically wake-up after this many seconds (Turn LEDs on after power up/reset is overriden, it will always turn on)
- Delay sleep: if set larger than 0, ESP will not go to sleep for this many seconds after you power it off. Timer is reset when switched back on during this time.
To override the default settings, place the `#define` in wled.h or add `-D DEEPSLEEP_xxx` to your platformio_override.ini build flags
* `DEEPSLEEP_WAKEUPPIN x` - define the pin to be used for wake-up, see list of useable pins above. The pin can be used normally as a button pin in WLED.
* `DEEPSLEEP_WAKEWHENHIGH` - if defined, wakes up when pin goes high (default is low)
* `DEEPSLEEP_DISABLEPULL` - if defined, internal pullup/pulldown is disabled in deep sleep (default is ebnabled)
* `DEEPSLEEP_WAKEUPINTERVAL` - number of seconds after which a wake-up happens automatically, sooner if button is pressed. 0 = never. accuracy is about 2%
* `DEEPSLEEP_DELAY` - delay between power-off and sleep
example for env build flags:
`-D USERMOD_DEEP_SLEEP`
`-D DEEPSLEEP_WAKEUPPIN=4`
`-D DEEPSLEEP_DISABLEPULL=0` ;enable pull-up/down resistors by default
`-D DEEPSLEEP_WAKEUPINTERVAL=43200` ;wake up after 12 hours (or when button is pressed)
### Hardware Setup
To wake from deep-sleep an external trigger signal on the configured GPIO is required. When using timed-only wake-up, use a GPIO that has an on-board pull-up resistor (GPIO0 on most boards). When using push-buttons it is highly recommended to use an external pull-up resistor: not all IO's on all devices have properly working internal resistors.
Using sensors like PIR, IR, touch sensors or any other sensor with a digital output can be used instead of a button.
now go on and save some power
@dedehai
## Change log
2024-09
* Initial version
2024-10
* Changed from #define configuration to UI configuration

View File

@@ -0,0 +1,227 @@
#pragma once
#include "wled.h"
#include "driver/rtc_io.h"
#ifdef ESP8266
#error The "Deep Sleep" usermod does not support ESP8266
#endif
#ifndef DEEPSLEEP_WAKEUPPIN
#define DEEPSLEEP_WAKEUPPIN 0
#endif
#ifndef DEEPSLEEP_WAKEWHENHIGH
#define DEEPSLEEP_WAKEWHENHIGH 0
#endif
#ifndef DEEPSLEEP_DISABLEPULL
#define DEEPSLEEP_DISABLEPULL 1
#endif
#ifndef DEEPSLEEP_WAKEUPINTERVAL
#define DEEPSLEEP_WAKEUPINTERVAL 0
#endif
#ifndef DEEPSLEEP_DELAY
#define DEEPSLEEP_DELAY 1
#endif
RTC_DATA_ATTR bool powerup = true; // variable in RTC data persists on a reboot
class DeepSleepUsermod : public Usermod {
private:
bool enabled = true;
bool initDone = false;
uint8_t wakeupPin = DEEPSLEEP_WAKEUPPIN;
uint8_t wakeWhenHigh = DEEPSLEEP_WAKEWHENHIGH; // wake up when pin goes high if 1, triggers on low if 0
bool noPull = true; // use pullup/pulldown resistor
int wakeupAfter = DEEPSLEEP_WAKEUPINTERVAL; // in seconds, <=0: button only
int sleepDelay = DEEPSLEEP_DELAY; // in seconds, 0 = immediate
int delaycounter = 5; // delay deep sleep at bootup until preset settings are applied
uint32_t lastLoopTime = 0;
// string that are used multiple time (this will save some flash memory)
static const char _name[];
static const char _enabled[];
bool pin_is_valid(uint8_t wakePin) {
#ifdef CONFIG_IDF_TARGET_ESP32 //ESP32: GPIOs 0,2,4, 12-15, 25-39 can be used for wake-up
if (wakePin == 0 || wakePin == 2 || wakePin == 4 || (wakePin >= 12 && wakePin <= 15) || (wakePin >= 25 && wakePin <= 27) || (wakePin >= 32 && wakePin <= 39)) {
return true;
}
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2) //ESP32 S3 & S3: GPIOs 0-21 can be used for wake-up
if (wakePin <= 21) {
return true;
}
#endif
#ifdef CONFIG_IDF_TARGET_ESP32C3 // ESP32 C3: GPIOs 0-5 can be used for wake-up
if (wakePin <= 5) {
return true;
}
#endif
DEBUG_PRINTLN(F("Error: unsupported deep sleep wake-up pin"));
return false;
}
public:
inline void enable(bool enable) { enabled = enable; } // Enable/Disable the usermod
inline bool isEnabled() { return enabled; } //Get usermod enabled/disabled state
// setup is called at boot (or in this case after every exit of sleep mode)
void setup() {
//TODO: if the de-init of RTC pins is required to do it could be done here
//rtc_gpio_deinit(wakeupPin);
initDone = true;
}
void loop() {
if (!enabled || !offMode) { // disabled or LEDs are on
lastLoopTime = 0; // reset timer
return;
}
if (sleepDelay > 0) {
if(lastLoopTime == 0) lastLoopTime = millis(); // initialize
if (millis() - lastLoopTime < sleepDelay * 1000) {
return; // wait until delay is over
}
}
if(powerup == false && delaycounter) { // delay sleep in case a preset is being loaded and turnOnAtBoot is disabled (handleIO() does enable offMode temporarily in this case)
delaycounter--;
if(delaycounter == 2 && offMode) { // force turn on, no matter the settings (device is bricked if user set sleepDelay=0, no bootup preset and turnOnAtBoot=false)
if (briS == 0) bri = 10; // turn on at low brightness
else bri = briS;
strip.setBrightness(bri); // needed to make handleIO() not turn off LEDs (really? does not help in bootup preset)
offMode = false;
applyPresetWithFallback(0, CALL_MODE_INIT, FX_MODE_STATIC, 0); // try to apply preset 0, fallback to static
if (rlyPin >= 0) {
digitalWrite(rlyPin, (rlyMde ? HIGH : LOW)); // turn relay on TODO: this should be done by wled, what function to call?
}
}
return;
}
DEBUG_PRINTLN(F("DeepSleep UM: entering deep sleep..."));
powerup = false; // turn leds on in all subsequent bootups (overrides Turn LEDs on after power up/reset' at reboot)
if(!pin_is_valid(wakeupPin)) return;
esp_err_t halerror = ESP_OK;
pinMode(wakeupPin, INPUT); // make sure GPIO is input with pullup/pulldown disabled
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); //disable all wake-up sources (just in case)
if(wakeupAfter)
esp_sleep_enable_timer_wakeup((uint64_t)wakeupAfter * (uint64_t)1e6); //sleep for x seconds
#if defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32 C3
if(noPull)
gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_FLOATING);
else { // enable pullup/pulldown resistor
if(wakeWhenHigh)
gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLDOWN_ONLY);
else
gpio_sleep_set_pull_mode((gpio_num_t)wakeupPin, GPIO_PULLUP_ONLY);
}
if(wakeWhenHigh)
halerror = esp_deep_sleep_enable_gpio_wakeup(1<<wakeupPin, ESP_GPIO_WAKEUP_GPIO_HIGH);
else
halerror = esp_deep_sleep_enable_gpio_wakeup(1<<wakeupPin, ESP_GPIO_WAKEUP_GPIO_LOW);
#else // ESP32, S2, S3
gpio_pulldown_dis((gpio_num_t)wakeupPin); // disable internal pull resistors for GPIO use
gpio_pullup_dis((gpio_num_t)wakeupPin);
if(noPull) {
rtc_gpio_pullup_dis((gpio_num_t)wakeupPin);
rtc_gpio_pulldown_dis((gpio_num_t)wakeupPin);
}
else { // enable pullup/pulldown resistor for RTC use
if(wakeWhenHigh)
rtc_gpio_pulldown_en((gpio_num_t)wakeupPin);
else
rtc_gpio_pullup_en((gpio_num_t)wakeupPin);
}
if(wakeWhenHigh)
halerror = esp_sleep_enable_ext0_wakeup((gpio_num_t)wakeupPin, HIGH); // only RTC pins can be used
else
halerror = esp_sleep_enable_ext0_wakeup((gpio_num_t)wakeupPin, LOW);
#endif
delay(1); // wait for pin to be ready
if(halerror == ESP_OK) esp_deep_sleep_start(); // go into deep sleep
else DEBUG_PRINTLN(F("sleep failed"));
}
//void connected() {} //unused, this is called every time the WiFi is (re)connected
void addToConfig(JsonObject& root) override
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
//save these vars persistently whenever settings are saved
top["gpio"] = wakeupPin;
top["wakeWhen"] = wakeWhenHigh;
top["pull"] = noPull;
top["wakeAfter"] = wakeupAfter;
top["delaySleep"] = sleepDelay;
}
bool readFromConfig(JsonObject& root) override
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top["gpio"], wakeupPin, DEEPSLEEP_WAKEUPPIN);
if (!pin_is_valid(wakeupPin)) {
wakeupPin = 0; // set to 0 if invalid
configComplete = false; // Mark config as incomplete if pin is invalid
}
configComplete &= getJsonValue(top["wakeWhen"], wakeWhenHigh, DEEPSLEEP_WAKEWHENHIGH); // default to wake on low
configComplete &= getJsonValue(top["pull"], noPull, DEEPSLEEP_DISABLEPULL); // default to no pullup/pulldown
configComplete &= getJsonValue(top["wakeAfter"], wakeupAfter, DEEPSLEEP_WAKEUPINTERVAL);
configComplete &= getJsonValue(top["delaySleep"], sleepDelay, DEEPSLEEP_DELAY);
return configComplete;
}
/*
* appendConfigData() is called when user enters usermod settings page
* it may add additional metadata for certain entry fields (adding drop down is possible)
* be careful not to add too much as oappend() buffer is limited to 3k
*/
void appendConfigData() override
{
// dropdown for wakeupPin
oappend(SET_F("dd=addDropdown('DeepSleep','gpio');"));
for (int pin = 0; pin < 40; pin++) { // possible pins are in range 0-39
if (pin_is_valid(pin)) {
oappend(SET_F("addOption(dd,'"));
oappend(String(pin).c_str());
oappend(SET_F("',"));
oappend(String(pin).c_str());
oappend(SET_F(");"));
}
}
oappend(SET_F("dd=addDropdown('DeepSleep','wakeWhen');"));
oappend(SET_F("addOption(dd,'Low',0);"));
oappend(SET_F("addOption(dd,'High',1);"));
oappend(SET_F("addInfo('DeepSleep:pull',1,'','-up/down disable: ');")); // first string is suffix, second string is prefix
oappend(SET_F("addInfo('DeepSleep:wakeAfter',1,'seconds <i>(0 = never)<i>');"));
oappend(SET_F("addInfo('DeepSleep:delaySleep',1,'seconds <i>(0 = sleep at powerup)<i>');")); // first string is suffix, second string is prefix
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() {
return USERMOD_ID_DEEP_SLEEP;
}
};
// add more strings here to reduce flash memory usage
const char DeepSleepUsermod::_name[] PROGMEM = "DeepSleep";
const char DeepSleepUsermod::_enabled[] PROGMEM = "enabled";

View File

@@ -9,7 +9,7 @@ The actual / original code that controls the LED modes is from Adam Zeloof. I ta
It was quite a bit more work than I hoped, but I got there eventually :)
## Requirements
* "ESP Rotary" by Lennart Hennigs, v1.5.0 or higher: https://github.com/LennartHennigs/ESPRotary
* "ESP Rotary" by Lennart Hennigs, v2.1.1 or higher: https://github.com/LennartHennigs/ESPRotary
## Usermod installation
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D RGB_ROTARY_ENCODER`.
@@ -20,7 +20,7 @@ ESP32:
extends = env:esp32dev
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D RGB_ROTARY_ENCODER
lib_deps = ${esp32.lib_deps}
lennarthennigs/ESP Rotary@^1.5.0
lennarthennigs/ESP Rotary@^2.1.1
```
ESP8266 / D1 Mini:
@@ -29,7 +29,7 @@ ESP8266 / D1 Mini:
extends = env:d1_mini
build_flags = ${common.build_flags_esp8266} -D RGB_ROTARY_ENCODER
lib_deps = ${esp8266.lib_deps}
lennarthennigs/ESP Rotary@^1.5.0
lennarthennigs/ESP Rotary@^2.1.1
```
## How to connect the board to your ESP

View File

@@ -49,7 +49,7 @@ private:
void setColor(int r, int g, int b)
{
strip.setColor(0, r, g, b);
strip.getMainSegment().setColor(0, RGBW32(r, g, b, 0));
stateUpdated(CALL_MODE_DIRECT_CHANGE);
char msg[18] {};
sprintf(msg, "rgb(%d,%d,%d)", r, g, b);

View File

@@ -7,11 +7,12 @@ platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
upload_speed = 460800
-D USERMOD_FOUR_LINE_DISPLAY
-D FLD_TYPE=SH1106
-D I2CSCLPIN=27
-D I2CSDAPIN=26
lib_deps =
${esp32.lib_deps}
U8g2@~2.34.4
Wire

View File

@@ -1,16 +1,8 @@
# I2C/SPI 4 Line Display Usermod ALT
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display"
"usermod_v2_rotary_encoder_ui"
This usermod could be used in compination with `usermod_v2_rotary_encoder_ui_ALT`.
The core of these usermods are a copy of the originals. The main changes are to the FourLineDisplay usermod.
The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
## Functionalities
Press the encoder to cycle through the options:
* Brightness
@@ -18,26 +10,18 @@ Press the encoder to cycle through the options:
* Intensity
* Palette
* Effect
* Main Color (only if display is used)
* Saturation (only if display is used)
* Main Color
* Saturation
Press and hold the encoder to display Network Info. If AP is active, it will display AP, SSID and password
Press and hold the encoder to display Network Info. If AP is active, it will display the AP, SSID and Password
Also shows if the timer is enabled
Also shows if the timer is enabled.
[See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI)
## Installation
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.
Copy the example `platformio_override.sample.ini` from the usermod_v2_rotary_encoder_ui_ALT folder to the root directory of your particular build and rename it to `platformio_override.ini`.
This file should be placed in the same directory as `platformio.ini`.
Then, to activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
Copy the example `platformio_override.sample.ini` to the root directory of your particular build.
## Configuration

View File

@@ -0,0 +1,14 @@
[platformio]
default_envs = esp32dev
[env:esp32dev]
board = esp32dev
platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_ROTARY_ENCODER_UI
-D USERMOD_ROTARY_ENCODER_GPIO=INPUT
-D ENCODER_DT_PIN=21
-D ENCODER_CLK_PIN=23
-D ENCODER_SW_PIN=0

View File

@@ -1,16 +1,8 @@
# Rotary Encoder UI Usermod ALT
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display"
"usermod_v2_rotary_encoder_ui"
This usermod supports the UI of the `usermod_v2_rotary_encoder_ui_ALT`.
The core of these usermods are a copy of the originals. The main changes are to the FourLineDisplay usermod.
The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
## Functionalities
Press the encoder to cycle through the options:
* Brightness
@@ -21,8 +13,7 @@ Press the encoder to cycle through the options:
* Main Color (only if display is used)
* Saturation (only if display is used)
Press and hold the encoder to display Network Info
if AP is active, it will display the AP, SSID and Password
Press and hold the encoder to display Network Info. If AP is active, it will display the AP, SSID and Password
Also shows if the timer is enabled.
@@ -30,9 +21,7 @@ Also shows if the timer is enabled.
## Installation
Copy the example `platformio_override.sample.ini` to the root directory of your particular build and rename it to `platformio_override.ini`.
To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, or add `-D USE_ALT_DISPlAY` to your `platformio_override.ini` file
Copy the example `platformio_override.sample.ini` to the root directory of your particular build.
### Define Your Options
@@ -40,7 +29,6 @@ To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
also tells this usermod that the display is available
(see the Four Line Display usermod `readme.md` for more details)
* `USE_ALT_DISPlAY` - Mandatory to use Four Line Display
* `ENCODER_DT_PIN` - defaults to 18
* `ENCODER_CLK_PIN` - defaults to 5
* `ENCODER_SW_PIN` - defaults to 19
@@ -50,7 +38,7 @@ To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE
### PlatformIO requirements
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
No special requirements.
## Change Log

View File

@@ -31,14 +31,14 @@ public:
//strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//select first two segments (background color + FX settable)
WS2812FX::Segment &seg = strip.getSegment(0);
Segment &seg = strip.getSegment(0);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF)));
strip.getSegment(0).setOption(0, false);
strip.getSegment(0).setOption(2, false);
//other segments are text
for (int i = 1; i < 10; i++)
{
WS2812FX::Segment &seg = strip.getSegment(i);
Segment &seg = strip.getSegment(i);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF)));
strip.getSegment(i).setOption(0, true);
strip.setBrightness(64);
@@ -80,61 +80,61 @@ public:
void displayTime(byte hour, byte minute)
{
bool isToHour = false; //true if minute > 30
strip.setSegment(0, 0, 64); // background
strip.setSegment(1, 0, 2); //It is
strip.getSegment(0).setGeometry(0, 64); // background
strip.getSegment(1).setGeometry(0, 2); //It is
strip.setSegment(2, 0, 0);
strip.setSegment(3, 0, 0); //disable minutes
strip.setSegment(4, 0, 0); //past
strip.setSegment(6, 0, 0); //to
strip.setSegment(8, 0, 0); //disable o'clock
strip.getSegment(2).setGeometry(0, 0);
strip.getSegment(3).setGeometry(0, 0); //disable minutes
strip.getSegment(4).setGeometry(0, 0); //past
strip.getSegment(6).setGeometry(0, 0); //to
strip.getSegment(8).setGeometry(0, 0); //disable o'clock
if (hour < 24) //valid time, display
{
if (minute == 30)
{
strip.setSegment(2, 3, 6); //half
strip.setSegment(3, 0, 0); //minutes
strip.getSegment(2).setGeometry(3, 6); //half
strip.getSegment(3).setGeometry(0, 0); //minutes
}
else if (minute == 15 || minute == 45)
{
strip.setSegment(3, 0, 0); //minutes
strip.getSegment(3).setGeometry(0, 0); //minutes
}
else if (minute == 10)
{
//strip.setSegment(5, 6, 8); //ten
//strip.getSegment(5).setGeometry(6, 8); //ten
}
else if (minute == 5)
{
//strip.setSegment(5, 16, 18); //five
//strip.getSegment(5).setGeometry(16, 18); //five
}
else if (minute == 0)
{
strip.setSegment(3, 0, 0); //minutes
strip.getSegment(3).setGeometry(0, 0); //minutes
//hourChime();
}
else
{
strip.setSegment(3, 18, 22); //minutes
strip.getSegment(3).setGeometry(18, 22); //minutes
}
//past or to?
if (minute == 0)
{ //full hour
strip.setSegment(3, 0, 0); //disable minutes
strip.setSegment(4, 0, 0); //disable past
strip.setSegment(6, 0, 0); //disable to
strip.setSegment(8, 60, 64); //o'clock
strip.getSegment(3).setGeometry(0, 0); //disable minutes
strip.getSegment(4).setGeometry(0, 0); //disable past
strip.getSegment(6).setGeometry(0, 0); //disable to
strip.getSegment(8).setGeometry(60, 64); //o'clock
}
else if (minute > 34)
{
//strip.setSegment(6, 22, 24); //to
//strip.getSegment(6).setGeometry(22, 24); //to
//minute = 60 - minute;
isToHour = true;
}
else
{
//strip.setSegment(4, 24, 27); //past
//strip.getSegment(4).setGeometry(24, 27); //past
//isToHour = false;
}
}
@@ -143,68 +143,68 @@ public:
if (minute <= 4)
{
strip.setSegment(3, 0, 0); //nothing
strip.setSegment(5, 0, 0); //nothing
strip.setSegment(6, 0, 0); //nothing
strip.setSegment(8, 60, 64); //o'clock
strip.getSegment(3).setGeometry(0, 0); //nothing
strip.getSegment(5).setGeometry(0, 0); //nothing
strip.getSegment(6).setGeometry(0, 0); //nothing
strip.getSegment(8).setGeometry(60, 64); //o'clock
}
else if (minute <= 9)
{
strip.setSegment(5, 16, 18); // five past
strip.setSegment(4, 24, 27); //past
strip.getSegment(5).setGeometry(16, 18); // five past
strip.getSegment(4).setGeometry(24, 27); //past
}
else if (minute <= 14)
{
strip.setSegment(5, 6, 8); // ten past
strip.setSegment(4, 24, 27); //past
strip.getSegment(5).setGeometry(6, 8); // ten past
strip.getSegment(4).setGeometry(24, 27); //past
}
else if (minute <= 19)
{
strip.setSegment(5, 8, 12); // quarter past
strip.setSegment(3, 0, 0); //minutes
strip.setSegment(4, 24, 27); //past
strip.getSegment(5).setGeometry(8, 12); // quarter past
strip.getSegment(3).setGeometry(0, 0); //minutes
strip.getSegment(4).setGeometry(24, 27); //past
}
else if (minute <= 24)
{
strip.setSegment(5, 12, 16); // twenty past
strip.setSegment(4, 24, 27); //past
strip.getSegment(5).setGeometry(12, 16); // twenty past
strip.getSegment(4).setGeometry(24, 27); //past
}
else if (minute <= 29)
{
strip.setSegment(5, 12, 18); // twenty-five past
strip.setSegment(4, 24, 27); //past
strip.getSegment(5).setGeometry(12, 18); // twenty-five past
strip.getSegment(4).setGeometry(24, 27); //past
}
else if (minute <= 34)
{
strip.setSegment(5, 3, 6); // half past
strip.setSegment(3, 0, 0); //minutes
strip.setSegment(4, 24, 27); //past
strip.getSegment(5).setGeometry(3, 6); // half past
strip.getSegment(3).setGeometry(0, 0); //minutes
strip.getSegment(4).setGeometry(24, 27); //past
}
else if (minute <= 39)
{
strip.setSegment(5, 12, 18); // twenty-five to
strip.setSegment(6, 22, 24); //to
strip.getSegment(5).setGeometry(12, 18); // twenty-five to
strip.getSegment(6).setGeometry(22, 24); //to
}
else if (minute <= 44)
{
strip.setSegment(5, 12, 16); // twenty to
strip.setSegment(6, 22, 24); //to
strip.getSegment(5).setGeometry(12, 16); // twenty to
strip.getSegment(6).setGeometry(22, 24); //to
}
else if (minute <= 49)
{
strip.setSegment(5, 8, 12); // quarter to
strip.setSegment(3, 0, 0); //minutes
strip.setSegment(6, 22, 24); //to
strip.getSegment(5).setGeometry(8, 12); // quarter to
strip.getSegment(3).setGeometry(0, 0); //minutes
strip.getSegment(6).setGeometry(22, 24); //to
}
else if (minute <= 54)
{
strip.setSegment(5, 6, 8); // ten to
strip.setSegment(6, 22, 24); //to
strip.getSegment(5).setGeometry(6, 8); // ten to
strip.getSegment(6).setGeometry(22, 24); //to
}
else if (minute <= 59)
{
strip.setSegment(5, 16, 18); // five to
strip.setSegment(6, 22, 24); //to
strip.getSegment(5).setGeometry(16, 18); // five to
strip.getSegment(6).setGeometry(22, 24); //to
}
//hours
@@ -220,45 +220,45 @@ public:
switch (hour)
{
case 1:
strip.setSegment(7, 27, 29);
strip.getSegment(7).setGeometry(27, 29);
break; //one
case 2:
strip.setSegment(7, 35, 37);
strip.getSegment(7).setGeometry(35, 37);
break; //two
case 3:
strip.setSegment(7, 29, 32);
strip.getSegment(7).setGeometry(29, 32);
break; //three
case 4:
strip.setSegment(7, 32, 35);
strip.getSegment(7).setGeometry(32, 35);
break; //four
case 5:
strip.setSegment(7, 37, 40);
strip.getSegment(7).setGeometry(37, 40);
break; //five
case 6:
strip.setSegment(7, 43, 45);
strip.getSegment(7).setGeometry(43, 45);
break; //six
case 7:
strip.setSegment(7, 40, 43);
strip.getSegment(7).setGeometry(40, 43);
break; //seven
case 8:
strip.setSegment(7, 45, 48);
strip.getSegment(7).setGeometry(45, 48);
break; //eight
case 9:
strip.setSegment(7, 48, 50);
strip.getSegment(7).setGeometry(48, 50);
break; //nine
case 10:
strip.setSegment(7, 54, 56);
strip.getSegment(7).setGeometry(54, 56);
break; //ten
case 11:
strip.setSegment(7, 50, 54);
strip.getSegment(7).setGeometry(50, 54);
break; //eleven
case 12:
strip.setSegment(7, 56, 60);
strip.getSegment(7).setGeometry(56, 60);
break; //twelve
}
selectWordSegments(true);
applyMacro(1);
applyPreset(1);
}
void timeOfDay()

View File

@@ -1,305 +0,0 @@
#include "wled.h"
/*
* This v1 usermod file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
* If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
*
* Consider the v2 usermod API if you need a more advanced feature set!
*/
uint8_t minuteLast = 99;
int dayBrightness = 128;
int nightBrightness = 16;
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
saveMacro(14, "A=128", false);
saveMacro(15, "A=64", false);
saveMacro(16, "A=16", false);
saveMacro(1, "&FX=0&R=255&G=255&B=255", false);
//strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//select first two segments (background color + FX settable)
Segment &seg = strip.getSegment(0);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF)));
strip.getSegment(0).setOption(0, false);
strip.getSegment(0).setOption(2, false);
//other segments are text
for (int i = 1; i < 10; i++)
{
Segment &seg = strip.getSegment(i);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF)));
strip.getSegment(i).setOption(0, true);
strip.setBrightness(128);
}
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
void selectWordSegments(bool state)
{
for (int i = 1; i < 10; i++)
{
//Segment &seg = strip.getSegment(i);
strip.getSegment(i).setOption(0, state);
// strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//seg.mode = 12;
//seg.palette = 1;
//strip.setBrightness(255);
}
strip.getSegment(0).setOption(0, !state);
}
void hourChime()
{
//strip.resetSegments();
selectWordSegments(true);
colorUpdated(CALL_MODE_FX_CHANGED);
//savePreset(255);
selectWordSegments(false);
//strip.getSegment(0).setOption(0, true);
strip.getSegment(0).setOption(2, true);
applyPreset(12);
colorUpdated(CALL_MODE_FX_CHANGED);
}
void displayTime(byte hour, byte minute)
{
bool isToHour = false; //true if minute > 30
strip.setSegment(0, 0, 64); // background
strip.setSegment(1, 0, 2); //It is
strip.setSegment(2, 0, 0);
strip.setSegment(3, 0, 0); //disable minutes
strip.setSegment(4, 0, 0); //past
strip.setSegment(6, 0, 0); //to
strip.setSegment(8, 0, 0); //disable o'clock
if (hour < 24) //valid time, display
{
if (minute == 30)
{
strip.setSegment(2, 3, 6); //half
strip.setSegment(3, 0, 0); //minutes
}
else if (minute == 15 || minute == 45)
{
strip.setSegment(3, 0, 0); //minutes
}
else if (minute == 10)
{
//strip.setSegment(5, 6, 8); //ten
}
else if (minute == 5)
{
//strip.setSegment(5, 16, 18); //five
}
else if (minute == 0)
{
strip.setSegment(3, 0, 0); //minutes
//hourChime();
}
else
{
strip.setSegment(3, 18, 22); //minutes
}
//past or to?
if (minute == 0)
{ //full hour
strip.setSegment(3, 0, 0); //disable minutes
strip.setSegment(4, 0, 0); //disable past
strip.setSegment(6, 0, 0); //disable to
strip.setSegment(8, 60, 64); //o'clock
}
else if (minute > 34)
{
//strip.setSegment(6, 22, 24); //to
//minute = 60 - minute;
isToHour = true;
}
else
{
//strip.setSegment(4, 24, 27); //past
//isToHour = false;
}
}
else
{ //temperature display
}
//byte minuteRem = minute %10;
if (minute <= 4)
{
strip.setSegment(3, 0, 0); //nothing
strip.setSegment(5, 0, 0); //nothing
strip.setSegment(6, 0, 0); //nothing
strip.setSegment(8, 60, 64); //o'clock
}
else if (minute <= 9)
{
strip.setSegment(5, 16, 18); // five past
strip.setSegment(4, 24, 27); //past
}
else if (minute <= 14)
{
strip.setSegment(5, 6, 8); // ten past
strip.setSegment(4, 24, 27); //past
}
else if (minute <= 19)
{
strip.setSegment(5, 8, 12); // quarter past
strip.setSegment(3, 0, 0); //minutes
strip.setSegment(4, 24, 27); //past
}
else if (minute <= 24)
{
strip.setSegment(5, 12, 16); // twenty past
strip.setSegment(4, 24, 27); //past
}
else if (minute <= 29)
{
strip.setSegment(5, 12, 18); // twenty-five past
strip.setSegment(4, 24, 27); //past
}
else if (minute <= 34)
{
strip.setSegment(5, 3, 6); // half past
strip.setSegment(3, 0, 0); //minutes
strip.setSegment(4, 24, 27); //past
}
else if (minute <= 39)
{
strip.setSegment(5, 12, 18); // twenty-five to
strip.setSegment(6, 22, 24); //to
}
else if (minute <= 44)
{
strip.setSegment(5, 12, 16); // twenty to
strip.setSegment(6, 22, 24); //to
}
else if (minute <= 49)
{
strip.setSegment(5, 8, 12); // quarter to
strip.setSegment(3, 0, 0); //minutes
strip.setSegment(6, 22, 24); //to
}
else if (minute <= 54)
{
strip.setSegment(5, 6, 8); // ten to
strip.setSegment(6, 22, 24); //to
}
else if (minute <= 59)
{
strip.setSegment(5, 16, 18); // five to
strip.setSegment(6, 22, 24); //to
}
//hours
if (hour > 23)
return;
if (isToHour)
hour++;
if (hour > 12)
hour -= 12;
if (hour == 0)
hour = 12;
switch (hour)
{
case 1:
strip.setSegment(7, 27, 29);
break; //one
case 2:
strip.setSegment(7, 35, 37);
break; //two
case 3:
strip.setSegment(7, 29, 32);
break; //three
case 4:
strip.setSegment(7, 32, 35);
break; //four
case 5:
strip.setSegment(7, 37, 40);
break; //five
case 6:
strip.setSegment(7, 43, 45);
break; //six
case 7:
strip.setSegment(7, 40, 43);
break; //seven
case 8:
strip.setSegment(7, 45, 48);
break; //eight
case 9:
strip.setSegment(7, 48, 50);
break; //nine
case 10:
strip.setSegment(7, 54, 56);
break; //ten
case 11:
strip.setSegment(7, 50, 54);
break; //eleven
case 12:
strip.setSegment(7, 56, 60);
break; //twelve
}
selectWordSegments(true);
applyMacro(1);
}
void timeOfDay() {
// NOT USED: use timed macros instead
//Used to set brightness dependant of time of day - lights dimmed at night
//monday to thursday and sunday
if ((weekday(localTime) == 6) | (weekday(localTime) == 7)) {
if (hour(localTime) > 0 | hour(localTime) < 8) {
strip.setBrightness(nightBrightness);
}
else {
strip.setBrightness(dayBrightness);
}
}
else {
if (hour(localTime) < 6 | hour(localTime) >= 22) {
strip.setBrightness(nightBrightness);
}
else {
strip.setBrightness(dayBrightness);
}
}
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
if (minute(localTime) != minuteLast)
{
updateLocalTime();
//timeOfDay();
minuteLast = minute(localTime);
displayTime(hour(localTime), minute(localTime));
if (minute(localTime) == 0){
hourChime();
}
if (minute(localTime) == 1){
//turn off background segment;
strip.getSegment(0).setOption(2, false);
//applyPreset(255);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
#include <vector>
#include "const.h"
#include "bus_manager.h"
#define FASTLED_INTERNAL //remove annoying pragma messages
#define USE_GET_MILLISECOND_TIMER
@@ -42,10 +43,30 @@
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
#endif
extern bool realtimeRespectLedMaps; // used in getMappedPixelIndex()
extern byte realtimeMode; // used in getMappedPixelIndex()
/* Not used in all effects yet */
#define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME strip.getFrameTime()
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2)
#define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive
#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
#define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core
#else
#define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY
#endif
#define FPS_UNLIMITED 0
// FPS calculation (can be defined as compile flag for debugging)
#ifndef FPS_CALC_AVG
#define FPS_CALC_AVG 7 // average FPS calculation over this many frames (moving average)
#endif
#ifndef FPS_MULTIPLIER
#define FPS_MULTIPLIER 1 // dev option: multiplier to get sub-frame FPS without floats
#endif
#define FPS_CALC_SHIFT 7 // bit shift for fixed point math
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
@@ -68,16 +89,14 @@
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments())
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT strip._segments[strip.getCurrSegmentId()]
#define SEGENV strip._segments[strip.getCurrSegmentId()]
//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x])
//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength()
#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */
#define SEGCOLOR(x) Segment::getCurrentColor(x)
#define SEGPALETTE Segment::getCurrentPalette()
#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */
#define SEGLEN Segment::vLength()
#define SEG_W Segment::vWidth()
#define SEG_H Segment::vHeight()
#define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN)
// some common colors
@@ -189,7 +208,7 @@
#define FX_MODE_COLORTWINKLE 74
#define FX_MODE_LAKE 75
#define FX_MODE_METEOR 76
#define FX_MODE_METEOR_SMOOTH 77
//#define FX_MODE_METEOR_SMOOTH 77 // merged with meteor
#define FX_MODE_RAILWAY 78
#define FX_MODE_RIPPLE 79
#define FX_MODE_TWINKLEFOX 80
@@ -354,6 +373,7 @@ typedef struct Segment {
};
uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows
uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows
// note: two bytes of padding are added here
char *name;
// runtime data
@@ -382,7 +402,7 @@ typedef struct Segment {
uint32_t _stepT;
uint32_t _callT;
uint8_t *_dataT;
uint16_t _dataLenT;
unsigned _dataLenT;
TemporarySegmentData()
: _dataT(nullptr) // just in case...
, _dataLenT(0)
@@ -400,15 +420,20 @@ typedef struct Segment {
uint8_t _reserved : 4;
};
};
uint16_t _dataLen;
static uint16_t _usedSegmentData;
// perhaps this should be per segment, not static
uint8_t _default_palette; // palette number that gets assigned to pal0
unsigned _dataLen;
static unsigned _usedSegmentData;
static uint8_t _segBri; // brightness of segment for current effect
static unsigned _vLength; // 1D dimension used for current effect
static unsigned _vWidth, _vHeight; // 2D dimensions used for current effect
static uint32_t _currentColors[NUM_COLORS]; // colors used for current effect
static bool _colorScaled; // color has been scaled prior to setPixelColor() call
static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette())
static CRGBPalette16 _randomPalette; // actual random palette
static CRGBPalette16 _newRandomPalette; // target random palette
static uint16_t _lastPaletteChange; // last random palette change time in millis()/1000
static uint16_t _lastPaletteBlend; // blend palette according to set Transition Delay in millis()%0xFFFF
static uint16_t _transitionprogress; // current transition progress 0 - 0xFFFF
#ifndef WLED_DISABLE_MODE_BLEND
static bool _modeBlend; // mode/effect blending semaphore
#endif
@@ -435,6 +460,8 @@ typedef struct Segment {
{}
} *_t;
[[gnu::hot]] void _setPixelColorXY_raw(int& x, int& y, uint32_t& col); // set pixel without mapping (internal use only)
public:
Segment(uint16_t sStart=0, uint16_t sStop=30) :
@@ -467,6 +494,7 @@ typedef struct Segment {
aux1(0),
data(nullptr),
_capabilities(0),
_default_palette(0),
_dataLen(0),
_t(nullptr)
{
@@ -515,23 +543,30 @@ typedef struct Segment {
inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength() const { return grouping + spacing; }
inline uint8_t getLightCapabilities() const { return _capabilities; }
inline void deactivate() { setGeometry(0,0); }
inline static uint16_t getUsedSegmentData() { return _usedSegmentData; }
inline static void addUsedSegmentData(int len) { _usedSegmentData += len; }
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
#ifndef WLED_DISABLE_MODE_BLEND
inline static void modeBlend(bool blend) { _modeBlend = blend; }
inline static void modeBlend(bool blend) { _modeBlend = blend; }
#endif
static void handleRandomPalette();
inline static unsigned vLength() { return Segment::_vLength; }
inline static unsigned vWidth() { return Segment::_vWidth; }
inline static unsigned vHeight() { return Segment::_vHeight; }
inline static uint32_t getCurrentColor(unsigned i) { return Segment::_currentColors[i]; } // { return i < 3 ? Segment::_currentColors[i] : 0; }
inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; }
inline static uint8_t getCurrentBrightness() { return Segment::_segBri; }
static void handleRandomPalette();
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
void beginDraw(); // set up parameters for current effect
void setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t m12=0);
Segment &setColor(uint8_t slot, uint32_t c);
Segment &setCCT(uint16_t k);
Segment &setOpacity(uint8_t o);
Segment &setOption(uint8_t n, bool val);
Segment &setMode(uint8_t fx, bool loadDefaults = false);
Segment &setPalette(uint8_t pal);
uint8_t differs(Segment& b) const;
uint8_t differs(const Segment& b) const;
void refreshLightCapabilities();
// runtime data functions
@@ -550,17 +585,17 @@ typedef struct Segment {
// transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change
void stopTransition(); // ends transition mode by destroying transition structure (does nothing if not in transition)
inline void handleTransition() { if (progress() == 0xFFFFU) stopTransition(); }
inline void handleTransition() { updateTransitionProgress(); if (progress() == 0xFFFFU) stopTransition(); }
#ifndef WLED_DISABLE_MODE_BLEND
void swapSegenv(tmpsegd_t &tmpSegD); // copies segment data into specifed buffer, if buffer is not a transition buffer, segment data is overwritten from transition buffer
void restoreSegenv(tmpsegd_t &tmpSegD); // restores segment data from buffer, if buffer is not transition buffer, changed values are copied to transition buffer
#endif
[[gnu::hot]] uint16_t progress() const; // transition progression between 0-65535
[[gnu::hot]] void updateTransitionProgress(); // set current progression of transition
inline uint16_t progress() const { return _transitionprogress; }; // transition progression between 0-65535
[[gnu::hot]] uint8_t currentBri(bool useCct = false) const; // current segment brightness/CCT (blended while in transition)
uint8_t currentMode() const; // currently active effect/mode (while in transition)
[[gnu::hot]] uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition)
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
void setCurrentPalette();
// 1D strip
[[gnu::hot]] uint16_t virtualLength() const;
@@ -581,21 +616,19 @@ typedef struct Segment {
void fadeToBlackBy(uint8_t fadeBy);
inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); }
inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); }
inline void addPixelColor(int n, uint32_t color, bool fast = false) { setPixelColor(n, color_add(getPixelColor(n), color, fast)); }
inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); }
inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); }
inline void addPixelColor(int n, uint32_t color, bool preserveCR = true) { setPixelColor(n, color_add(getPixelColor(n), color, preserveCR)); }
inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColor(n, RGBW32(r,g,b,w), preserveCR); }
inline void addPixelColor(int n, CRGB c, bool preserveCR = true) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), preserveCR); }
inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); }
[[gnu::hot]] uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const;
[[gnu::hot]] uint32_t color_wheel(uint8_t pos) const;
// 2D Blur: shortcuts for bluring columns or rows only (50% faster than full 2D blur)
inline void blurCols(fract8 blur_amount, bool smear = false) { // blur all columns
const unsigned cols = virtualWidth();
for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear);
blur2D(0, blur_amount, smear);
}
inline void blurRows(fract8 blur_amount, bool smear = false) { // blur all rows
const unsigned rows = virtualHeight();
for ( unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear);
blur2D(blur_amount, 0, smear);
}
// 2D matrix
@@ -624,31 +657,28 @@ typedef struct Segment {
// 2D support functions
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast)); }
inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); }
inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); }
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); }
void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur
void blur2D(uint8_t blur_amount, bool smear = false);
void blurRow(uint32_t row, fract8 blur_amount, bool smear = false);
void blurCol(uint32_t col, fract8 blur_amount, bool smear = false);
void moveX(int8_t delta, bool wrap = false);
void moveY(int8_t delta, bool wrap = false);
void move(uint8_t dir, uint8_t delta, bool wrap = false);
inline void addPixelColorXY(int x, int y, uint32_t color, bool preserveCR = true) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, preserveCR)); }
inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(r,g,b,w), preserveCR); }
inline void addPixelColorXY(int x, int y, CRGB c, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), preserveCR); }
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); }
//void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur
void blur2D(uint8_t blur_x, uint8_t blur_y, bool smear = false);
void moveX(int delta, bool wrap = false);
void moveY(int delta, bool wrap = false);
void move(unsigned dir, unsigned delta, bool wrap = false);
void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false);
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0);
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0, bool usePalGrad = false);
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0, bool usePalGrad = false) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate, usePalGrad); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
inline void blur2d(fract8 blur_amount) { blur(blur_amount); }
inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
#else
inline uint16_t XY(uint16_t x, uint16_t y) { return x; }
inline uint16_t XY(int x, int y) { return x; }
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
@@ -662,16 +692,16 @@ typedef struct Segment {
inline uint32_t getPixelColorXY(int x, int y) { return getPixelColor(x); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); }
inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); }
inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); }
inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); }
inline void addPixelColorXY(int x, int y, uint32_t color, bool saturate = false) { addPixelColor(x, color, saturate); }
inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColor(x, RGBW32(r,g,b,w), saturate); }
inline void addPixelColorXY(int x, int y, CRGB c, bool saturate = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), saturate); }
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); }
inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {}
inline void blur2D(uint8_t blur_amount, bool smear = false) {}
inline void blurRow(uint32_t row, fract8 blur_amount, bool smear = false) {}
inline void blurCol(uint32_t col, fract8 blur_amount, bool smear = false) {}
inline void moveX(int8_t delta, bool wrap = false) {}
inline void moveY(int8_t delta, bool wrap = false) {}
//inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {}
inline void blur2D(uint8_t blur_x, uint8_t blur_y, bool smear = false) {}
inline void blurRow(int row, fract8 blur_amount, bool smear = false) {}
inline void blurCol(int col, fract8 blur_amount, bool smear = false) {}
inline void moveX(int delta, bool wrap = false) {}
inline void moveY(int delta, bool wrap = false) {}
inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {}
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
@@ -679,9 +709,9 @@ typedef struct Segment {
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0, bool = false) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0, bool usePalGrad = false) {}
inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
#endif
} segment;
@@ -719,9 +749,6 @@ class WS2812FX { // 96 bytes
#endif
correctWB(false),
cctFromRgb(false),
// semi-private (just obscured) used in effect functions through macros
_colors_t{0,0,0},
_virtualSegmentLength(0),
// true private variables
_suspend(false),
_length(DEFAULT_LED_COUNT),
@@ -729,7 +756,7 @@ class WS2812FX { // 96 bytes
_transitionDur(750),
_targetFps(WLED_FPS),
_frametime(FRAMETIME_FIXED),
_cumulativeFps(2),
_cumulativeFps(50 << FPS_CALC_SHIFT),
_isServicing(false),
_isOffRefreshRequired(false),
_hasWhiteChannel(false),
@@ -739,6 +766,7 @@ class WS2812FX { // 96 bytes
customMappingTable(nullptr),
customMappingSize(0),
_lastShow(0),
_lastServiceShow(0),
_segment_index(0),
_mainSegment(0)
{
@@ -768,26 +796,22 @@ class WS2812FX { // 96 bytes
#endif
finalizeInit(), // initialises strip components
service(), // executes effect functions when due and calls strip.show()
setMode(uint8_t segid, uint8_t m), // sets effect/mode for given segment (high level API)
setColor(uint8_t slot, uint32_t c), // sets color (in slot) for given segment (high level API)
setCCT(uint16_t k), // sets global CCT (either in relative 0-255 value or in K)
setBrightness(uint8_t b, bool direct = false), // sets strip brightness
setRange(uint16_t i, uint16_t i2, uint32_t col), // used for clock overlay
purgeSegments(), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint)
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1),
setMainSegmentId(uint8_t n),
setMainSegmentId(unsigned n = 0),
resetSegments(), // marks all segments for reset
makeAutoSegments(bool forceReset = false), // will create segments based on configured outputs
fixInvalidSegments(), // fixes incorrect segment configuration
setPixelColor(unsigned n, uint32_t c), // paints absolute strip pixel with index n and color c
show(), // initiates LED output
setTargetFps(uint8_t fps),
setTargetFps(unsigned fps),
setupEffectData(); // add default effects to the list; defined in FX.cpp
inline void resetTimebase() { timebase = 0UL - millis(); }
inline void restartRuntime() { for (Segment &seg : _segments) { seg.markForReset().resetIfRequired(); } }
inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); }
inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
inline void setPixelColor(unsigned n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); }
inline void setPixelColor(unsigned n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); }
inline void fill(uint32_t c) { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
@@ -803,9 +827,9 @@ class WS2812FX { // 96 bytes
checkSegmentAlignment(),
hasRGBWBus() const,
hasCCTBus() const,
isUpdating() const, // return true if the strip is being sent pixel updates
deserializeMap(uint8_t n=0);
deserializeMap(unsigned n = 0);
inline bool isUpdating() const { return !BusManager::canAllShow(); } // return true if the strip is being sent pixel updates
inline bool isServicing() const { return _isServicing; } // returns true if strip.service() is executing
inline bool hasWhiteChannel() const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel
inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset)
@@ -822,7 +846,7 @@ class WS2812FX { // 96 bytes
addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp;
inline uint8_t getBrightness() const { return _brightness; } // returns current strip brightness
inline uint8_t getMaxSegments() const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value)
inline static constexpr unsigned getMaxSegments() { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value)
inline uint8_t getSegmentsNum() const { return _segments.size(); } // returns currently present segments
inline uint8_t getCurrSegmentId() const { return _segment_index; } // returns current segment index (only valid while strip.isServicing())
inline uint8_t getMainSegmentId() const { return _mainSegment; } // returns main segment index
@@ -832,28 +856,27 @@ class WS2812FX { // 96 bytes
uint16_t
getLengthPhysical() const,
getLengthTotal() const, // will include virtual/nonexistent pixels in matrix
getFps() const,
getMappedPixelIndex(uint16_t index) const;
getLengthTotal() const; // will include virtual/nonexistent pixels in matrix
inline uint16_t getFps() const { return (millis() - _lastShow > 2000) ? 0 : (FPS_MULTIPLIER * _cumulativeFps) >> FPS_CALC_SHIFT; } // Returns the refresh rate of the LED strip (_cumulativeFps is stored in fixed point)
inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms)
inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
inline uint16_t getMinShowDelay() const { return MIN_FRAME_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H)
inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms)
inline uint16_t getMappedPixelIndex(uint16_t index) const { // convert logical address to physical
if (index < customMappingSize && (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index];
return index;
};
unsigned long now, timebase;
uint32_t getPixelColor(unsigned) const;
inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call
inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } // returns currently valid color (for slot i) AKA SEGCOLOR(); may be blended between two colors while in transition
inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call
const char *
getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); }
const char *getModeData(unsigned id = 0) const { return (id && id < _modeCount) ? _modeData[id] : PSTR("Solid"); }
inline const char **getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data
const char **
getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data
Segment& getSegment(uint8_t id);
Segment& getSegment(unsigned id);
inline Segment& getFirstSelectedSeg() { return _segments[getFirstSelectedSegId()]; } // returns reference to first segment that is "selected"
inline Segment& getMainSegment() { return _segments[getMainSegmentId()]; } // returns reference to main segment
inline Segment* getSegments() { return &(_segments[0]); } // returns pointer to segment vector structure (warning: use carefully)
@@ -912,11 +935,6 @@ class WS2812FX { // 96 bytes
bool cctFromRgb : 1;
};
// using public variables to reduce code size increase due to inline function getSegment() (with bounds checking)
// and color transitions
uint32_t _colors_t[3]; // color used for effect (includes transition)
uint16_t _virtualSegmentLength;
std::vector<segment> _segments;
friend class Segment;
@@ -949,6 +967,7 @@ class WS2812FX { // 96 bytes
uint16_t customMappingSize;
unsigned long _lastShow;
unsigned long _lastServiceShow;
uint8_t _segment_index;
uint8_t _mainSegment;

View File

@@ -148,57 +148,68 @@ void WS2812FX::setUpMatrix() {
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t IRAM_ATTR_YN Segment::XY(int x, int y)
{
unsigned width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
unsigned height = virtualHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%width) + (y%height) * width : 0;
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%vW) + (y%vH) * vW : 0;
}
// raw setColor function without checks (checks are done in setPixelColorXY())
void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(int& x, int& y, uint32_t& col)
{
const int baseX = start + x;
const int baseY = startY + y;
#ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress());
#endif
strip.setPixelColorXY(baseX, baseY, col);
// Apply mirroring
if (mirror || mirror_y) {
auto setMirroredPixel = [&](int mx, int my) {
strip.setPixelColorXY(mx, my, col);
};
const int mirrorX = start + width() - x - 1;
const int mirrorY = startY + height() - y - 1;
if (mirror) setMirroredPixel(transpose ? baseX : mirrorX, transpose ? mirrorY : baseY);
if (mirror_y) setMirroredPixel(transpose ? mirrorX : baseX, transpose ? baseY : mirrorY);
if (mirror && mirror_y) setMirroredPixel(mirrorX, mirrorY);
}
}
void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col)
{
if (!isActive()) return; // not active
if ((unsigned)x >= virtualWidth() || (unsigned)y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
col = color_fade(col, _bri_t);
}
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
// negative values of x & y cast into unsigend will become very large values and will therefore be greater than vW/vH
if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
// if color is unscaled
if (!_colorScaled) col = color_fade(col, _segBri);
if (reverse ) x = vW - x - 1;
if (reverse_y) y = vH - y - 1;
if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed
unsigned groupLen = groupLength();
x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
int W = width();
int H = height();
if (x >= W || y >= H) return; // if pixel would fall out of segment just exit
uint32_t tmpCol = col;
for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally
int xX = (x+g), yY = (y+j);
if (xX >= W || yY >= H) continue; // we have reached one dimension's end
#ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
if (mirror) { //set the corresponding horizontally mirrored pixel
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
}
if (mirror_y) { //set the corresponding vertically mirrored pixel
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
}
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, tmpCol);
if (groupLen > 1) {
int W = width();
int H = height();
x *= groupLen; // expand to physical pixels
y *= groupLen; // expand to physical pixels
const int maxY = std::min(y + grouping, H);
const int maxX = std::min(x + grouping, W);
for (int yY = y; yY < maxY; yY++) {
for (int xX = x; xX < maxX; xX++) {
_setPixelColorXY_raw(xX, yY, col);
}
}
} else {
_setPixelColorXY_raw(x, y, col);
}
}
@@ -209,11 +220,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
if (!isActive()) return; // not active
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
float fX = x * (cols-1);
float fY = y * (rows-1);
float fX = x * (vWidth()-1);
float fY = y * (vHeight()-1);
if (aa) {
unsigned xL = roundf(fX-0.49f);
unsigned xR = roundf(fX+0.49f);
@@ -251,9 +259,11 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel
uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const {
if (!isActive()) return 0; // not active
if ((unsigned)x >= virtualWidth() || (unsigned)y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
const int vW = vWidth();
const int vH = vHeight();
if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = vW - x - 1;
if (reverse_y) y = vH - y - 1;
if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed
x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
@@ -261,128 +271,69 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const {
return strip.getPixelColorXY(start + x, startY + y);
}
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){
if (!isActive() || blur_amount == 0) return; // not active
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
if (row >= rows) return;
// blur one row
uint8_t keep = smear ? 255 : 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
uint32_t carryover = BLACK;
// 2D blurring, can be asymmetrical
void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) {
if (!isActive()) return; // not active
const unsigned cols = vWidth();
const unsigned rows = vHeight();
uint32_t lastnew;
uint32_t last;
uint32_t curnew = BLACK;
for (unsigned x = 0; x < cols; x++) {
uint32_t cur = getPixelColorXY(x, row);
uint32_t part = color_fade(cur, seep);
curnew = color_fade(cur, keep);
if (x > 0) {
if (carryover)
curnew = color_add(curnew, carryover, true);
uint32_t prev = color_add(lastnew, part, true);
if (last != prev) // optimization: only set pixel if color has changed
setPixelColorXY(x - 1, row, prev);
} else // first pixel
setPixelColorXY(x, row, curnew);
lastnew = curnew;
last = cur; // save original value for comparison on next iteration
carryover = part;
}
setPixelColorXY(cols-1, row, curnew); // set last pixel
}
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) {
if (!isActive() || blur_amount == 0) return; // not active
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
if (col >= cols) return;
// blur one column
uint8_t keep = smear ? 255 : 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
uint32_t carryover = BLACK;
uint32_t lastnew;
uint32_t last;
uint32_t curnew = BLACK;
for (unsigned y = 0; y < rows; y++) {
uint32_t cur = getPixelColorXY(col, y);
uint32_t part = color_fade(cur, seep);
curnew = color_fade(cur, keep);
if (y > 0) {
if (carryover)
curnew = color_add(curnew, carryover, true);
uint32_t prev = color_add(lastnew, part, true);
if (last != prev) // optimization: only set pixel if color has changed
setPixelColorXY(col, y - 1, prev);
} else // first pixel
setPixelColorXY(col, y, curnew);
lastnew = curnew;
last = cur; //save original value for comparison on next iteration
carryover = part;
}
setPixelColorXY(col, rows - 1, curnew);
}
void Segment::blur2D(uint8_t blur_amount, bool smear) {
if (!isActive() || blur_amount == 0) return; // not active
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
const uint8_t keep = smear ? 255 : 255 - blur_amount;
const uint8_t seep = blur_amount >> (1 + smear);
uint32_t lastnew;
uint32_t last;
for (unsigned row = 0; row < rows; row++) {
uint32_t carryover = BLACK;
uint32_t curnew = BLACK;
for (unsigned x = 0; x < cols; x++) {
uint32_t cur = getPixelColorXY(x, row);
uint32_t part = color_fade(cur, seep);
curnew = color_fade(cur, keep);
if (x > 0) {
if (carryover) curnew = color_add(curnew, carryover, true);
uint32_t prev = color_add(lastnew, part, true);
// optimization: only set pixel if color has changed
if (last != prev) setPixelColorXY(x - 1, row, prev);
} else setPixelColorXY(x, row, curnew); // first pixel
lastnew = curnew;
last = cur; // save original value for comparison on next iteration
carryover = part;
if (blur_x) {
const uint8_t keepx = smear ? 255 : 255 - blur_x;
const uint8_t seepx = blur_x >> 1;
for (unsigned row = 0; row < rows; row++) { // blur rows (x direction)
uint32_t carryover = BLACK;
uint32_t curnew = BLACK;
for (unsigned x = 0; x < cols; x++) {
uint32_t cur = getPixelColorXY(x, row);
uint32_t part = color_fade(cur, seepx);
curnew = color_fade(cur, keepx);
if (x > 0) {
if (carryover) curnew = color_add(curnew, carryover);
uint32_t prev = color_add(lastnew, part);
// optimization: only set pixel if color has changed
if (last != prev) setPixelColorXY(x - 1, row, prev);
} else setPixelColorXY(x, row, curnew); // first pixel
lastnew = curnew;
last = cur; // save original value for comparison on next iteration
carryover = part;
}
setPixelColorXY(cols-1, row, curnew); // set last pixel
}
setPixelColorXY(cols-1, row, curnew); // set last pixel
}
for (unsigned col = 0; col < cols; col++) {
uint32_t carryover = BLACK;
uint32_t curnew = BLACK;
for (unsigned y = 0; y < rows; y++) {
uint32_t cur = getPixelColorXY(col, y);
uint32_t part = color_fade(cur, seep);
curnew = color_fade(cur, keep);
if (y > 0) {
if (carryover) curnew = color_add(curnew, carryover, true);
uint32_t prev = color_add(lastnew, part, true);
// optimization: only set pixel if color has changed
if (last != prev) setPixelColorXY(col, y - 1, prev);
} else setPixelColorXY(col, y, curnew); // first pixel
lastnew = curnew;
last = cur; //save original value for comparison on next iteration
carryover = part;
if (blur_y) {
const uint8_t keepy = smear ? 255 : 255 - blur_y;
const uint8_t seepy = blur_y >> 1;
for (unsigned col = 0; col < cols; col++) {
uint32_t carryover = BLACK;
uint32_t curnew = BLACK;
for (unsigned y = 0; y < rows; y++) {
uint32_t cur = getPixelColorXY(col, y);
uint32_t part = color_fade(cur, seepy);
curnew = color_fade(cur, keepy);
if (y > 0) {
if (carryover) curnew = color_add(curnew, carryover);
uint32_t prev = color_add(lastnew, part);
// optimization: only set pixel if color has changed
if (last != prev) setPixelColorXY(col, y - 1, prev);
} else setPixelColorXY(col, y, curnew); // first pixel
lastnew = curnew;
last = cur; //save original value for comparison on next iteration
carryover = part;
}
setPixelColorXY(col, rows - 1, curnew);
}
setPixelColorXY(col, rows - 1, curnew);
}
}
/*
// 2D Box blur
void Segment::box_blur(unsigned radius, bool smear) {
if (!isActive() || radius == 0) return; // not active
if (radius > 3) radius = 3;
const unsigned d = (1 + 2*radius) * (1 + 2*radius); // averaging divisor
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
const unsigned cols = vWidth();
const unsigned rows = vHeight();
uint16_t *tmpRSum = new uint16_t[cols*rows];
uint16_t *tmpGSum = new uint16_t[cols*rows];
uint16_t *tmpBSum = new uint16_t[cols*rows];
@@ -448,40 +399,56 @@ void Segment::box_blur(unsigned radius, bool smear) {
delete[] tmpBSum;
delete[] tmpWSum;
}
void Segment::moveX(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const int cols = virtualWidth();
const int rows = virtualHeight();
if (!delta || abs(delta) >= cols) return;
uint32_t newPxCol[cols];
for (int y = 0; y < rows; y++) {
if (delta > 0) {
for (int x = 0; x < cols-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y);
for (int x = cols-delta; x < cols; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - cols : x, y);
} else {
for (int x = cols-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y);
for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + cols : x, y);
*/
void Segment::moveX(int delta, bool wrap) {
if (!isActive() || !delta) return; // not active
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
int absDelta = abs(delta);
if (absDelta >= vW) return;
uint32_t newPxCol[vW];
int newDelta;
int stop = vW;
int start = 0;
if (wrap) newDelta = (delta + vW) % vW; // +cols in case delta < 0
else {
if (delta < 0) start = absDelta;
stop = vW - absDelta;
newDelta = delta > 0 ? delta : 0;
}
for (int y = 0; y < vH; y++) {
for (int x = 0; x < stop; x++) {
int srcX = x + newDelta;
if (wrap) srcX %= vW; // Wrap using modulo when `wrap` is true
newPxCol[x] = getPixelColorXY(srcX, y);
}
for (int x = 0; x < cols; x++) setPixelColorXY(x, y, newPxCol[x]);
for (int x = 0; x < stop; x++) setPixelColorXY(x + start, y, newPxCol[x]);
}
}
void Segment::moveY(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const int cols = virtualWidth();
const int rows = virtualHeight();
if (!delta || abs(delta) >= rows) return;
uint32_t newPxCol[rows];
for (int x = 0; x < cols; x++) {
if (delta > 0) {
for (int y = 0; y < rows-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta));
for (int y = rows-delta; y < rows; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - rows : y);
} else {
for (int y = rows-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta));
for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + rows : y);
void Segment::moveY(int delta, bool wrap) {
if (!isActive() || !delta) return; // not active
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
int absDelta = abs(delta);
if (absDelta >= vH) return;
uint32_t newPxCol[vH];
int newDelta;
int stop = vH;
int start = 0;
if (wrap) newDelta = (delta + vH) % vH; // +rows in case delta < 0
else {
if (delta < 0) start = absDelta;
stop = vH - absDelta;
newDelta = delta > 0 ? delta : 0;
}
for (int x = 0; x < vW; x++) {
for (int y = 0; y < stop; y++) {
int srcY = y + newDelta;
if (wrap) srcY %= vH; // Wrap using modulo when `wrap` is true
newPxCol[y] = getPixelColorXY(x, srcY);
}
for (int y = 0; y < rows; y++) setPixelColorXY(x, y, newPxCol[y]);
for (int y = 0; y < stop; y++) setPixelColorXY(x, y + start, newPxCol[y]);
}
}
@@ -489,7 +456,7 @@ void Segment::moveY(int8_t delta, bool wrap) {
// @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down
// @param delta number of pixels to move
// @param wrap around
void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
void Segment::move(unsigned dir, unsigned delta, bool wrap) {
if (delta==0) return;
switch (dir) {
case 0: moveX( delta, wrap); break;
@@ -507,46 +474,49 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col,
if (!isActive() || radius == 0) return; // not active
if (soft) {
// Xiaolin Wus algorithm
int rsq = radius*radius;
const int rsq = radius*radius;
int x = 0;
int y = radius;
unsigned oldFade = 0;
while (x < y) {
float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
uint8_t fade = float(0xFF) * (ceilf(yf) - yf); // how much color to keep
if (oldFade > fade) y--;
oldFade = fade;
setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true));
setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true));
setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true));
setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true));
setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true));
setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true));
setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true));
setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true));
setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true));
setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true));
setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true));
setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true));
int px, py;
for (uint8_t i = 0; i < 16; i++) {
int swaps = (i & 0x4 ? 1 : 0); // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1
int adj = (i < 8) ? 0 : 1; // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1
int dx = (i & 1) ? -1 : 1; // 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1
int dy = (i & 2) ? -1 : 1; // 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1
if (swaps) {
px = cx + (y - adj) * dx;
py = cy + x * dy;
} else {
px = cx + x * dx;
py = cy + (y - adj) * dy;
}
uint32_t pixCol = getPixelColorXY(px, py);
setPixelColorXY(px, py, adj ?
color_blend(pixCol, col, fade) :
color_blend(col, pixCol, fade));
}
x++;
}
} else {
// pre-scale color for all pixels
col = color_fade(col, _segBri);
_colorScaled = true;
// Bresenhams Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
while (y >= x) {
setPixelColorXY(cx+x, cy+y, col);
setPixelColorXY(cx-x, cy+y, col);
setPixelColorXY(cx+x, cy-y, col);
setPixelColorXY(cx-x, cy-y, col);
setPixelColorXY(cx+y, cy+x, col);
setPixelColorXY(cx-y, cy+x, col);
setPixelColorXY(cx+y, cy-x, col);
setPixelColorXY(cx-y, cy-x, col);
for (int i = 0; i < 4; i++) {
int dx = (i & 1) ? -x : x;
int dy = (i & 2) ? -y : y;
setPixelColorXY(cx + dx, cy + dy, col);
setPixelColorXY(cx + dy, cy + dx, col);
}
x++;
if (d > 0) {
y--;
@@ -555,33 +525,38 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col,
d += 4 * x + 6;
}
}
_colorScaled = false;
}
}
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
if (!isActive() || radius == 0) return; // not active
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
// draw soft bounding circle
if (soft) drawCircle(cx, cy, radius, col, soft);
// pre-scale color for all pixels
col = color_fade(col, _segBri);
_colorScaled = true;
// fill it
const int cols = virtualWidth();
const int rows = virtualHeight();
for (int y = -radius; y <= radius; y++) {
for (int x = -radius; x <= radius; x++) {
if (x * x + y * y <= radius * radius &&
int(cx)+x>=0 && int(cy)+y>=0 &&
int(cx)+x<cols && int(cy)+y<rows)
int(cx)+x >= 0 && int(cy)+y >= 0 &&
int(cx)+x < vW && int(cy)+y < vH)
setPixelColorXY(cx + x, cy + y, col);
}
}
_colorScaled = false;
}
//line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) {
if (!isActive()) return; // not active
const int cols = virtualWidth();
const int rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
if (x0 >= vW || x1 >= vW || y0 >= vH || y1 >= vH) return;
const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
@@ -608,17 +583,20 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
float gradient = x1-x0 == 0 ? 1.0f : float(y1-y0) / float(x1-x0);
float intersectY = y0;
for (int x = x0; x <= x1; x++) {
unsigned keep = float(0xFFFF) * (intersectY-int(intersectY)); // how much color to keep
unsigned seep = 0xFFFF - keep; // how much background to keep
uint8_t keep = float(0xFF) * (intersectY-int(intersectY)); // how much color to keep
uint8_t seep = 0xFF - keep; // how much background to keep
int y = int(intersectY);
if (steep) std::swap(x,y); // temporaryly swap if steep
// pixel coverage is determined by fractional part of y co-ordinate
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep, true));
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep, true));
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep));
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep));
intersectY += gradient;
if (steep) std::swap(x,y); // restore if steep
}
} else {
// pre-scale color for all pixels
c = color_fade(c, _segBri);
_colorScaled = true;
// Bresenham's algorithm
int err = (dx>dy ? dx : -dy)/2; // error direction
for (;;) {
@@ -628,6 +606,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
_colorScaled = false;
}
}
@@ -639,16 +618,15 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate) {
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate, bool usePalGrad) {
if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries
const int cols = virtualWidth();
const int rows = virtualHeight();
const int font = w*h;
CRGB col = CRGB(color);
CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col);
if(usePalGrad) grad = SEGPALETTE; // selected palette as gradient
//if (w<5 || w>6 || h!=8) return;
for (int i = 0; i<h; i++) { // character height
@@ -661,7 +639,10 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font
default: return;
}
col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND);
uint32_t c = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND);
// pre-scale color for all pixels
c = color_fade(c, _segBri);
_colorScaled = true;
for (int j = 0; j<w; j++) { // character width
int x0, y0;
switch (rotate) {
@@ -671,11 +652,12 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
case 1: x0 = x + i; y0 = y + j; break; // +90 deg
default: x0 = x + (w-1) - j; y0 = y + i; break; // no rotation
}
if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen
if (x0 < 0 || x0 >= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
setPixelColorXY(x0, y0, col);
setPixelColorXY(x0, y0, c);
}
}
_colorScaled = false;
}
}

View File

@@ -66,15 +66,21 @@ static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTy
///////////////////////////////////////////////////////////////////////////////
// Segment class implementation
///////////////////////////////////////////////////////////////////////////////
uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[]
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
uint16_t Segment::maxHeight = 1;
unsigned Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[]
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
uint16_t Segment::maxHeight = 1;
unsigned Segment::_vLength = 0;
unsigned Segment::_vWidth = 0;
unsigned Segment::_vHeight = 0;
uint8_t Segment::_segBri = 0;
uint32_t Segment::_currentColors[NUM_COLORS] = {0,0,0};
bool Segment::_colorScaled = false;
CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black);
CRGBPalette16 Segment::_randomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR);
CRGBPalette16 Segment::_newRandomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR);
uint16_t Segment::_lastPaletteChange = 0; // perhaps it should be per segment
uint16_t Segment::_lastPaletteBlend = 0; //in millis (lowest 16 bits only)
uint16_t Segment::_transitionprogress = 0xFFFF;
#ifndef WLED_DISABLE_MODE_BLEND
bool Segment::_modeBlend = false;
@@ -195,24 +201,12 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip
//default palette. Differs depending on effect
if (pal == 0) switch (mode) {
case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette
case FX_MODE_COLORWAVES : pal = 26; break; // landscape 33
case FX_MODE_FILLNOISE8 : pal = 9; break; // ocean colors
case FX_MODE_NOISE16_1 : pal = 20; break; // Drywet
case FX_MODE_NOISE16_2 : pal = 43; break; // Blue cyan yellow
case FX_MODE_NOISE16_3 : pal = 35; break; // heat palette
case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33
case FX_MODE_GLITTER : pal = 11; break; // rainbow colors
case FX_MODE_SUNRISE : pal = 35; break; // heat palette
case FX_MODE_RAILWAY : pal = 3; break; // prim + sec
case FX_MODE_2DSOAP : pal = 11; break; // rainbow colors
}
if (pal == 0) pal = _default_palette; //load default palette set in FX _data, party colors as default
switch (pal) {
case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break;
case 1: //randomly generated palette
targetPalette = _randomPalette; //random palette is generated at intervals in handleRandomPalette()
targetPalette = _randomPalette; //random palette is generated at intervals in handleRandomPalette()
break;
case 2: {//primary color only
CRGB prim = gamma32(colors[0]);
@@ -236,23 +230,11 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec);
}
break;}
case 6: //Party colors
targetPalette = PartyColors_p; break;
case 7: //Cloud colors
targetPalette = CloudColors_p; break;
case 8: //Lava colors
targetPalette = LavaColors_p; break;
case 9: //Ocean colors
targetPalette = OceanColors_p; break;
case 10: //Forest colors
targetPalette = ForestColors_p; break;
case 11: //Rainbow colors
targetPalette = RainbowColors_p; break;
case 12: //Rainbow stripe colors
targetPalette = RainbowStripeColors_p; break;
default: //progmem palettes
if (pal>245) {
targetPalette = strip.customPalettes[255-pal]; // we checked bounds above
} else if (pal < 13) { // palette 6 - 12, fastled palettes
targetPalette = *fastledPalettes[pal-6];
} else {
byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72);
@@ -317,12 +299,12 @@ void Segment::stopTransition() {
}
// transition progression between 0-65535
uint16_t IRAM_ATTR Segment::progress() const {
inline void Segment::updateTransitionProgress() {
_transitionprogress = 0xFFFFU;
if (isInTransition()) {
unsigned diff = millis() - _t->_start;
if (_t->_dur > 0 && diff < _t->_dur) return diff * 0xFFFFU / _t->_dur;
if (_t->_dur > 0 && diff < _t->_dur) _transitionprogress = diff * 0xFFFFU / _t->_dur;
}
return 0xFFFFU;
}
#ifndef WLED_DISABLE_MODE_BLEND
@@ -396,7 +378,7 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
}
#endif
uint8_t IRAM_ATTR Segment::currentBri(bool useCct) const {
uint8_t Segment::currentBri(bool useCct) const {
unsigned prog = progress();
if (prog < 0xFFFFU) {
unsigned curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
@@ -414,16 +396,31 @@ uint8_t Segment::currentMode() const {
return mode;
}
uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const {
uint32_t Segment::currentColor(uint8_t slot) const {
if (slot >= NUM_COLORS) slot = 0;
#ifndef WLED_DISABLE_MODE_BLEND
return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
return isInTransition() ? color_blend16(_t->_segT._colorT[slot], colors[slot], progress()) : colors[slot];
#else
return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
return isInTransition() ? color_blend16(_t->_colorT[slot], colors[slot], progress()) : colors[slot];
#endif
}
void Segment::setCurrentPalette() {
// pre-calculate drawing parameters for faster access (based on the idea from @softhack007 from MM fork)
void Segment::beginDraw() {
_vWidth = virtualWidth();
_vHeight = virtualHeight();
_vLength = virtualLength();
_segBri = currentBri();
// adjust gamma for effects
for (unsigned i = 0; i < NUM_COLORS; i++) {
#ifndef WLED_DISABLE_MODE_BLEND
uint32_t col = isInTransition() ? color_blend16(_t->_segT._colorT[i], colors[i], progress()) : colors[i];
#else
uint32_t col = isInTransition() ? color_blend16(_t->_colorT[i], colors[i], progress()) : colors[i];
#endif
_currentColors[i] = gamma32(col);
}
// load palette into _currentPalette
loadPalette(_currentPalette, palette);
unsigned prog = progress();
if (strip.paletteFade && prog < 0xFFFFU) {
@@ -442,7 +439,7 @@ void Segment::handleRandomPalette() {
if ((uint16_t)((uint16_t)(millis() / 1000U) - _lastPaletteChange) > randomPaletteChangeTime){
_newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
_lastPaletteChange = (uint16_t)(millis() / 1000U);
_lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately
_lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately
}
// if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls)
@@ -455,8 +452,10 @@ void Segment::handleRandomPalette() {
nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
}
// segId is given when called from network callback, changes are queued if that segment is currently in its effect function
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
// sets Segment geometry (length or width/height and grouping, spacing and offset as well as 2D mapping)
// strip must be suspended (strip.suspend()) before calling this function
// this function may call fill() to clear pixels if spacing or mapping changed (which requires setting _vWidth, _vHeight, _vLength or beginDraw())
void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t m12) {
// return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D
@@ -464,11 +463,19 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
#endif
if (boundsUnchanged
&& (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return;
&& (ofs == UINT16_MAX || ofs == offset)
&& (m12 == map1D2D)
) return;
stateChanged = true; // send UDP/WS broadcast
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (stop || spc != spacing || m12 != map1D2D) {
_vWidth = virtualWidth();
_vHeight = virtualHeight();
_vLength = virtualLength();
_segBri = currentBri();
fill(BLACK); // turn old segment range off or clears pixels if changing spacing (requires _vWidth/_vHeight/_vLength/_segBri)
}
if (grp) { // prevent assignment of 0
grouping = grp;
spacing = spc;
@@ -477,6 +484,7 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
spacing = 0;
}
if (ofs < UINT16_MAX) offset = ofs;
map1D2D = constrain(m12, 0, 7);
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
DEBUG_PRINT(','); DEBUG_PRINT(i2);
@@ -565,9 +573,9 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) {
if (modeBlending) startTransition(strip.getTransition()); // set effect transitions
#endif
mode = fx;
int sOpt;
// load default values from effect string
if (loadDefaults) {
int sOpt;
sOpt = extractModeDefaults(fx, "sx"); speed = (sOpt >= 0) ? sOpt : DEFAULT_SPEED;
sOpt = extractModeDefaults(fx, "ix"); intensity = (sOpt >= 0) ? sOpt : DEFAULT_INTENSITY;
sOpt = extractModeDefaults(fx, "c1"); custom1 = (sOpt >= 0) ? sOpt : DEFAULT_C1;
@@ -584,6 +592,9 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
}
sOpt = extractModeDefaults(fx, "pal"); // always extract 'pal' to set _default_palette
if(sOpt <= 0) sOpt = 6; // partycolors if zero or not set
_default_palette = sOpt; // _deault_palette is loaded into pal0 in loadPalette() (if selected)
markForReset();
stateChanged = true; // send UDP/WS broadcast
}
@@ -602,14 +613,14 @@ Segment &Segment::setPalette(uint8_t pal) {
}
// 2D matrix
unsigned IRAM_ATTR Segment::virtualWidth() const {
unsigned Segment::virtualWidth() const {
unsigned groupLen = groupLength();
unsigned vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vWidth;
}
unsigned IRAM_ATTR Segment::virtualHeight() const {
unsigned Segment::virtualHeight() const {
unsigned groupLen = groupLength();
unsigned vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
@@ -653,7 +664,7 @@ static int getPinwheelLength(int vW, int vH) {
#endif
// 1D strip
uint16_t IRAM_ATTR Segment::virtualLength() const {
uint16_t Segment::virtualLength() const {
#ifndef WLED_DISABLE_2D
if (is2D()) {
unsigned vW = virtualWidth();
@@ -687,18 +698,31 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
{
if (!isActive()) return; // not active
if (!isActive() || i < 0) return; // not active or invalid index
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
int vStrip = 0;
#endif
i &= 0xFFFF;
if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit
int vL = vLength();
// if the 1D effect is using virtual strips "i" will have virtual strip id stored in upper 16 bits
// in such case "i" will be > virtualLength()
if (i >= vL) {
// check if this is a virtual strip
#ifndef WLED_DISABLE_2D
vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
i &= 0xFFFF; //truncate vstrip index
if (i >= vL) return; // if pixel would still fall out of segment just exit
#else
return;
#endif
}
#ifndef WLED_DISABLE_2D
if (is2D()) {
int vH = virtualHeight(); // segment height in logical pixels
int vW = virtualWidth();
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
// pre-scale color for all pixels
col = color_fade(col, _segBri);
_colorScaled = true;
switch (map1D2D) {
case M12_Pixels:
// use all available pixels as a long strip
@@ -706,12 +730,12 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
break;
case M12_pBar:
// expand 1D effect vertically or have it play on virtual strips
if (vStrip>0) setPixelColorXY(vStrip - 1, vH - i - 1, col);
else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col);
if (vStrip > 0) setPixelColorXY(vStrip - 1, vH - i - 1, col);
else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col);
break;
case M12_pArc:
// expand in circular fashion from center
if (i==0)
if (i == 0)
setPixelColorXY(0, 0, col);
else {
float r = i;
@@ -768,7 +792,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
// Odd rays start further from center if prevRay started at center.
static int prevRay = INT_MIN; // previous ray number
if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) {
int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
posx += inc_x * jump;
posy += inc_y * jump;
}
@@ -790,13 +814,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
break;
}
}
_colorScaled = false;
return;
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
} else if (Segment::maxHeight != 1 && (width() == 1 || height() == 1)) {
if (start < Segment::maxWidth*Segment::maxHeight) {
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
int x = 0, y = 0;
if (virtualHeight()>1) y = i;
if (virtualWidth() >1) x = i;
if (vHeight() > 1) y = i;
if (vWidth() > 1) x = i;
setPixelColorXY(x, y, col);
return;
}
@@ -804,10 +829,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
#endif
unsigned len = length();
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
col = color_fade(col, _bri_t);
}
// if color is unscaled
if (!_colorScaled) col = color_fade(col, _segBri);
// expand pixel (taking into account start, grouping, spacing [and offset])
i = i * groupLength();
@@ -830,14 +853,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexMir), col, uint16_t(0xFFFFU - progress()));
#endif
strip.setPixelColor(indexMir, tmpCol);
}
indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexSet), col, uint16_t(0xFFFFU - progress()));
#endif
strip.setPixelColor(indexSet, tmpCol);
}
@@ -882,23 +905,20 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
{
if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16;
#endif
i &= 0xFFFF;
#ifndef WLED_DISABLE_2D
if (is2D()) {
int vH = virtualHeight(); // segment height in logical pixels
int vW = virtualWidth();
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
switch (map1D2D) {
case M12_Pixels:
return getPixelColorXY(i % vW, i / vW);
break;
case M12_pBar:
if (vStrip>0) return getPixelColorXY(vStrip - 1, vH - i -1);
else return getPixelColorXY(0, vH - i -1);
break;
case M12_pBar: {
int vStrip = i>>16; // virtual strips are only relevant in Bar expansion mode
if (vStrip > 0) return getPixelColorXY(vStrip - 1, vH - (i & 0xFFFF) -1);
else return getPixelColorXY(0, vH - i -1);
break; }
case M12_pArc:
if (i >= vW && i >= vH) {
unsigned vI = sqrt16(i*i/2);
@@ -942,7 +962,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
}
#endif
if (reverse) i = virtualLength() - i - 1;
if (reverse) i = vLength() - i - 1;
i *= groupLength();
i += start;
// offset/phase
@@ -951,7 +971,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
return strip.getPixelColor(i);
}
uint8_t Segment::differs(Segment& b) const {
uint8_t Segment::differs(const Segment& b) const {
uint8_t d = 0;
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
@@ -1031,12 +1051,16 @@ void Segment::refreshLightCapabilities() {
*/
void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const int cols = is2D() ? virtualWidth() : virtualLength();
const int rows = virtualHeight(); // will be 1 for 1D
const int cols = is2D() ? vWidth() : vLength();
const int rows = vHeight(); // will be 1 for 1D
// pre-scale color for all pixels
c = color_fade(c, _segBri);
_colorScaled = true;
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, c);
else setPixelColor(x, c);
}
_colorScaled = false;
}
/*
@@ -1044,8 +1068,8 @@ void Segment::fill(uint32_t c) {
*/
void Segment::fade_out(uint8_t rate) {
if (!isActive()) return; // not active
const int cols = is2D() ? virtualWidth() : virtualLength();
const int rows = virtualHeight(); // will be 1 for 1D
const int cols = is2D() ? vWidth() : vLength();
const int rows = vHeight(); // will be 1 for 1D
rate = (255-rate) >> 1;
float mappedRate = 1.0f / (float(rate) + 1.1f);
@@ -1083,8 +1107,8 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const int cols = is2D() ? virtualWidth() : virtualLength();
const int rows = virtualHeight(); // will be 1 for 1D
const int cols = is2D() ? vWidth() : vLength();
const int rows = vHeight(); // will be 1 for 1D
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
@@ -1094,20 +1118,21 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
/*
* blurs segment content, source: FastLED colorutils.cpp
* Note: for blur_amount > 215 this function does not work properly (creates alternating pattern)
*/
void Segment::blur(uint8_t blur_amount, bool smear) {
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D
if (is2D()) {
// compatibility with 2D
blur2D(blur_amount, smear);
blur2D(blur_amount, blur_amount, smear); // symmetrical 2D blur
//box_blur(map(blur_amount,1,255,1,3), smear);
return;
}
#endif
uint8_t keep = smear ? 255 : 255 - blur_amount;
uint8_t seep = blur_amount >> (1 + smear);
unsigned vlength = virtualLength();
uint8_t seep = blur_amount >> 1;
unsigned vlength = vLength();
uint32_t carryover = BLACK;
uint32_t lastnew;
uint32_t last;
@@ -1117,12 +1142,11 @@ void Segment::blur(uint8_t blur_amount, bool smear) {
uint32_t part = color_fade(cur, seep);
curnew = color_fade(cur, keep);
if (i > 0) {
if (carryover) curnew = color_add(curnew, carryover, true);
uint32_t prev = color_add(lastnew, part, true);
if (carryover) curnew = color_add(curnew, carryover);
uint32_t prev = color_add(lastnew, part);
// optimization: only set pixel if color has changed
if (last != prev) setPixelColor(i - 1, prev);
} else // first pixel
setPixelColor(i, curnew);
} else setPixelColor(i, curnew); // first pixel
lastnew = curnew;
last = cur; // save original value for comparison on next iteration
carryover = part;
@@ -1137,11 +1161,11 @@ void Segment::blur(uint8_t blur_amount, bool smear) {
*/
uint32_t Segment::color_wheel(uint8_t pos) const {
if (palette) return color_from_palette(pos, false, true, 0); // perhaps "strip.paletteBlend < 2" should be better instead of "true"
uint8_t w = W(currentColor(0));
uint8_t w = W(getCurrentColor(0));
pos = 255 - pos;
if (pos < 85) {
return RGBW32((255 - pos * 3), 0, (pos * 3), w);
} else if(pos < 170) {
} else if (pos < 170) {
pos -= 85;
return RGBW32(0, (pos * 3), (255 - pos * 3), w);
} else {
@@ -1160,18 +1184,21 @@ uint32_t Segment::color_wheel(uint8_t pos) const {
* @returns Single color from palette
*/
uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) const {
uint32_t color = gamma32(currentColor(mcol));
uint32_t color = getCurrentColor(mcol < NUM_COLORS ? mcol : 0);
// default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) return (pbri == 255) ? color : color_fade(color, pbri, true);
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
return color_fade(color, pbri, true);
}
const int vL = vLength();
unsigned paletteIndex = i;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
if (mapping && vL > 1) paletteIndex = (i*255)/(vL -1);
// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined)
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
palcol.w = W(color);
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color));
return palcol.color32;
}
@@ -1204,7 +1231,7 @@ void WS2812FX::finalizeInit() {
static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins),
"The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES");
unsigned prevLen = 0;
unsigned pinsIndex = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
@@ -1215,7 +1242,7 @@ void WS2812FX::finalizeInit() {
// if we need more pins than available all outputs have been configured
if (pinsIndex + busPins > defNumPins) break;
// Assign all pins first so we can check for conflicts on this bus
for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j];
@@ -1286,14 +1313,9 @@ void WS2812FX::finalizeInit() {
_isOffRefreshRequired |= bus->isOffRefreshRequired() && !bus->isPWM(); // use refresh bit for phase shift with analog
unsigned busEnd = bus->getStart() + bus->getLength();
if (busEnd > _length) _length = busEnd;
#ifdef ESP8266
// why do we need to reinitialise GPIO3???
//if (!bus->isDigital() || bus->is2Pin()) continue;
//uint8_t pins[5];
//if (!bus->getPins(pins)) continue;
//BusDigital* bd = static_cast<BusDigital*>(bus);
//if (pins[0] == 3) bd->reinit();
#endif
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
bus->begin();
}
Segment::maxWidth = _length;
@@ -1309,7 +1331,14 @@ void WS2812FX::finalizeInit() {
void WS2812FX::service() {
unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase;
if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return;
if (_suspend) return;
unsigned long elapsed = nowUp - _lastServiceShow;
if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited
if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime
if (elapsed < _frametime) return; // too early for service
}
bool doShow = false;
_isServicing = true;
@@ -1326,18 +1355,13 @@ void WS2812FX::service() {
if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{
doShow = true;
unsigned frameDelay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen
int oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
_virtualSegmentLength = seg.virtualLength(); //SEGLEN
_colors_t[0] = gamma32(seg.currentColor(0));
_colors_t[1] = gamma32(seg.currentColor(1));
_colors_t[2] = gamma32(seg.currentColor(2));
seg.setCurrentPalette(); // load actual palette
// when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio
// when cctFromRgb is true we implicitly calculate WW and CW from RGB values
if (cctFromRgb) BusManager::setSegmentCCT(-1);
@@ -1349,13 +1373,14 @@ void WS2812FX::service() {
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
frameDelay = (*_mode[seg.mode])(); // run new/current mode
seg.beginDraw(); // set up parameters for get/setPixelColor()
frameDelay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
_virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed)
seg.beginDraw(); // set up parameters for get/setPixelColor()
unsigned d2 = (*_mode[tmpMode])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
frameDelay = min(frameDelay,d2); // use shortest delay
@@ -1371,20 +1396,20 @@ void WS2812FX::service() {
}
_segment_index++;
}
_virtualSegmentLength = 0;
_isServicing = false;
_triggered = false;
#ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif
if (doShow) {
yield();
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
show();
_lastServiceShow = nowUp; // update timestamp, for precise FPS control
}
#ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif
}
@@ -1404,61 +1429,26 @@ void WS2812FX::show() {
// avoid race condition, capture _callback value
show_callback callback = _callback;
if (callback) callback();
unsigned long showNow = millis();
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
BusManager::show();
unsigned long showNow = millis();
size_t diff = showNow - _lastShow;
size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
_lastShow = showNow;
}
/**
* Returns a true value if any of the strips are still being updated.
* On some hardware (ESP32), strip updates are done asynchronously.
*/
bool WS2812FX::isUpdating() const {
return !BusManager::canAllShow();
}
/**
* Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough.
* Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies
*/
uint16_t WS2812FX::getFps() const {
if (millis() - _lastShow > 2000) return 0;
return _cumulativeFps +1;
}
void WS2812FX::setTargetFps(uint8_t fps) {
if (fps > 0 && fps <= 120) _targetFps = fps;
_frametime = 1000 / _targetFps;
}
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (segid >= _segments.size()) return;
if (m >= getModeCount()) m = getModeCount() - 1;
if (_segments[segid].mode != m) {
_segments[segid].setMode(m); // do not load defaults
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
_lastShow = showNow;
}
}
//applies to all active and selected segments
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (slot >= NUM_COLORS) return;
for (segment &seg : _segments) {
if (seg.isActive() && seg.isSelected()) {
seg.setColor(slot, c);
}
}
void WS2812FX::setTargetFps(unsigned fps) {
if (fps <= 250) _targetFps = fps;
if (_targetFps > 0) _frametime = 1000 / _targetFps;
else _frametime = MIN_FRAME_DELAY; // unlimited mode
}
void WS2812FX::setCCT(uint16_t k) {
@@ -1485,7 +1475,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
BusManager::setBrightness(b);
if (!direct) {
unsigned long t = millis();
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon
}
}
@@ -1507,7 +1497,7 @@ uint8_t WS2812FX::getFirstSelectedSegId() const {
return getMainSegmentId();
}
void WS2812FX::setMainSegmentId(uint8_t n) {
void WS2812FX::setMainSegmentId(unsigned n) {
_mainSegment = 0;
if (n < _segments.size()) {
_mainSegment = n;
@@ -1583,23 +1573,10 @@ void WS2812FX::purgeSegments() {
}
}
Segment& WS2812FX::getSegment(uint8_t id) {
Segment& WS2812FX::getSegment(unsigned id) {
return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors
}
// sets new segment bounds, queues if that segment is currently running
void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
if (segId >= getSegmentsNum()) {
if (i2 <= i1) return; // do not append empty/inactive segments
appendSegment(Segment(0, strip.getLengthTotal()));
segId = getSegmentsNum()-1; // segments are added at the end of list
}
suspend();
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
resume();
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
}
void WS2812FX::resetSegments() {
_segments.clear(); // destructs all Segment as part of clearing
#ifndef WLED_DISABLE_2D
@@ -1798,7 +1775,7 @@ void WS2812FX::loadCustomPalettes() {
}
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
bool WS2812FX::deserializeMap(uint8_t n) {
bool WS2812FX::deserializeMap(unsigned n) {
// 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
char fileName[32];
@@ -1850,14 +1827,6 @@ bool WS2812FX::deserializeMap(uint8_t n) {
return (customMappingSize > 0);
}
uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) const {
// convert logical address to physical
if (index < customMappingSize
&& (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index];
return index;
}
WS2812FX* WS2812FX::instance = nullptr;
@@ -1870,5 +1839,5 @@ const char JSON_palette_names[] PROGMEM = R"=====([
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf",
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
"Candy2"
"Candy2","Traffic Light"
])=====";

View File

@@ -126,10 +126,10 @@ void onAlexaChange(EspalexaDevice* dev)
} else {
colorKtoRGB(k, rgbw);
}
strip.setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]));
strip.getMainSegment().setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]));
} else {
uint32_t color = dev->getRGB();
strip.setColor(0, color);
strip.getMainSegment().setColor(0, color);
}
stateUpdated(CALL_MODE_ALEXA);
}

View File

@@ -150,21 +150,11 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
//_buffering = bc.doubleBuffer;
uint16_t lenToCreate = bc.count;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr);
_valid = (_busPtr != nullptr);
DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax);
}
//fine tune power estimation constants for your setup
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
#ifndef MA_FOR_ESP
#ifdef ESP8266
#define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA)
#else
#define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA)
#endif
#endif
//DISCLAIMER
//The following function attemps to calculate the current LED power usage,
//and will limit the brightness to stay below a set amperage threshold.
@@ -306,22 +296,22 @@ void BusDigital::setStatusPixel(uint32_t c) {
}
}
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
if (!_valid) return;
uint8_t cctWW = 0, cctCW = 0;
if (hasWhite()) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
if (_data) {
size_t offset = pix * getNumberOfChannels();
uint8_t* dataptr = _data + offset;
if (hasRGB()) {
_data[offset++] = R(c);
_data[offset++] = G(c);
_data[offset++] = B(c);
*dataptr++ = R(c);
*dataptr++ = G(c);
*dataptr++ = B(c);
}
if (hasWhite()) _data[offset++] = W(c);
if (hasWhite()) *dataptr++ = W(c);
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
// we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio)
if (hasCCT()) _data[offset] = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it
if (hasCCT()) *dataptr = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it
} else {
if (_reversed) pix = _len - pix -1;
pix += _skip;
@@ -336,16 +326,22 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
}
}
if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
uint16_t wwcw = 0;
if (hasCCT()) {
uint8_t cctWW = 0, cctCW = 0;
Bus::calculateCCT(c, cctWW, cctCW);
wwcw = (cctCW<<8) | cctWW;
}
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
}
}
// returns original color if global buffering is enabled, else returns lossly restored color from bus
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const {
uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
if (!_valid) return 0;
if (_data) {
size_t offset = pix * getNumberOfChannels();
const size_t offset = pix * getNumberOfChannels();
uint32_t c;
if (!hasRGB()) {
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
@@ -356,7 +352,7 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const {
} else {
if (_reversed) pix = _len - pix -1;
pix += _skip;
unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
unsigned r = R(c);
@@ -410,9 +406,9 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
};
}
void BusDigital::reinit() {
void BusDigital::begin() {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins);
PolyBus::begin(_busPtr, _iType, _pins, _frequencykHz);
}
void BusDigital::cleanup() {
@@ -501,7 +497,7 @@ BusPwm::BusPwm(BusConfig &bc)
DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
}
void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
void BusPwm::setPixelColor(unsigned pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
@@ -538,7 +534,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
}
//does no index check
uint32_t BusPwm::getPixelColor(uint16_t pix) const {
uint32_t BusPwm::getPixelColor(unsigned pix) const {
if (!_valid) return 0;
// TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented)
switch (_type) {
@@ -567,19 +563,15 @@ void BusPwm::show() {
const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8)
[[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)
// use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness
// the formula is based on 12 bit resolution as there is no need for greater precision
// use CIE brightness formula (linear + cubic) to approximate human eye perceived brightness
// see: https://en.wikipedia.org/wiki/Lightness
unsigned pwmBri = (unsigned)_bri * 100; // enlarge to use integer math for linear response
if (pwmBri < 2040) {
// linear response for values [0-20]
pwmBri = ((pwmBri << 12) + 115043) / 230087; //adding '0.5' before division for correct rounding
} else {
// cubic response for values [21-255]
pwmBri += 4080;
float temp = (float)pwmBri / 29580.0f;
temp = temp * temp * temp * (float)maxBri;
pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri]
unsigned pwmBri = _bri;
if (pwmBri < 21) { // linear response for values [0-20]
pwmBri = (pwmBri * maxBri + 2300 / 2) / 2300 ; // adding '0.5' before division for correct rounding, 2300 gives a good match to CIE curve
} else { // cubic response for values [21-255]
float temp = float(pwmBri + 41) / float(255 + 41); // 41 is to match offset & slope to linear part
temp = temp * temp * temp * (float)maxBri;
pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] C
}
[[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri)
@@ -674,7 +666,7 @@ BusOnOff::BusOnOff(BusConfig &bc)
DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
}
void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
void BusOnOff::setPixelColor(unsigned pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
c = autoWhiteCalc(c);
uint8_t r = R(c);
@@ -684,7 +676,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
_data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
}
uint32_t BusOnOff::getPixelColor(uint16_t pix) const {
uint32_t BusOnOff::getPixelColor(unsigned pix) const {
if (!_valid) return 0;
return RGBW32(_data[0], _data[0], _data[0], _data[0]);
}
@@ -734,7 +726,7 @@ BusNetwork::BusNetwork(BusConfig &bc)
DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
}
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
void BusNetwork::setPixelColor(unsigned pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (_hasWhite) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
@@ -745,7 +737,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (_hasWhite) _data[offset+3] = W(c);
}
uint32_t BusNetwork::getPixelColor(uint16_t pix) const {
uint32_t BusNetwork::getPixelColor(unsigned pix) const {
if (!_valid || pix >= _len) return 0;
unsigned offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (hasWhite() ? _data[offset+3] : 0));
@@ -910,7 +902,7 @@ void BusManager::on() {
if (busses[i]->isDigital() && busses[i]->getPins(pins)) {
if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) {
BusDigital *bus = static_cast<BusDigital*>(busses[i]);
bus->reinit();
bus->begin();
break;
}
}
@@ -943,7 +935,6 @@ void BusManager::show() {
busses[i]->show();
_milliAmpsUsed += busses[i]->getUsedCurrent();
}
if (_milliAmpsUsed) _milliAmpsUsed += MA_FOR_ESP;
}
void BusManager::setStatusPixel(uint32_t c) {
@@ -952,7 +943,7 @@ void BusManager::setStatusPixel(uint32_t c) {
}
}
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) {
for (unsigned i = 0; i < numBusses; i++) {
unsigned bstart = busses[i]->getStart();
if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue;
@@ -975,7 +966,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
Bus::setCCT(cct);
}
uint32_t BusManager::getPixelColor(uint16_t pix) {
uint32_t BusManager::getPixelColor(unsigned pix) {
for (unsigned i = 0; i < numBusses; i++) {
unsigned bstart = busses[i]->getStart();
if (!busses[i]->containsPixel(pix)) continue;

View File

@@ -6,6 +6,7 @@
*/
#include "const.h"
#include "pin_manager.h"
#include <vector>
//colors.cpp
@@ -79,13 +80,14 @@ class Bus {
virtual ~Bus() {} //throw the bus under the bus
virtual void begin() {};
virtual void show() = 0;
virtual bool canShow() const { return true; }
virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual void setPixelColor(unsigned pix, uint32_t c) = 0;
virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void setColorOrder(uint8_t co) {}
virtual uint32_t getPixelColor(uint16_t pix) const { return 0; }
virtual uint32_t getPixelColor(unsigned pix) const { return 0; }
virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; }
virtual uint16_t getLength() const { return isOk() ? _len : 0; }
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
@@ -109,7 +111,7 @@ class Bus {
inline void setStart(uint16_t start) { _start = start; }
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
inline uint8_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
inline uint32_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
inline uint16_t getStart() const { return _start; }
inline uint8_t getType() const { return _type; }
inline bool isOk() const { return _valid; }
@@ -118,8 +120,8 @@ class Bus {
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
static constexpr uint32_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
static constexpr uint32_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
static constexpr bool hasRGB(uint8_t type) {
return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
}
@@ -203,9 +205,9 @@ class BusDigital : public Bus {
bool canShow() const override;
void setBrightness(uint8_t b) override;
void setStatusPixel(uint32_t c) override;
[[gnu::hot]] void setPixelColor(uint16_t pix, uint32_t c) override;
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override;
void setColorOrder(uint8_t colorOrder) override;
[[gnu::hot]] uint32_t getPixelColor(uint16_t pix) const override;
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
uint8_t getColorOrder() const override { return _colorOrder; }
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
uint8_t skippedLeds() const override { return _skip; }
@@ -213,7 +215,7 @@ class BusDigital : public Bus {
uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
uint16_t getMaxCurrent() const override { return _milliAmpsMax; }
void reinit();
void begin() override;
void cleanup();
static std::vector<LEDType> getLEDTypes();
@@ -251,8 +253,8 @@ class BusPwm : public Bus {
BusPwm(BusConfig &bc);
~BusPwm() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override; //does no index check
void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override; //does no index check
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
uint16_t getFrequency() const override { return _frequency; }
void show() override;
@@ -278,8 +280,8 @@ class BusOnOff : public Bus {
BusOnOff(BusConfig &bc);
~BusOnOff() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override;
void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override;
uint8_t getPins(uint8_t* pinArray) const override;
void show() override;
void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); }
@@ -298,8 +300,8 @@ class BusNetwork : public Bus {
~BusNetwork() { cleanup(); }
bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override;
void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override;
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
void show() override;
void cleanup();
@@ -362,6 +364,16 @@ struct BusConfig {
};
//fine tune power estimation constants for your setup
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
#ifndef MA_FOR_ESP
#ifdef ESP8266
#define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA)
#else
#define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA)
#endif
#endif
class BusManager {
public:
BusManager() {};
@@ -369,7 +381,7 @@ class BusManager {
//utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc);
static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1);
static uint16_t currentMilliamps() { return _milliAmpsUsed; }
static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; }
static uint16_t ablMilliampsMax() { return _milliAmpsMax; }
static int add(BusConfig &bc);
@@ -384,13 +396,13 @@ class BusManager {
static void show();
static bool canAllShow();
static void setStatusPixel(uint32_t c);
[[gnu::hot]] static void setPixelColor(uint16_t pix, uint32_t c);
[[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c);
static void setBrightness(uint8_t b);
// for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K
// WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT()
static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
static uint32_t getPixelColor(uint16_t pix);
[[gnu::hot]] static uint32_t getPixelColor(unsigned pix);
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
static Bus* getBus(uint8_t busNr);

View File

@@ -336,7 +336,7 @@ class PolyBus {
// initialize SPI bus speed for DotStar methods
template <class T>
static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) {
static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz /* 0 == use default */) {
T dotStar_strip = static_cast<T>(busPtr);
#ifdef ESP8266
dotStar_strip->Begin();
@@ -363,7 +363,7 @@ class PolyBus {
tm1914_strip->SetPixelSettings(NeoTm1914Settings()); //NeoTm1914_Mode_DinFdinAutoSwitch, NeoTm1914_Mode_DinOnly, NeoTm1914_Mode_FdinOnly
}
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) {
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz /* only used by DotStar */) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
@@ -480,7 +480,7 @@ class PolyBus {
}
}
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel) {
#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
// NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation
// since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
@@ -597,7 +597,7 @@ class PolyBus {
case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break;
case I_SS_P98_3: busPtr = new B_SS_P98_3(len, pins[1], pins[0]); break;
}
begin(busPtr, busType, pins, clock_kHz);
return busPtr;
}

View File

@@ -436,13 +436,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
else gammaCorrectBri = false;
if (light_gc_col > 1.0f) gammaCorrectCol = true;
else gammaCorrectCol = false;
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) {
if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal);
} else {
if (gammaCorrectVal <= 1.0f || gammaCorrectVal > 3) {
gammaCorrectVal = 1.0f; // no gamma correction
gammaCorrectBri = false;
gammaCorrectCol = false;
}
NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table
JsonObject light_tr = light["tr"];
CJSON(fadeTransition, light_tr["mode"]);

View File

@@ -5,61 +5,56 @@
*/
/*
* color blend function
* color blend function, based on FastLED blend function
* the calculation for each color is: result = (A*(amountOfA) + A + B*(amountOfB) + B) / 256 with amountOfA = 255 - amountOfB
*/
uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if (blend == 0) return color1;
unsigned blendmax = b16 ? 0xFFFF : 0xFF;
if (blend == blendmax) return color2;
unsigned shift = b16 ? 16 : 8;
uint32_t w1 = W(color1);
uint32_t r1 = R(color1);
uint32_t g1 = G(color1);
uint32_t b1 = B(color1);
uint32_t w2 = W(color2);
uint32_t r2 = R(color2);
uint32_t g2 = G(color2);
uint32_t b2 = B(color2);
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return RGBW32(r3, g3, b3, w3);
uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
// min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance
uint32_t rb1 = color1 & 0x00FF00FF;
uint32_t wg1 = (color1>>8) & 0x00FF00FF;
uint32_t rb2 = color2 & 0x00FF00FF;
uint32_t wg2 = (color2>>8) & 0x00FF00FF;
uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & 0x00FF00FF;
uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & 0xFF00FF00;
return rb3 | wg3;
}
/*
* color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
* original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
* speed optimisations by @dedehai
*/
uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR)
{
if (c1 == BLACK) return c2;
if (c2 == BLACK) return c1;
if (fast) {
uint8_t r = R(c1);
uint8_t g = G(c1);
uint8_t b = B(c1);
uint8_t w = W(c1);
r = qadd8(r, R(c2));
g = qadd8(g, G(c2));
b = qadd8(b, B(c2));
w = qadd8(w, W(c2));
return RGBW32(r,g,b,w);
uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once
uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF);
uint32_t r = rb >> 16; // extract single color values
uint32_t b = rb & 0xFFFF;
uint32_t w = wg >> 16;
uint32_t g = wg & 0xFFFF;
if (preserveCR) { // preserve color ratios
uint32_t max = std::max(r,g); // check for overflow note
max = std::max(max,b);
max = std::max(max,w);
//unsigned max = r; // check for overflow note
//max = g > max ? g : max;
//max = b > max ? b : max;
//max = w > max ? w : max;
if (max > 255) {
uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead
rb = ((rb * scale) >> 8) & 0x00FF00FF; //
wg = (wg * scale) & 0xFF00FF00;
} else wg = wg << 8; //shift white and green back to correct position
return rb | wg;
} else {
uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
unsigned max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = w;
if (max < 256) return RGBW32(r, g, b, w);
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
r = r > 255 ? 255 : r;
g = g > 255 ? 255 : g;
b = b > 255 ? 255 : b;
w = w > 255 ? 255 : w;
return RGBW32(r,g,b,w);
}
}
@@ -70,27 +65,53 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
{
if (c1 == BLACK || amount + video == 0) return BLACK;
if (amount == 255) return c1;
if (c1 == BLACK || amount == 0) return BLACK;
uint32_t scaledcolor; // color order is: W R G B from MSB to LSB
uint32_t r = R(c1);
uint32_t g = G(c1);
uint32_t b = B(c1);
uint32_t w = W(c1);
uint32_t scale = amount; // 32bit for faster calculation
if (video) {
scaledcolor = (((r * scale) >> 8) + ((r && scale) ? 1 : 0)) << 16;
scaledcolor |= (((g * scale) >> 8) + ((g && scale) ? 1 : 0)) << 8;
scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0);
scaledcolor |= (((w * scale) >> 8) + ((w && scale) ? 1 : 0)) << 24;
} else {
scaledcolor = ((r * scale) >> 8) << 16;
scaledcolor |= ((g * scale) >> 8) << 8;
scaledcolor |= (b * scale) >> 8;
scaledcolor |= ((w * scale) >> 8) << 24;
uint32_t addRemains = 0;
if (!video) scale++; // add one for correct scaling using bitshifts
else { // video scaling: make sure colors do not dim to zero if they started non-zero
addRemains = R(c1) ? 0x00010000 : 0;
addRemains |= G(c1) ? 0x00000100 : 0;
addRemains |= B(c1) ? 0x00000001 : 0;
addRemains |= W(c1) ? 0x01000000 : 0;
}
uint32_t rb = (((c1 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue
uint32_t wg = (((c1 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green
scaledcolor = (rb | wg) + addRemains;
return scaledcolor;
}
// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes)
uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType)
{
if (blendType == LINEARBLEND_NOWRAP) {
index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping
}
unsigned hi4 = byte(index) >> 4;
const CRGB* entry = (CRGB*)((uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB)));
unsigned red1 = entry->r;
unsigned green1 = entry->g;
unsigned blue1 = entry->b;
if (blendType != NOBLEND) {
if (hi4 == 15) entry = &(pal[0]);
else ++entry;
unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8
unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max
red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8;
green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8;
blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8;
}
if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted
uint32_t scale = brightness + 1; // adjust for rounding (bitshift)
red1 = (red1 * scale) >> 8;
green1 = (green1 * scale) >> 8;
blue1 = (blue1 * scale) >> 8;
}
return RGBW32(red1,green1,blue1,0);
}
void setRandomColor(byte* rgb)
{
lastRandomIndex = get_random_wheel_index(lastRandomIndex);
@@ -103,91 +124,91 @@ void setRandomColor(byte* rgb)
*/
CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette)
{
CHSV palettecolors[4]; //array of colors for the new palette
uint8_t keepcolorposition = random8(4); //color position of current random palette to keep
palettecolors[keepcolorposition] = rgb2hsv_approximate(basepalette.entries[keepcolorposition*5]); //read one of the base colors of the current palette
palettecolors[keepcolorposition].hue += random8(10)-5; // +/- 5 randomness of base color
//generate 4 saturation and brightness value numbers
//only one saturation is allowed to be below 200 creating mostly vibrant colors
//only one brightness value number is allowed below 200, creating mostly bright palettes
CHSV palettecolors[4]; // array of colors for the new palette
uint8_t keepcolorposition = hw_random8(4); // color position of current random palette to keep
palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette
palettecolors[keepcolorposition].hue += hw_random8(10)-5; // +/- 5 randomness of base color
// generate 4 saturation and brightness value numbers
// only one saturation is allowed to be below 200 creating mostly vibrant colors
// only one brightness value number is allowed below 200, creating mostly bright palettes
for (int i = 0; i < 3; i++) { //generate three high values
palettecolors[i].saturation = random8(200,255);
palettecolors[i].value = random8(220,255);
for (int i = 0; i < 3; i++) { // generate three high values
palettecolors[i].saturation = hw_random8(200,255);
palettecolors[i].value = hw_random8(220,255);
}
//allow one to be lower
palettecolors[3].saturation = random8(20,255);
palettecolors[3].value = random8(80,255);
// allow one to be lower
palettecolors[3].saturation = hw_random8(20,255);
palettecolors[3].value = hw_random8(80,255);
//shuffle the arrays
// shuffle the arrays
for (int i = 3; i > 0; i--) {
std::swap(palettecolors[i].saturation, palettecolors[random8(i + 1)].saturation);
std::swap(palettecolors[i].value, palettecolors[random8(i + 1)].value);
std::swap(palettecolors[i].saturation, palettecolors[hw_random8(i + 1)].saturation);
std::swap(palettecolors[i].value, palettecolors[hw_random8(i + 1)].value);
}
//now generate three new hues based off of the hue of the chosen current color
// now generate three new hues based off of the hue of the chosen current color
uint8_t basehue = palettecolors[keepcolorposition].hue;
uint8_t harmonics[3]; //hues that are harmonic but still a little random
uint8_t type = random8(5); //choose a harmony type
uint8_t harmonics[3]; // hues that are harmonic but still a little random
uint8_t type = hw_random8(5); // choose a harmony type
switch (type) {
case 0: // analogous
harmonics[0] = basehue + random8(30, 50);
harmonics[1] = basehue + random8(10, 30);
harmonics[2] = basehue - random8(10, 30);
harmonics[0] = basehue + hw_random8(30, 50);
harmonics[1] = basehue + hw_random8(10, 30);
harmonics[2] = basehue - hw_random8(10, 30);
break;
case 1: // triadic
harmonics[0] = basehue + 113 + random8(15);
harmonics[1] = basehue + 233 + random8(15);
harmonics[2] = basehue - 7 + random8(15);
harmonics[0] = basehue + 113 + hw_random8(15);
harmonics[1] = basehue + 233 + hw_random8(15);
harmonics[2] = basehue - 7 + hw_random8(15);
break;
case 2: // split-complementary
harmonics[0] = basehue + 145 + random8(10);
harmonics[1] = basehue + 205 + random8(10);
harmonics[2] = basehue - 5 + random8(10);
harmonics[0] = basehue + 145 + hw_random8(10);
harmonics[1] = basehue + 205 + hw_random8(10);
harmonics[2] = basehue - 5 + hw_random8(10);
break;
case 3: // square
harmonics[0] = basehue + 85 + random8(10);
harmonics[1] = basehue + 175 + random8(10);
harmonics[2] = basehue + 265 + random8(10);
harmonics[0] = basehue + 85 + hw_random8(10);
harmonics[1] = basehue + 175 + hw_random8(10);
harmonics[2] = basehue + 265 + hw_random8(10);
break;
case 4: // tetradic
harmonics[0] = basehue + 80 + random8(20);
harmonics[1] = basehue + 170 + random8(20);
harmonics[2] = basehue - 15 + random8(30);
harmonics[0] = basehue + 80 + hw_random8(20);
harmonics[1] = basehue + 170 + hw_random8(20);
harmonics[2] = basehue - 15 + hw_random8(30);
break;
}
if (random8() < 128) {
//50:50 chance of shuffling hues or keep the color order
if (hw_random8() < 128) {
// 50:50 chance of shuffling hues or keep the color order
for (int i = 2; i > 0; i--) {
std::swap(harmonics[i], harmonics[random8(i + 1)]);
std::swap(harmonics[i], harmonics[hw_random8(i + 1)]);
}
}
//now set the hues
// now set the hues
int j = 0;
for (int i = 0; i < 4; i++) {
if (i==keepcolorposition) continue; //skip the base color
if (i==keepcolorposition) continue; // skip the base color
palettecolors[i].hue = harmonics[j];
j++;
}
bool makepastelpalette = false;
if (random8() < 25) { //~10% chance of desaturated 'pastel' colors
if (hw_random8() < 25) { // ~10% chance of desaturated 'pastel' colors
makepastelpalette = true;
}
//apply saturation & gamma correction
// apply saturation & gamma correction
CRGB RGBpalettecolors[4];
for (int i = 0; i < 4; i++) {
if (makepastelpalette && palettecolors[i].saturation > 180) {
if (makepastelpalette && palettecolors[i].saturation > 180) {
palettecolors[i].saturation -= 160; //desaturate all four colors
}
}
RGBpalettecolors[i] = (CRGB)palettecolors[i]; //convert to RGB
RGBpalettecolors[i] = gamma32(((uint32_t)RGBpalettecolors[i]) & 0x00FFFFFFU); //strip alpha from CRGB
}
@@ -198,34 +219,72 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette)
RGBpalettecolors[3]);
}
CRGBPalette16 generateRandomPalette() //generate fully random palette
CRGBPalette16 generateRandomPalette() // generate fully random palette
{
return CRGBPalette16(CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)));
return CRGBPalette16(CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)),
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)),
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)),
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)));
}
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0)
{
float h = ((float)hue)/10922.5f; // hue*6/65535
float s = ((float)sat)/255.0f;
int i = int(h);
float f = h - i;
int p = int(255.0f * (1.0f-s));
int q = int(255.0f * (1.0f-s*f));
int t = int(255.0f * (1.0f-s*(1.0f-f)));
p = constrain(p, 0, 255);
q = constrain(q, 0, 255);
t = constrain(t, 0, 255);
switch (i%6) {
case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break;
case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break;
case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break;
case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break;
case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break;
case 5: rgb[0]=255,rgb[1]=p, rgb[2]=q; break;
unsigned int remainder, region, p, q, t;
unsigned int h = hsv.h;
unsigned int s = hsv.s;
unsigned int v = hsv.v;
if (s == 0) {
rgb = v << 16 | v << 8 | v;
return;
}
region = h / 10923; // 65536 / 6 = 10923
remainder = (h - (region * 10923)) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 16))) >> 8;
t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8;
switch (region) {
case 0:
rgb = v << 16 | t << 8 | p; break;
case 1:
rgb = q << 16 | v << 8 | p; break;
case 2:
rgb = p << 16 | v << 8 | t; break;
case 3:
rgb = p << 16 | q << 8 | v; break;
case 4:
rgb = t << 16 | p << 8 | v; break;
default:
rgb = v << 16 | p << 8 | q; break;
}
}
void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version
{
hsv.raw = 0;
int32_t r = (rgb>>16)&0xFF;
int32_t g = (rgb>>8)&0xFF;
int32_t b = rgb&0xFF;
int32_t minval, maxval, delta;
minval = min(r, g);
minval = min(minval, b);
maxval = max(r, g);
maxval = max(maxval, b);
if (maxval == 0) return; // black
hsv.v = maxval;
delta = maxval - minval;
hsv.s = (255 * delta) / maxval;
if (hsv.s == 0) return; // gray value
if (maxval == r) hsv.h = (10923 * (g - b)) / delta;
else if (maxval == g) hsv.h = 21845 + (10923 * (b - r)) / delta;
else hsv.h = 43690 + (10923 * (r - g)) / delta;
}
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb
uint32_t crgb;
hsv2rgb(CHSV32(hue, sat, 255), crgb);
rgb[0] = byte((crgb) >> 16);
rgb[1] = byte((crgb) >> 8);
rgb[2] = byte(crgb);
}
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
@@ -452,24 +511,8 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
}
}
//gamma 2.8 lookup table used for color correction
uint8_t NeoGammaWLEDMethod::gammaT[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
// gamma lookup table used for color correction (filled on 1st use (cfg.cpp & set.cpp))
uint8_t NeoGammaWLEDMethod::gammaT[256];
// re-calculates & fills gamma table
void NeoGammaWLEDMethod::calcGammaTable(float gamma)

View File

@@ -5,7 +5,7 @@
* Readability defines and their associated numerical values + compile-time constants
*/
#define GRADIENT_PALETTE_COUNT 58
#define GRADIENT_PALETTE_COUNT 59
// You can define custom product info from build flags.
// This is useful to allow API consumer to identify what type of WLED version
@@ -203,6 +203,7 @@
#define USERMOD_ID_LD2410 52 //Usermod "usermod_ld2410.h"
#define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h"
#define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h"
#define USERMOD_ID_DEEP_SLEEP 55 //Usermod "usermod_deep_sleep.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

View File

@@ -167,9 +167,10 @@
</div>
<div style="display: flex; justify-content: center;">
<div id="palettes" class="palettesMain">
<div id="palTop" class="palTop">
Currently in use custom palettes
</div>
<div id="distDiv" class="palTop"></div>
<div id="palTop" class="palTop">
Currently in use custom palettes
</div>
</div>
</div>
@@ -187,7 +188,7 @@
Available static palettes
</div>
</div>
</div>
</div>
</body>
@@ -204,6 +205,13 @@
var paletteName = []; // Holds the names of the palettes after load.
var svgSave = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M7,12L12,17V14H16V10H12V7L7,12Z"/></svg>'
var svgEdit = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M15.1,7.07C15.24,7.07 15.38,7.12 15.5,7.23L16.77,8.5C17,8.72 17,9.07 16.77,9.28L15.77,10.28L13.72,8.23L14.72,7.23C14.82,7.12 14.96,7.07 15.1,7.07M13.13,8.81L15.19,10.87L9.13,16.93H7.07V14.87L13.13,8.81Z"/></svg>'
var svgDist = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M4 22H2V2H4V22M22 2H20V22H22V2M13.5 7H10.5V17H13.5V7Z"/></svg>'
var svgTrash = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30px" height="30px"><path style="fill:#880000; stroke: #888888; stroke-width: -2px;stroke-dasharray: 0.1, 8;" d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/></svg>'
const distDiv = gId("distDiv");
distDiv.addEventListener('click', distribute);
distDiv.setAttribute('title', 'Distribute colors equally');
distDiv.innerHTML = svgDist;
function recOf() {
rect = gradientBox.getBoundingClientRect();
@@ -433,7 +441,7 @@
renderY = e.srcElement.getBoundingClientRect().y + 13;
trash.id = "trash";
trash.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30px" height="30px"><path style="fill:#880000; stroke: #888888; stroke-width: -2px;stroke-dasharray: 0.1, 8;" d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/></svg>';
trash.innerHTML = svgTrash;
trash.style.position = "absolute";
trash.style.left = (renderX) + "px";
trash.style.top = (renderY) + "px";
@@ -712,9 +720,27 @@
}
}
function distribute() {
let colorMarkers = [...gradientBox.querySelectorAll('.color-marker')];
colorMarkers.sort((a, b) => a.getAttribute('data-truepos') - b.getAttribute('data-truepos'));
colorMarkers = colorMarkers.slice(1, -1);
const spacing = Math.round(256 / (colorMarkers.length + 1));
colorMarkers.forEach((e, i) => {
const markerId = e.id.match(/\d+/)[0];
const trueCol = e.getAttribute("data-truecol");
gradientBox.removeChild(e);
gradientBox.removeChild(gId(`colorPicker${markerId}`));
gradientBox.removeChild(gId(`colorPickerMarker${markerId}`));
gradientBox.removeChild(gId(`deleteMarker${markerId}`));
addC(spacing * (i + 1), trueCol);
});
}
function rgbToHex(r, g, b) {
const hex = ((r << 16) | (g << 8) | b).toString(16);
return "#" + "0".repeat(6 - hex.length) + hex;
}
</script>
</html>

View File

@@ -97,6 +97,7 @@ button {
.labels {
margin: 0;
padding: 8px 0 2px 0;
font-size: 19px;
}
#namelabel {
@@ -890,12 +891,12 @@ a.btn {
line-height: 28px;
}
/* Quick color select Black button (has white border) */
.qcsb {
/* Quick color select Black and White button (has white/black border, depending on the theme) */
.qcsb, .qcsw {
width: 26px;
height: 26px;
line-height: 26px;
border: 1px solid #fff;
border: 1px solid var(--c-f);
}
/* Hex color input wrapper div */
@@ -1299,6 +1300,14 @@ TD .checkmark, TD .radiomark {
width: 100%;
}
#segutil {
margin-bottom: 12px;
}
#segcont > div:first-child, #fxFind {
margin-top: 4px;
}
/* Simplify segments */
.simplified #segcont .lstI {
margin-top: 4px;
@@ -1438,6 +1447,11 @@ dialog {
position: relative;
}
.presin {
width: 100%;
box-sizing: border-box;
}
.btn-s,
.btn-n {
border: 1px solid var(--c-2);

View File

@@ -106,7 +106,7 @@
<div class="qcs" onclick="pC('#ffa000');" style="background-color:#ffa000;"></div>
<div class="qcs" onclick="pC('#ffc800');" style="background-color:#ffc800;"></div>
<div class="qcs" onclick="pC('#ffe0a0');" style="background-color:#ffe0a0;"></div>
<div class="qcs" onclick="pC('#ffffff');" style="background-color:#ffffff;"></div>
<div class="qcs qcsw" onclick="pC('#ffffff');" style="background-color:#ffffff;"></div>
<div class="qcs qcsb" onclick="pC('#000000');" style="background-color:#000000;"></div><br>
<div class="qcs" onclick="pC('#ff00ff');" style="background-color:#ff00ff;"></div>
<div class="qcs" onclick="pC('#0000ff');" style="background-color:#0000ff;"></div>

View File

@@ -2827,7 +2827,7 @@ function search(field, listId = null) {
// restore default preset sorting if no search term is entered
if (!search) {
if (listId === 'pcont') { populatePresets(); return; }
if (listId === 'pcont') { populatePresets(); return; }
if (listId === 'pallist') {
let id = parseInt(d.querySelector('#pallist input[name="palette"]:checked').value); // preserve selected palette
populatePalettes();
@@ -2846,12 +2846,16 @@ function search(field, listId = null) {
// filter list items but leave (Default & Solid) always visible
const listItems = gId(listId).querySelectorAll('.lstI');
listItems.forEach((listItem,i)=>{
if (listId!=='pcont' && i===0) return;
listItems.forEach((listItem, i) => {
if (listId !== 'pcont' && i === 0) return;
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
const searchIndex = listItemName.indexOf(field.value.toUpperCase());
listItem.style.display = (searchIndex < 0) ? 'none' : '';
listItem.dataset.searchIndex = searchIndex;
if (searchIndex < 0) {
listItem.dataset.searchIndex = Number.MAX_SAFE_INTEGER;
} else {
listItem.dataset.searchIndex = searchIndex;
}
listItem.style.display = (searchIndex < 0) && !listItem.classList.contains("selected") ? 'none' : '';
});
// sort list items by search index and name
@@ -2920,11 +2924,11 @@ function filterFx() {
inputField.value = '';
inputField.focus();
clean(inputField.nextElementSibling);
gId("fxlist").querySelectorAll('.lstI').forEach((listItem,i) => {
gId("fxlist").querySelectorAll('.lstI').forEach((listItem, i) => {
const listItemName = listItem.querySelector('.lstIname').innerText;
let hide = false;
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i>0 /*true*/; });
listItem.style.display = hide ? 'none' : '';
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i > 0 /*true*/; });
listItem.style.display = hide && !listItem.classList.contains("selected") ? 'none' : '';
});
}

View File

@@ -54,8 +54,8 @@ Orientation: <select name="P${i}V" oninput="UI()">
</select><br>
Serpentine: <input type="checkbox" name="P${i}S" oninput="UI()"><br>
Dimensions (WxH): <input name="P${i}W" type="number" min="1" max="255" value="${pw}" oninput="UI()"> x <input name="P${i}H" type="number" min="1" max="255" value="${ph}" oninput="UI()"><br>
Offset X:<input name="P${i}X" type="number" min="0" max="255" value="0" oninput="UI()">
Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()"><br><i>(offset from top-left corner in # LEDs)</i>
Offset X: <input name="P${i}X" type="number" min="0" max="255" value="0" oninput="UI()">
Y: <input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()"><br><i>(offset from top-left corner in # LEDs)</i>
</div>`;
p.insertAdjacentHTML("beforeend", b);
}

View File

@@ -6,7 +6,7 @@
<title>LED Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var laprev=55,maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var oMaxB=1;
var customStarts=false,startsDirty=[];
function off(n) { gN(n).value = -1;}
@@ -42,15 +42,14 @@
if (loc) d.Sf.action = getURL('/settings/leds');
}
function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
// maxB - max buses (can be changed if using ESP32 parallel I2S)
// maxD - max digital channels (can be changed if using ESP32 parallel I2S)
// maxA - max analog channels
// maxV - min virtual buses
// maxPB - max LEDs per bus
// maxM - max LED memory
// maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
// maxCO - max Color Order mappings
oMaxB = maxB = b; maxD = d, maxA = a, maxV = v; maxM = m; maxPB = p; maxL = l; maxCO = o;
oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S)
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S)
maxA = a; // maxA - max analog channels
maxV = v; // maxV - min virtual buses
maxPB = p; // maxPB - max LEDs per bus
maxM = m; // maxM - max LED memory
maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
maxCO = o; // maxCO - max Color Order mappings
}
function pinsOK() {
var ok = true;
@@ -380,6 +379,11 @@
gId('psu').innerHTML = s;
gId('psu2').innerHTML = s2;
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
// show/hide FPS warning messages
gId('fpsNone').style.display = (d.Sf.FR.value == 0) ? 'block':'none';
gId('fpsWarn').style.display = (d.Sf.FR.value == 0) || (d.Sf.FR.value >= 80) ? 'block':'none';
gId('fpsHigh').style.display = (d.Sf.FR.value >= 80) ? 'block':'none';
}
function lastEnd(i) {
if (i-- < 1) return 0;
@@ -472,6 +476,8 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
if (i >= maxB || twopinB >= 1) disable(sel,'option[data-type="2P"]'); // NOTE: see isD2P()
disable(sel,`option[data-type^="${'A'.repeat(maxA-analogB+1)}"]`); // NOTE: see isPWM()
sel.selectedIndex = sel.querySelector('option:not(:disabled)').index;
// initialize current limiter
enLA(d.Sf["LAsel"+s],s);
}
if (n==-1) {
o[--i].remove();--i;
@@ -755,7 +761,7 @@ Swap: <select id="xw${s}" name="XW${s}">
Enable automatic brightness limiter: <input type="checkbox" name="ABL" onchange="enABL()"><br>
<div id="abl">
<i>Automatically limits brightness to stay close to the limit.<br>
Keep at &lt;1A if poweing LEDs directly from the ESP 5V pin!<br>
Keep at &lt;1A if powering LEDs directly from the ESP 5V pin!<br>
If using multiple outputs it is recommended to use per-output limiter.<br>
Analog (PWM) and virtual LEDs cannot use automatic brightness limiter.<br></i>
<div id="psuMA">Maximum PSU Current: <input name="MA" type="number" class="xl" min="250" max="65000" oninput="UI()" required> mA<br></div>
@@ -870,7 +876,10 @@ Swap: <select id="xw${s}" name="XW${s}">
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
</select><br>
Target refresh rate: <input type="number" class="s" min="1" max="120" name="FR" required> FPS
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>
<div id="fpsWarn" class="warn" style="display: none;">Please <a class="lnk" href="sec#backup">backup</a> WLED configuration and presets first!<br></div>
<hr class="sml">
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"><button type="button" class="sml" onclick="loadCfg(d.Sf.data2)">Apply</button><br></div>
<hr>

View File

@@ -57,11 +57,11 @@
<h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO">
<hr>
<hr id="backup">
<h3>Backup & Restore</h3>
<div class="warn">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up.
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.<br>
For security reasons, passwords are not backed up.</div>
<a class="btn lnk" id="bckcfg" href="/presets.json" download="presets">Backup presets</a><br>
<div>Restore presets<br><input type="file" name="data" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data,'/presets.json');">Upload</button><br></div><br>
<a class="btn lnk" id="bckpresets" href="/cfg.json" download="cfg">Backup configuration</a><br>
@@ -78,4 +78,4 @@
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>
</html>
</html>

View File

@@ -17,7 +17,7 @@
<h2>WLED Software Update</h2>
<form method='POST' action='./update' id='uf' enctype='multipart/form-data' onsubmit="U()">
Installed version: <span class="sip">##VERSION##</span><br>
Download the latest binary:&nbsp;<a href="https://github.com/Aircoookie/WLED/releases" target="_blank"
Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank"
style="vertical-align: text-bottom; display: inline-flex;">
<img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->

View File

@@ -39,6 +39,7 @@ void handleDDPPacket(e131_packet_t* p) {
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) {
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
for (unsigned i = start; i < stop; i++, c += ddpChannelsPerLed) {
setRealtimePixel(i, data[c], data[c+1], data[c+2], ddpChannelsPerLed >3 ? data[c+3] : 0);
}
@@ -147,6 +148,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
wChannel = (availDMXLen > 3) ? e131_data[dataOffset+3] : 0;
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
for (unsigned i = 0; i < totalLen; i++)
setRealtimePixel(i, e131_data[dataOffset+0], e131_data[dataOffset+1], e131_data[dataOffset+2], wChannel);
break;
@@ -164,6 +166,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
strip.setBrightness(bri, true);
}
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
for (unsigned i = 0; i < totalLen; i++)
setRealtimePixel(i, e131_data[dataOffset+1], e131_data[dataOffset+2], e131_data[dataOffset+3], wChannel);
break;
@@ -308,6 +311,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
}
}
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
if (!is4Chan) {
for (unsigned i = previousLeds; i < ledsTotal; i++) {
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0);

View File

@@ -66,6 +66,89 @@ 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:
@@ -78,13 +161,18 @@ class NeoGammaWLEDMethod {
};
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
[[gnu::hot]] uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
[[gnu::hot]] uint32_t color_add(uint32_t,uint32_t, bool fast=false);
[[gnu::hot]] 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]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false);
[[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false);
[[gnu::hot]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND);
CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette);
CRGBPalette16 generateRandomPalette();
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 colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
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
@@ -370,9 +458,15 @@ void userConnected();
void userLoop();
//util.cpp
#ifdef ESP8266
#define HW_RND_REGISTER RANDOM_REG32
#else // ESP32 family
#include "soc/wdev_reg.h"
#define HW_RND_REGISTER REG_READ(WDEV_RND_REG)
#endif
int getNumVal(const String* req, uint16_t pos);
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255);
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255); // getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
bool getBoolVal(JsonVariant elem, bool dflt);
bool updateVal(const char* req, const char* key, byte* val, byte minv=0, byte maxv=255);
size_t printSetFormCheckbox(Print& settingsScript, const char* key, int val);
@@ -389,11 +483,31 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
void checkSettingsPIN(const char *pin);
uint16_t crc16(const unsigned char* data_p, size_t length);
uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0);
uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0);
uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
float mapf(float x, float in_min, float in_max, float out_min, float out_max);
// fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1
// note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz)
// tests show it is still highly random reading it quickly in a loop (better than fastled PRNG)
// for 8bit and 16bit random functions: no limit check is done for best speed
// 32bit inputs are used for speed and code size, limits don't work if inverted or out of range
// inlining does save code size except for random(a,b) and 32bit random with limits
#define random hw_random // replace arduino random()
inline uint32_t hw_random() { return HW_RND_REGISTER; };
uint32_t hw_random(uint32_t upperlimit); // not inlined for code size
int32_t hw_random(int32_t lowerlimit, int32_t upperlimit);
inline uint16_t hw_random16() { return HW_RND_REGISTER; };
inline uint16_t hw_random16(uint32_t upperlimit) { return (hw_random16() * upperlimit) >> 16; }; // input range 0-65535 (uint16_t)
inline int16_t hw_random16(int32_t lowerlimit, int32_t upperlimit) { int32_t range = upperlimit - lowerlimit; return lowerlimit + hw_random16(range); }; // signed limits, use int16_t ranges
inline uint8_t hw_random8() { return HW_RND_REGISTER; };
inline uint8_t hw_random8(uint32_t upperlimit) { return (hw_random8() * upperlimit) >> 8; }; // input range 0-255
inline uint8_t hw_random8(uint32_t lowerlimit, uint32_t upperlimit) { uint32_t range = upperlimit - lowerlimit; return lowerlimit + hw_random8(range); }; // input range 0-255
// RAII guard class for the JSON Buffer lock
// Modeled after std::lock_guard
class JSONBufferGuard {
@@ -419,27 +533,37 @@ void clearEEPROM();
#endif
//wled_math.cpp
#if defined(ESP8266) && !defined(WLED_USE_REAL_MATH)
template <typename T> T atan_t(T x);
float cos_t(float phi);
float sin_t(float x);
float tan_t(float x);
float acos_t(float x);
float asin_t(float x);
float floor_t(float x);
float fmod_t(float num, float denom);
#else
#include <math.h>
#define sin_t sinf
#define cos_t cosf
#define tan_t tanf
#define asin_t asinf
#define acos_t acosf
#define atan_t atanf
#define fmod_t fmodf
#define floor_t floorf
#endif
//float cos_t(float phi); // use float math
//float sin_t(float phi);
//float tan_t(float x);
int16_t sin16_t(uint16_t theta);
int16_t cos16_t(uint16_t theta);
uint8_t sin8_t(uint8_t theta);
uint8_t cos8_t(uint8_t theta);
float sin_approx(float theta); // uses integer math (converted to float), accuracy +/-0.0015 (compared to sinf())
float cos_approx(float theta);
float tan_approx(float x);
float atan2_t(float y, float x);
float acos_t(float x);
float asin_t(float x);
template <typename T> T atan_t(T x);
float floor_t(float x);
float fmod_t(float num, float denom);
#define sin_t sin_approx
#define cos_t cos_approx
#define tan_t tan_approx
/*
#include <math.h> // standard math functions. use a lot of flash
#define sin_t sinf
#define cos_t cosf
#define tan_t tanf
#define asin_t asinf
#define acos_t acosf
#define atan_t atanf
#define fmod_t fmodf
#define floor_t floorf
*/
//wled_serial.cpp
void handleSerial();
void updateBaudRate(uint32_t rate);

View File

@@ -129,7 +129,7 @@ static void changeEffectSpeed(int8_t amount)
} else { // if Effect == "solid Color", change the hue of the primary color
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col = CRGB(sseg.colors[0]);
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
CHSV prim_hsv = rgb2hsv(fastled_col);
int16_t new_val = (int16_t)prim_hsv.h + amount;
if (new_val > 255) new_val -= 255; // roll-over if bigger than 255
if (new_val < 0) new_val += 255; // roll-over if smaller than 0
@@ -173,7 +173,7 @@ static void changeEffectIntensity(int8_t amount)
} else { // if Effect == "solid Color", change the saturation of the primary color
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col = CRGB(sseg.colors[0]);
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
CHSV prim_hsv = rgb2hsv(fastled_col);
int16_t new_val = (int16_t) prim_hsv.s + amount;
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
hsv2rgb_rainbow(prim_hsv, fastled_col);
@@ -435,7 +435,7 @@ static void decodeIR44(uint32_t code)
case IR44_DIY2 : presetFallback(2, FX_MODE_BREATH, 0); break;
case IR44_DIY3 : presetFallback(3, FX_MODE_FIRE_FLICKER, 0); break;
case IR44_DIY4 : presetFallback(4, FX_MODE_RAINBOW, 0); break;
case IR44_DIY5 : presetFallback(5, FX_MODE_METEOR_SMOOTH, 0); break;
case IR44_DIY5 : presetFallback(5, FX_MODE_METEOR, 0); break;
case IR44_DIY6 : presetFallback(6, FX_MODE_RAIN, 0); break;
case IR44_AUTO : changeEffect(FX_MODE_STATIC); break;
case IR44_FLASH : changeEffect(FX_MODE_PALETTE); break;
@@ -593,7 +593,7 @@ static void decodeIRJson(uint32_t code)
decBrightness();
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
uint8_t p1 = fdo["PL"] | 1;
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
uint8_t p2 = fdo["FX"] | hw_random8(strip.getModeCount() -1);
uint8_t p3 = fdo["FP"] | 0;
presetFallback(p1, p2, p3);
}

View File

@@ -34,7 +34,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
//DEBUG_PRINTLN(F("-- JSON deserialize segment."));
Segment& seg = strip.getSegment(id);
//DEBUG_PRINTF_P(PSTR("-- Original segment: %p (%p)\n"), &seg, seg.data);
Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor)
const Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor)
//DEBUG_PRINTF_P(PSTR("-- Duplicate segment: %p (%p)\n"), &prev, prev.data);
int start = elem["start"] | seg.start;
@@ -96,17 +96,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t of = seg.offset;
uint8_t soundSim = elem["si"] | seg.soundSim;
uint8_t map1D2D = elem["m12"] | seg.map1D2D;
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = constrain(map1D2D, 0, 7);
uint8_t set = elem[F("set")] | seg.set;
seg.set = constrain(set, 0, 3);
seg.soundSim = constrain(soundSim, 0, 3);
uint8_t set = elem[F("set")] | seg.set;
seg.set = constrain(set, 0, 3);
int len = 1;
if (stop > start) len = stop - start;
int len = (stop > start) ? stop - start : 1;
int offset = elem[F("of")] | INT32_MAX;
if (offset != INT32_MAX) {
int offsetAbs = abs(offset);
@@ -117,7 +111,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if (stop > start && of > len -1) of = len -1;
// update segment (delete if necessary)
seg.setUp(start, stop, grp, spc, of, startY, stopY); // strip needs to be suspended for this to work without issues
seg.setGeometry(start, stop, grp, spc, of, startY, stopY, map1D2D); // strip needs to be suspended for this to work without issues
if (newSeg) seg.refreshLightCapabilities(); // fix for #3403
@@ -223,30 +217,17 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
#endif
byte fx = seg.mode;
byte last = strip.getModeCount();
// partial fix for #3605
if (!elem["fx"].isNull() && elem["fx"].is<const char*>()) {
const char *tmp = elem["fx"].as<const char *>();
if (strlen(tmp) > 3 && (strchr(tmp,'r') || strchr(tmp,'~') != strrchr(tmp,'~'))) last = 0; // we have "X~Y(r|[w]~[-])" form
}
// end fix
if (getVal(elem["fx"], &fx, 0, last)) { //load effect ('r' random, '~' inc/dec, 0-255 exact value, 5~10r pick random between 5 & 10)
if (getVal(elem["fx"], &fx, 0, strip.getModeCount())) {
if (!presetId && currentPlaylist>=0) unloadPlaylist();
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")]);
}
//getVal also supports inc/decrementing and random
getVal(elem["sx"], &seg.speed);
getVal(elem["ix"], &seg.intensity);
uint8_t pal = seg.palette;
last = strip.getPaletteCount();
if (!elem["pal"].isNull() && elem["pal"].is<const char*>()) {
const char *tmp = elem["pal"].as<const char *>();
if (strlen(tmp) > 3 && (strchr(tmp,'r') || strchr(tmp,'~') != strrchr(tmp,'~'))) last = 0; // we have "X~Y(r|[w]~[-])" form
}
if (seg.getLightCapabilities() & 1) { // ignore palette for White and On/Off segments
if (getVal(elem["pal"], &pal, 0, last)) seg.setPalette(pal);
if (getVal(elem["pal"], &pal, 0, strip.getPaletteCount())) seg.setPalette(pal);
}
getVal(elem["c1"], &seg.custom1);
@@ -467,7 +448,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
DEBUG_PRINTF_P(PSTR("Preset direct: %d\n"), currentPreset);
} else if (!root["ps"].isNull()) {
// we have "ps" call (i.e. from button or external API call) or "pd" that includes "ps" (i.e. from UI call)
if (root["win"].isNull() && getVal(root["ps"], &presetCycCurr, 0, 0) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
if (root["win"].isNull() && getVal(root["ps"], &presetCycCurr, 1, 250) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
DEBUG_PRINTF_P(PSTR("Preset select: %d\n"), presetCycCurr);
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
applyPreset(presetCycCurr, callMode); // async load from file system (only preset ID was specified)
@@ -902,10 +883,7 @@ void serializePalettes(JsonObject root, int page)
setPaletteColors(curPalette, PartyColors_p);
break;
case 1: //random
curPalette.add("r");
curPalette.add("r");
curPalette.add("r");
curPalette.add("r");
for (int j = 0; j < 4; j++) curPalette.add("r");
break;
case 2: //primary color only
curPalette.add("c1");
@@ -922,53 +900,20 @@ void serializePalettes(JsonObject root, int page)
curPalette.add("c1");
break;
case 5: //primary + secondary (+tertiary if not off), more distinct
for (int j = 0; j < 5; j++) curPalette.add("c1");
for (int j = 0; j < 5; j++) curPalette.add("c2");
for (int j = 0; j < 5; j++) curPalette.add("c3");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c1");
break;
case 6: //Party colors
setPaletteColors(curPalette, PartyColors_p);
break;
case 7: //Cloud colors
setPaletteColors(curPalette, CloudColors_p);
break;
case 8: //Lava colors
setPaletteColors(curPalette, LavaColors_p);
break;
case 9: //Ocean colors
setPaletteColors(curPalette, OceanColors_p);
break;
case 10: //Forest colors
setPaletteColors(curPalette, ForestColors_p);
break;
case 11: //Rainbow colors
setPaletteColors(curPalette, RainbowColors_p);
break;
case 12: //Rainbow stripe colors
setPaletteColors(curPalette, RainbowStripeColors_p);
break;
default:
{
if (i>=palettesCount) {
if (i >= palettesCount)
setPaletteColors(curPalette, strip.customPalettes[i - palettesCount]);
} else {
else if (i < 13) // palette 6 - 12, fastled palettes
setPaletteColors(curPalette, *fastledPalettes[i-6]);
else {
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i - 13])), 72);
setPaletteColors(curPalette, tcp);
}
}
break;
}
}

View File

@@ -75,6 +75,7 @@ byte scaledBri(byte in)
void applyBri() {
if (!realtimeMode || !arlsForceMaxBri)
{
//DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld);
strip.setBrightness(scaledBri(briT));
}
}
@@ -139,7 +140,6 @@ void stateUpdated(byte callMode) {
if (transitionActive) {
briOld = briT;
tperLast = 0;
} else
strip.setTransitionMode(true); // force all segments to transition mode
transitionActive = true;
@@ -179,22 +179,21 @@ void handleTransitions()
updateInterfaces(interfaceUpdateCallMode);
if (transitionActive && strip.getTransition() > 0) {
float tper = (millis() - transitionStartTime)/(float)strip.getTransition();
if (tper >= 1.0f) {
int ti = millis() - transitionStartTime;
int tr = strip.getTransition();
if (ti/tr) {
strip.setTransitionMode(false); // stop all transitions
// restore (global) transition time if not called from UDP notifier or single/temporary transition from JSON (also playlist)
if (jsonTransitionOnce) strip.setTransition(transitionDelay);
transitionActive = false;
jsonTransitionOnce = false;
tperLast = 0;
applyFinalBri();
return;
}
if (tper - tperLast < 0.004f) return; // less than 1 bit change (1/255)
tperLast = tper;
briT = briOld + ((bri - briOld) * tper);
applyBri();
byte briTO = briT;
int deltaBri = (int)bri - (int)briOld;
briT = briOld + (deltaBri * ti / tr);
if (briTO != briT) applyBri();
}
}
@@ -229,8 +228,8 @@ void handleNightlight()
colNlT[1] = effectSpeed;
colNlT[2] = effectPalette;
strip.setMode(strip.getFirstSelectedSegId(), FX_MODE_STATIC); // make sure seg runtime is reset if it was in sunrise mode
effectCurrent = FX_MODE_SUNRISE;
strip.getFirstSelectedSeg().setMode(FX_MODE_STATIC); // make sure seg runtime is reset if it was in sunrise mode
effectCurrent = FX_MODE_SUNRISE; // colorUpdated() will take care of assigning that to all selected segments
effectSpeed = nightlightDelayMins;
effectPalette = 0;
if (effectSpeed > 60) effectSpeed = 60; //currently limited to 60 minutes

View File

@@ -207,6 +207,7 @@ void WiFiEvent(WiFiEvent_t event)
break;
#endif
default:
DEBUG_PRINTF_P(PSTR("Network event: %d\n"), (int)event);
break;
}
}

View File

@@ -1,5 +1,6 @@
/*
* Color palettes for FastLED effects (65-73).
* 4 bytes per color: index, red, green, blue
*/
// From ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb
@@ -844,6 +845,23 @@ const byte candy2_gp[] PROGMEM = {
211, 39, 33, 34,
255, 1, 1, 1};
const byte trafficlight_gp[] PROGMEM = {
0, 0, 0, 0, //black
85, 0, 255, 0, //green
170, 255, 255, 0, //yellow
255, 255, 0, 0}; //red
// array of fastled palettes (palette 6 - 12)
const TProgmemRGBPalette16 *const fastledPalettes[] PROGMEM = {
&PartyColors_p, //06-00 Party
&CloudColors_p, //07-01 Cloud
&LavaColors_p, //08-02 Lava
&OceanColors_p, //09-03 Ocean
&ForestColors_p, //10-04 Forest
&RainbowColors_p, //11-05 Rainbow
&RainbowStripeColors_p //12-06 Rainbow Bands
};
// Single array of defined cpt-city color palettes.
// This will let us programmatically choose one based on
// a number, rather than having to activate each explicitly
@@ -906,7 +924,8 @@ const byte* const gGradientPalettes[] PROGMEM = {
blink_red_gp, //67-54 Blink Red
red_shift_gp, //68-55 Red Shift
red_tide_gp, //69-56 Red Tide
candy2_gp //70-57 Candy2
candy2_gp, //70-57 Candy2
trafficlight_gp //71-58 Traffic Light
};
#endif

View File

@@ -164,6 +164,11 @@ void handlePresets()
DEBUG_PRINTF_P(PSTR("Applying preset: %u\n"), (unsigned)tmpPreset);
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
unsigned long start = millis();
while (strip.isUpdating() && millis() - start < FRAMETIME_FIXED) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
#endif
#ifdef ARDUINO_ARCH_ESP32
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
deserializeJson(*pDoc,tmpRAMbuffer);

View File

@@ -146,7 +146,7 @@ static bool remoteJson(int button)
parsed = true;
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
uint8_t p1 = fdo["PL"] | 1;
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
uint8_t p2 = fdo["FX"] | hw_random8(strip.getModeCount() -1);
uint8_t p3 = fdo["FP"] | 0;
presetWithFallback(p1, p2, p3);
parsed = true;

View File

@@ -215,6 +215,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
// we will not bother with pre-allocating ColorOrderMappings vector
BusManager::getColorOrderMap().reset();
for (int s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
int offset = s < 10 ? 48 : 55;
char xs[4] = "XS"; xs[2] = offset+s; xs[3] = 0; //start LED
@@ -318,13 +319,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
gammaCorrectBri = request->hasArg(F("GB"));
gammaCorrectCol = request->hasArg(F("GC"));
gammaCorrectVal = request->arg(F("GV")).toFloat();
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3)
NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal);
else {
if (gammaCorrectVal <= 1.0f || gammaCorrectVal > 3) {
gammaCorrectVal = 1.0f; // no gamma correction
gammaCorrectBri = false;
gammaCorrectCol = false;
}
NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table
fadeTransition = request->hasArg(F("TF"));
modeBlending = request->hasArg(F("EB"));
@@ -838,8 +838,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
// temporary values, write directly to segments, globals are updated by setValuesFromFirstSelectedSeg()
uint32_t col0 = selseg.colors[0];
uint32_t col1 = selseg.colors[1];
uint32_t col0 = selseg.colors[0];
uint32_t col1 = selseg.colors[1];
uint32_t col2 = selseg.colors[2];
byte colIn[4] = {R(col0), G(col0), B(col0), W(col0)};
byte colInSec[4] = {R(col1), G(col1), B(col1), W(col1)};
byte effectIn = selseg.mode;
@@ -874,7 +875,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
spcI = std::max(0,getNumVal(&req, pos));
}
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
strip.suspend(); // must suspend strip operations before changing geometry
selseg.setGeometry(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY, selseg.map1D2D);
strip.resume();
pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';
@@ -920,7 +923,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set brightness
updateVal(req.c_str(), "&A=", &bri);
bool col0Changed = false, col1Changed = false;
bool col0Changed = false, col1Changed = false, col2Changed = false;
//set colors
col0Changed |= updateVal(req.c_str(), "&R=", &colIn[0]);
col0Changed |= updateVal(req.c_str(), "&G=", &colIn[1]);
@@ -977,7 +980,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
//set color from HEX or 32bit DEC
byte tmpCol[4];
pos = req.indexOf(F("CL="));
if (pos > 0) {
colorFromDecOrHexString(colIn, (char*)req.substring(pos + 3).c_str());
@@ -990,10 +992,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
pos = req.indexOf(F("C3="));
if (pos > 0) {
byte tmpCol[4];
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
selseg.setColor(2, col2); // defined above (SS= or main)
if (!singleSegment) strip.setColor(2, col2); // will set color to all active & selected segments
col2Changed = true;
}
//set to random hue SR=0->1st SR=1->2nd
@@ -1004,29 +1007,22 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
col0Changed |= (!sec); col1Changed |= sec;
}
//swap 2nd & 1st
pos = req.indexOf(F("SC"));
if (pos > 0) {
byte temp;
for (unsigned i=0; i<4; i++) {
temp = colIn[i];
colIn[i] = colInSec[i];
colInSec[i] = temp;
}
col0Changed = col1Changed = true;
}
// apply colors to selected segment, and all selected segments if applicable
if (col0Changed) {
uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]);
selseg.setColor(0, colIn0);
if (!singleSegment) strip.setColor(0, colIn0); // will set color to all active & selected segments
col0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]);
selseg.setColor(0, col0);
}
if (col1Changed) {
uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]);
selseg.setColor(1, colIn1);
if (!singleSegment) strip.setColor(1, colIn1); // will set color to all active & selected segments
col1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]);
selseg.setColor(1, col1);
}
//swap 2nd & 1st
pos = req.indexOf(F("SC"));
if (pos > 0) {
std::swap(col0,col1);
col0Changed = col1Changed = true;
}
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
@@ -1056,6 +1052,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (speedChanged) seg.speed = speedIn;
if (intensityChanged) seg.intensity = intensityIn;
if (paletteChanged) seg.setPalette(paletteIn);
if (col0Changed) seg.setColor(0, col0);
if (col1Changed) seg.setColor(1, col1);
if (col2Changed) seg.setColor(2, col2);
if (custom1Changed) seg.custom1 = custom1In;
if (custom2Changed) seg.custom2 = custom2In;
if (custom3Changed) seg.custom3 = custom3In;

View File

@@ -34,8 +34,8 @@ static const int enablePin = -1; // disable the enable pin because it is not ne
static const int rxPin = -1; // disable the receiving pin because it is not needed - softhack007: Pin=-1 means "use default" not "disable"
static const int txPin = 2; // transmit DMX data over this pin (default is pin 2)
//DMX value array and size. Entry 0 will hold startbyte
static uint8_t dmxData[dmxMaxChannel] = { 0 };
//DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements
static uint8_t dmxData[dmxMaxChannel+1] = { 0 };
static int chanSize = 0;
#if !defined(DMX_SEND_ONLY)
static int currentChannel = 0;

View File

@@ -234,12 +234,12 @@ void parseNotifyPacket(uint8_t *udpIn) {
//apply colors from notification to main segment, only if not syncing full segments
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
// primary color, only apply white if intented (version > 0)
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
strip.getMainSegment().setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
if (version > 1) {
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
strip.getMainSegment().setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
}
if (version > 6) {
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
strip.getMainSegment().setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
if (version > 9 && udpIn[37] < 255) { // valid CCT/Kelvin value
unsigned cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
@@ -260,11 +260,12 @@ void parseNotifyPacket(uint8_t *udpIn) {
// are we syncing bounds and slave has more active segments than master?
if (receiveSegmentBounds && numSrcSegs < strip.getActiveSegmentsNum()) {
DEBUG_PRINTLN(F("Removing excessive segments."));
for (size_t i=strip.getSegmentsNum(); i>numSrcSegs; i--) {
if (strip.getSegment(i).isActive()) {
strip.setSegment(i-1,0,0); // delete segment
}
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
for (size_t i=strip.getSegmentsNum(); i>numSrcSegs && i>0; i--) {
Segment &seg = strip.getSegment(i-1);
if (seg.isActive()) seg.deactivate(); // delete segment
}
strip.resume();
}
size_t inactiveSegs = 0;
for (size_t i = 0; i < numSrcSegs && i < strip.getMaxSegments(); i++) {
@@ -300,7 +301,7 @@ void parseNotifyPacket(uint8_t *udpIn) {
if (!receiveSegmentOptions) {
DEBUG_PRINTF_P(PSTR("Set segment w/o options: %d [%d,%d;%d,%d]\n"), id, (int)start, (int)stop, (int)startY, (int)stopY);
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
selseg.setGeometry(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY, selseg.map1D2D);
strip.resume();
continue; // we do receive bounds, but not options
}
@@ -342,12 +343,12 @@ void parseNotifyPacket(uint8_t *udpIn) {
if (receiveSegmentBounds) {
DEBUG_PRINTF_P(PSTR("Set segment w/ options: %d [%d,%d;%d,%d]\n"), id, (int)start, (int)stop, (int)startY, (int)stopY);
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
selseg.setUp(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
selseg.setGeometry(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY, selseg.map1D2D);
strip.resume();
} else {
DEBUG_PRINTF_P(PSTR("Set segment grouping: %d [%d,%d]\n"), id, (int)udpIn[5+ofs], (int)udpIn[6+ofs]);
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
selseg.setUp(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
selseg.setGeometry(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY, selseg.map1D2D);
strip.resume();
}
}
@@ -416,18 +417,18 @@ void realtimeLock(uint32_t timeoutMs, byte md)
start = mainseg.start;
stop = mainseg.stop;
mainseg.freeze = true;
// if WLED was off and using main segment only, freeze non-main segments so they stay off
if (bri == 0) {
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
strip.getSegment(s).freeze = true;
}
}
} else {
start = 0;
stop = strip.getLengthTotal();
}
// clear strip/segment
for (size_t i = start; i < stop; i++) strip.setPixelColor(i,BLACK);
// if WLED was off and using main segment only, freeze non-main segments so they stay off
if (useMainSegmentOnly && bri == 0) {
for (size_t s=0; s < strip.getSegmentsNum(); s++) {
strip.getSegment(s).freeze = true;
}
}
}
// if strip is off (bri==0) and not already in RTM
if (briT == 0 && !realtimeMode && !realtimeOverride) {
@@ -510,12 +511,10 @@ void handleNotifications()
rgbUdp.read(lbuf, packetSize);
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION);
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
unsigned id = 0;
unsigned totalLen = strip.getLengthTotal();
for (size_t i = 0; i < packetSize -2; i += 3)
{
if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor()
for (size_t i = 0, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) {
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
id++; if (id >= totalLen) break;
}
if (!(realtimeMode && useMainSegmentOnly)) strip.show();
return;
@@ -595,17 +594,11 @@ void handleNotifications()
unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
unsigned totalLen = strip.getLengthTotal();
for (size_t i = 6; i < tpmPayloadFrameSize + 4U; i += 3)
{
if (id < totalLen)
{
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
id++;
}
else break;
if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor()
for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) {
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
}
if (tpmPacketCount == numPackets) //reset packet count and show if all packets were received
{
if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received
tpmPacketCount = 0;
strip.show();
}
@@ -629,6 +622,7 @@ void handleNotifications()
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
unsigned totalLen = strip.getLengthTotal();
if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor()
if (udpIn[0] == 1 && packetSize > 5) //warls
{
for (size_t i = 2; i < packetSize -3; i += 4)
@@ -637,39 +631,29 @@ void handleNotifications()
}
} else if (udpIn[0] == 2 && packetSize > 4) //drgb
{
unsigned id = 0;
for (size_t i = 2; i < packetSize -2; i += 3)
for (size_t i = 2, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++)
{
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
id++; if (id >= totalLen) break;
}
} else if (udpIn[0] == 3 && packetSize > 6) //drgbw
{
unsigned id = 0;
for (size_t i = 2; i < packetSize -3; i += 4)
for (size_t i = 2, id = 0; i < packetSize -3 && id < totalLen; i += 4, id++)
{
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
id++; if (id >= totalLen) break;
}
} else if (udpIn[0] == 4 && packetSize > 7) //dnrgb
{
unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
for (size_t i = 4; i < packetSize -2; i += 3)
for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 3, id++)
{
if (id >= totalLen) break;
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
id++;
}
} else if (udpIn[0] == 5 && packetSize > 8) //dnrgbw
{
unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
for (size_t i = 4; i < packetSize -2; i += 4)
for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 4, id++)
{
if (id >= totalLen) break;
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
id++;
}
}
strip.show();
@@ -704,11 +688,11 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
b = gamma8(b);
w = gamma8(w);
}
uint32_t col = RGBW32(r,g,b,w);
if (useMainSegmentOnly) {
Segment &seg = strip.getMainSegment();
if (pix<seg.length()) seg.setPixelColor(pix, r, g, b, w);
strip.getMainSegment().setPixelColor(pix, col); // this expects that strip.getMainSegment().beginDraw() has been called in handleNotification()
} else {
strip.setPixelColor(pix, r, g, b, w);
strip.setPixelColor(pix, col);
}
}
}

View File

@@ -45,7 +45,7 @@
#endif
#ifdef USERMOD_BH1750
#include "../usermods/BH1750_v2/usermod_BH1750.h"
#include "../usermods/BH1750_v2/usermod_bh1750.h"
#endif
// BME280 v2 usermod. Define "USERMOD_BME280" in my_config.h
@@ -242,6 +242,11 @@
#include "../usermods/LD2410_v2/usermod_ld2410.h"
#endif
#ifdef USERMOD_DEEP_SLEEP
#include "../usermods/deep_sleep/usermod_deep_sleep.h"
#endif
void registerUsermods()
{
/*
@@ -470,4 +475,8 @@ void registerUsermods()
#ifdef USERMOD_POV_DISPLAY
UsermodManager::add(new PovDisplayUsermod());
#endif
#ifdef USERMOD_DEEP_SLEEP
usermods.add(new DeepSleepUsermod());
#endif
}

View File

@@ -14,7 +14,7 @@ int getNumVal(const String* req, uint16_t pos)
void parseNumber(const char* str, byte* val, byte minv, byte maxv)
{
if (str == nullptr || str[0] == '\0') return;
if (str[0] == 'r') {*val = random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
if (str[0] == 'r') {*val = hw_random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
bool wrap = false;
if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;}
if (str[0] == '~') {
@@ -52,7 +52,7 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
*val = atoi(str);
}
//getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
if (elem.is<int>()) {
if (elem < 0) return false; //ignore e.g. {"ps":-1}
@@ -60,8 +60,12 @@ bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
return true;
} else if (elem.is<const char*>()) {
const char* str = elem;
size_t len = strnlen(str, 12);
if (len == 0 || len > 10) return false;
size_t len = strnlen(str, 14);
if (len == 0 || len > 12) return false;
// fix for #3605 & #4346
// ignore vmin and vmax and use as specified in API
if (len > 3 && (strchr(str,'r') || strchr(str,'~') != strrchr(str,'~'))) vmax = vmin = 0; // we have "X~Y(r|~[w][-][Z])" form
// end fix
parseNumber(str, val, vmin, vmax);
return true;
}
@@ -372,6 +376,39 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
return crc;
}
// fastled beatsin: 1:1 replacements to remove the use of fastled sin16()
// Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
{
uint16_t beat = beat88( beats_per_minute_88, timebase);
uint16_t beatsin (sin16_t( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
// Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
{
uint16_t beat = beat16( beats_per_minute, timebase);
uint16_t beatsin = (sin16_t( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
// Generates an 8-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset)
{
uint8_t beat = beat8( beats_per_minute, timebase);
uint8_t beatsin = sin8_t( beat + phase_offset);
uint8_t rangewidth = highest - lowest;
uint8_t scaledbeat = scale8( beatsin, rangewidth);
uint8_t result = lowest + scaledbeat;
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Begin simulateSound (to enable audio enhanced effects to display something)
@@ -431,15 +468,15 @@ um_data_t* simulateSound(uint8_t simulationId)
default:
case UMS_BeatSin:
for (int i = 0; i<16; i++)
fftResult[i] = beatsin8(120 / (i+1), 0, 255);
// fftResult[i] = (beatsin8(120, 0, 255) + (256/16 * i)) % 256;
fftResult[i] = beatsin8_t(120 / (i+1), 0, 255);
// fftResult[i] = (beatsin8_t(120, 0, 255) + (256/16 * i)) % 256;
volumeSmth = fftResult[8];
break;
case UMS_WeWillRockYou:
if (ms%2000 < 200) {
volumeSmth = random8(255);
volumeSmth = hw_random8();
for (int i = 0; i<5; i++)
fftResult[i] = random8(255);
fftResult[i] = hw_random8();
}
else if (ms%2000 < 400) {
volumeSmth = 0;
@@ -447,9 +484,9 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0;
}
else if (ms%2000 < 600) {
volumeSmth = random8(255);
volumeSmth = hw_random8();
for (int i = 5; i<11; i++)
fftResult[i] = random8(255);
fftResult[i] = hw_random8();
}
else if (ms%2000 < 800) {
volumeSmth = 0;
@@ -457,9 +494,9 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0;
}
else if (ms%2000 < 1000) {
volumeSmth = random8(255);
volumeSmth = hw_random8();
for (int i = 11; i<16; i++)
fftResult[i] = random8(255);
fftResult[i] = hw_random8();
}
else {
volumeSmth = 0;
@@ -469,17 +506,17 @@ um_data_t* simulateSound(uint8_t simulationId)
break;
case UMS_10_13:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
fftResult[i] = inoise8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8];
break;
case UMS_14_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
fftResult[i] = inoise8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8];
break;
}
samplePeak = random8() > 250;
samplePeak = hw_random8() > 250;
FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // walk thru full range of 21hz...8200hz
maxVol = 31; // this gets feedback fro UI
binNum = 8; // this gets feedback fro UI
@@ -545,7 +582,7 @@ void enumerateLedmaps() {
uint8_t get_random_wheel_index(uint8_t pos) {
uint8_t r = 0, x = 0, y = 0, d = 0;
while (d < 42) {
r = random8();
r = hw_random8();
x = abs(pos - r);
y = 255 - x;
d = MIN(x, y);
@@ -557,3 +594,18 @@ uint8_t get_random_wheel_index(uint8_t pos) {
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// 32 bit random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h)
uint32_t hw_random(uint32_t upperlimit) {
uint32_t rnd = hw_random();
uint64_t scaled = uint64_t(rnd) * uint64_t(upperlimit);
return scaled >> 32;
}
int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) {
if(lowerlimit >= upperlimit) {
return lowerlimit;
}
uint32_t diff = upperlimit - lowerlimit;
return hw_random(diff) + lowerlimit;
}

View File

@@ -222,6 +222,7 @@ void WLED::loop()
BusManager::setBrightness(bri); // fix re-initialised bus' brightness #4005
if (aligned) strip.makeAutoSegments();
else strip.fixInvalidSegments();
BusManager::setBrightness(bri); // fix re-initialised bus' brightness
doSerializeConfig = true;
}
if (loadLedmap >= 0) {
@@ -478,10 +479,7 @@ void WLED::setup()
if (strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) == 0)
showWelcomePage = true;
WiFi.persistent(false);
#ifdef WLED_USE_ETHERNET
WiFi.onEvent(WiFiEvent);
#endif
WiFi.mode(WIFI_STA); // enable scanning
findWiFi(true); // start scanning for available WiFi-s
@@ -546,14 +544,8 @@ void WLED::setup()
#endif
// Seed FastLED random functions with an esp random value, which already works properly at this point.
#if defined(ARDUINO_ARCH_ESP32)
const uint32_t seed32 = esp_random();
#elif defined(ARDUINO_ARCH_ESP8266)
const uint32_t seed32 = RANDOM_REG32;
#else
const uint32_t seed32 = random(std::numeric_limits<long>::max());
#endif
random16_set_seed((uint16_t)((seed32 & 0xFFFF) ^ (seed32 >> 16)));
const uint32_t seed32 = hw_random();
random16_set_seed((uint16_t)seed32);
#if WLED_WATCHDOG_TIMEOUT > 0
enableWatchdog();
@@ -578,10 +570,11 @@ void WLED::beginStrip()
} else {
// fix for #3196
if (bootPreset > 0) {
bool oldTransition = fadeTransition; // workaround if transitions are enabled
fadeTransition = false; // ignore transitions temporarily
strip.setColor(0, BLACK); // set all segments black
fadeTransition = oldTransition; // restore transitions
// set all segments black (no transition)
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
Segment &seg = strip.getSegment(i);
if (seg.isActive()) seg.colors[0] = BLACK;
}
col[0] = col[1] = col[2] = col[3] = 0; // needed for colorUpdated()
}
briLast = briS; bri = 0;
@@ -781,7 +774,7 @@ int8_t WLED::findWiFi(bool doScan) {
void WLED::initConnection()
{
DEBUG_PRINTLN(F("initConnection() called."));
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
#ifdef WLED_ENABLE_WEBSOCKETS
ws.onEvent(wsEvent);
@@ -825,9 +818,7 @@ void WLED::initConnection()
if (WLED_WIFI_CONFIGURED) {
showWelcomePage = false;
DEBUG_PRINT(F("Connecting to "));
DEBUG_PRINT(multiWiFi[selectedWiFi].clientSSID);
DEBUG_PRINTLN(F("..."));
DEBUG_PRINTF_P(PSTR("Connecting to %s...\n"), multiWiFi[selectedWiFi].clientSSID);
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
char hostname[25];
@@ -926,7 +917,8 @@ void WLED::handleConnection()
{
static bool scanDone = true;
static byte stacO = 0;
unsigned long now = millis();
const unsigned long now = millis();
const unsigned long nowS = now/1000;
const bool wifiConfigured = WLED_WIFI_CONFIGURED;
// ignore connection handling if WiFi is configured and scan still running
@@ -935,7 +927,7 @@ void WLED::handleConnection()
return;
if (lastReconnectAttempt == 0 || forceReconnect) {
DEBUG_PRINTLN(F("Initial connect or forced reconnect."));
DEBUG_PRINTF_P(PSTR("Initial connect or forced reconnect (@ %lus).\n"), nowS);
selectedWiFi = findWiFi(); // find strongest WiFi
initConnection();
interfacesInited = false;
@@ -955,8 +947,7 @@ void WLED::handleConnection()
#endif
if (stac != stacO) {
stacO = stac;
DEBUG_PRINT(F("Connected AP clients: "));
DEBUG_PRINTLN(stac);
DEBUG_PRINTF_P(PSTR("Connected AP clients: %d\n"), (int)stac);
if (!WLED_CONNECTED && wifiConfigured) { // trying to connect, but not connected
if (stac)
WiFi.disconnect(); // disable search so that AP can work
@@ -979,6 +970,7 @@ void WLED::handleConnection()
initConnection();
interfacesInited = false;
scanDone = true;
return;
}
//send improv failed 6 seconds after second init attempt (24 sec. after provisioning)
if (improvActive > 2 && now - lastReconnectAttempt > 6000) {
@@ -987,13 +979,13 @@ void WLED::handleConnection()
}
if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && wifiConfigured) {
if (improvActive == 2) improvActive = 3;
DEBUG_PRINTLN(F("Last reconnect too old."));
DEBUG_PRINTF_P(PSTR("Last reconnect (%lus) too old (@ %lus).\n"), lastReconnectAttempt/1000, nowS);
if (++selectedWiFi >= multiWiFi.size()) selectedWiFi = 0; // we couldn't connect, try with another network from the list
initConnection();
}
if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) {
if (!(apBehavior == AP_BEHAVIOR_TEMPORARY && now > WLED_AP_TIMEOUT)) {
DEBUG_PRINTLN(F("Not connected AP."));
DEBUG_PRINTF_P(PSTR("Not connected AP (@ %lus).\n"), nowS);
initAP(); // start AP only within first 5min
}
}
@@ -1003,7 +995,7 @@ void WLED::handleConnection()
dnsServer.stop();
WiFi.softAPdisconnect(true);
apActive = false;
DEBUG_PRINTLN(F("Temporary AP disabled."));
DEBUG_PRINTF_P(PSTR("Temporary AP disabled (@ %lus).\n"), nowS);
}
}
} else if (!interfacesInited) { //newly connected

View File

@@ -3,12 +3,11 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.15.0-b7
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2410270
#define VERSION 2412040
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -264,12 +263,12 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#define WLED_VERSION dev
#endif
#ifndef WLED_RELEASE_NAME
#define WLED_RELEASE_NAME dev_release
#define WLED_RELEASE_NAME "Custom"
#endif
// Global Variable definitions
WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION));
WLED_GLOBAL char releaseString[] _INIT(TOSTRING(WLED_RELEASE_NAME)); // somehow this will not work if using "const char releaseString[]
WLED_GLOBAL char releaseString[] _INIT(WLED_RELEASE_NAME); // must include the quotes when defining, e.g -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
#define WLED_CODENAME "Kōsen"
// AP and OTA default passwords (for maximum security change them!)
@@ -583,7 +582,6 @@ WLED_GLOBAL bool transitionActive _INIT(false);
WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration
WLED_GLOBAL uint16_t transitionDelayDefault _INIT(750); // default transition time (stored in cfg.json)
WLED_GLOBAL unsigned long transitionStartTime;
WLED_GLOBAL float tperLast _INIT(0.0f); // crossfade transition progress, 0.0f - 1.0f
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

View File

@@ -10,16 +10,25 @@
//#define WLED_DEBUG_MATH
// Note: cos_t, sin_t and tan_t are very accurate but slow
// the math.h functions use several kB of flash and are to be avoided if possible
// sin16_t / cos16_t are faster and much more accurate than the fastled variants
// sin_approx and cos_approx are float wrappers for sin16_t/cos16_t and have an accuracy better than +/-0.0015 compared to sinf()
// sin8_t / cos8_t are fastled replacements and use sin16_t / cos16_t. Slightly slower than fastled version but very accurate
// Taylor series approximations, replaced with Bhaskara I's approximation
/*
#define modd(x, y) ((x) - (int)((x) / (y)) * (y))
float cos_t(float phi)
{
float x = modd(phi, TWO_PI);
float x = modd(phi, M_TWOPI);
if (x < 0) x = -1 * x;
int8_t sign = 1;
if (x > PI)
if (x > M_PI)
{
x -= PI;
x -= M_PI;
sign = -1;
}
float xx = x * x;
@@ -31,8 +40,8 @@ float cos_t(float phi)
return res;
}
float sin_t(float x) {
float res = cos_t(HALF_PI - x);
float sin_t(float phi) {
float res = cos_t(M_PI_2 - phi);
#ifdef WLED_DEBUG_MATH
Serial.printf("sin: %f,%f,%f,(%f)\n",x,res,sin(x),res-sin(x));
#endif
@@ -48,6 +57,80 @@ float tan_t(float x) {
#endif
return res;
}
*/
// 16-bit, integer based Bhaskara I's sine approximation: 16*x*(pi - x) / (5*pi^2 - 4*x*(pi - x))
// input is 16bit unsigned (0-65535), output is 16bit signed (-32767 to +32767)
// optimized integer implementation by @dedehai
int16_t sin16_t(uint16_t theta) {
int scale = 1;
if (theta > 0x7FFF) {
theta = 0xFFFF - theta;
scale = -1; // second half of the sine function is negative (pi - 2*pi)
}
uint32_t precal = theta * (0x7FFF - theta);
uint64_t numerator = (uint64_t)precal * (4 * 0x7FFF); // 64bit required
int32_t denominator = 1342095361 - precal; // 1342095361 is 5 * 0x7FFF^2 / 4
int16_t result = numerator / denominator;
return result * scale;
}
int16_t cos16_t(uint16_t theta) {
return sin16_t(theta + 0x4000); //cos(x) = sin(x+pi/2)
}
uint8_t sin8_t(uint8_t theta) {
int32_t sin16 = sin16_t((uint16_t)theta * 257); // 255 * 257 = 0xFFFF
sin16 += 0x7FFF + 128; //shift result to range 0-0xFFFF, +128 for rounding
return min(sin16, int32_t(0xFFFF)) >> 8; // min performs saturation, and prevents overflow
}
uint8_t cos8_t(uint8_t theta) {
return sin8_t(theta + 64); //cos(x) = sin(x+pi/2)
}
float sin_approx(float theta) {
uint16_t scaled_theta = (int)(theta * (float)(0xFFFF / M_TWOPI)); // note: do not cast negative float to uint! cast to int first (undefined on C3)
int32_t result = sin16_t(scaled_theta);
float sin = float(result) / 0x7FFF;
return sin;
}
float cos_approx(float theta) {
uint16_t scaled_theta = (int)(theta * (float)(0xFFFF / M_TWOPI)); // note: do not cast negative float to uint! cast to int first (undefined on C3)
int32_t result = sin16_t(scaled_theta + 0x4000);
float cos = float(result) / 0x7FFF;
return cos;
}
float tan_approx(float x) {
float c = cos_approx(x);
if (c==0.0f) return 0;
float res = sin_approx(x) / c;
return res;
}
#define ATAN2_CONST_A 0.1963f
#define ATAN2_CONST_B 0.9817f
// atan2_t approximation, with the idea from https://gist.github.com/volkansalma/2972237?permalink_comment_id=3872525#gistcomment-3872525
float atan2_t(float y, float x) {
float abs_y = fabs(y);
float abs_x = fabs(x);
float r = (abs_x - abs_y) / (abs_y + abs_x + 1e-10f); // avoid division by zero by adding a small nubmer
float angle;
if(x < 0) {
r = -r;
angle = M_PI_2 + M_PI_4;
}
else
angle = M_PI_2 - M_PI_4;
float add = (ATAN2_CONST_A * (r * r) - ATAN2_CONST_B) * r;
angle += add;
angle = y < 0 ? -angle : angle;
return angle;
}
//https://stackoverflow.com/questions/3380628
// Absolute error <= 6.7e-5
@@ -60,10 +143,10 @@ float acos_t(float x) {
ret = ret * xabs;
ret = ret - 0.2121144f;
ret = ret * xabs;
ret = ret + HALF_PI;
ret = ret + M_PI_2;
ret = ret * sqrt(1.0f-xabs);
ret = ret - 2 * negate * ret;
float res = negate * PI + ret;
float res = negate * M_PI + ret;
#ifdef WLED_DEBUG_MATH
Serial.printf("acos: %f,%f,%f,(%f)\n",x,res,acos(x),res-acos(x));
#endif
@@ -71,7 +154,7 @@ float acos_t(float x) {
}
float asin_t(float x) {
float res = HALF_PI - acos_t(x);
float res = M_PI_2 - acos_t(x);
#ifdef WLED_DEBUG_MATH
Serial.printf("asin: %f,%f,%f,(%f)\n",x,res,asin(x),res-asin(x));
#endif
@@ -87,7 +170,7 @@ float atan_t(float x) {
//For A/B/C, see https://stackoverflow.com/a/42542593
static const double A { 0.0776509570923569 };
static const double B { -0.287434475393028 };
static const double C { ((HALF_PI/2) - A - B) };
static const double C { ((M_PI_4) - A - B) };
// polynominal factors for approximation between 1 and 5
static const float C0 { 0.089494f };
static const float C1 { 0.974207f };
@@ -102,7 +185,7 @@ float atan_t(float x) {
x = std::abs(x);
float res;
if (x > 5.0f) { // atan(x) converges to pi/2 - (1/x) for large values
res = HALF_PI - (1.0f/x);
res = M_PI_2 - (1.0f/x);
} else if (x > 1.0f) { //1 < x < 5
float xx = x * x;
res = (C4*xx*xx)+(C3*xx*x)+(C2*xx)+(C1*x)+C0;

View File

@@ -83,7 +83,7 @@ void appendGPIOinfo(Print& settingsScript) {
// usermod pin reservations will become unnecessary when settings pages will read cfg.json directly
if (requestJSONBufferLock(6)) {
// if we can't allocate JSON buffer ignore usermod pins
JsonObject mods = pDoc->createNestedObject(F("um"));
JsonObject mods = pDoc->createNestedObject("um");
UsermodManager::addToConfig(mods);
if (!mods.isNull()) fillUMPins(settingsScript, mods);
releaseJSONBufferLock();
@@ -91,35 +91,42 @@ void appendGPIOinfo(Print& settingsScript) {
settingsScript.print(F("];"));
// add reserved (unusable) pins
bool firstPin = true;
settingsScript.print(F("d.rsvd=["));
for (unsigned i = 0; i < WLED_NUM_PINS; i++) {
if (!PinManager::isPinOk(i, false)) { // include readonly pins
settingsScript.print(i); settingsScript.print(",");
if (!firstPin) settingsScript.print(',');
settingsScript.print(i);
firstPin = false;
}
}
#ifdef WLED_ENABLE_DMX
settingsScript.print(F("2,")); // DMX hardcoded pin
if (!firstPin) settingsScript.print(',');
settingsScript.print(2); // DMX hardcoded pin
firstPin = false;
#endif
#if defined(WLED_DEBUG) && !defined(WLED_DEBUG_HOST)
settingsScript.printf_P(PSTR(",%d"),hardwareTX); // debug output (TX) pin
if (!firstPin) settingsScript.print(',');
settingsScript.print(hardwareTX); // debug output (TX) pin
firstPin = false;
#endif
//Note: Using pin 3 (RX) disables Adalight / Serial JSON
#ifdef WLED_USE_ETHERNET
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
for (unsigned p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { settingsScript.printf(",%d", esp32_nonconfigurable_ethernet_pins[p].pin); }
if (ethernetBoards[ethernetType].eth_power>=0) { settingsScript.printf(",%d", ethernetBoards[ethernetType].eth_power); }
if (ethernetBoards[ethernetType].eth_mdc>=0) { settingsScript.printf(",%d", ethernetBoards[ethernetType].eth_mdc); }
if (ethernetBoards[ethernetType].eth_mdio>=0) { settingsScript.printf(",%d", ethernetBoards[ethernetType].eth_mdio); }
switch (ethernetBoards[ethernetType].eth_clk_mode) {
if (!firstPin) settingsScript.print(',');
for (unsigned p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { settingsScript.printf("%d,",esp32_nonconfigurable_ethernet_pins[p].pin); }
if (ethernetBoards[ethernetType].eth_power >= 0) { settingsScript.printf("%d,",ethernetBoards[ethernetType].eth_power); }
if (ethernetBoards[ethernetType].eth_mdc >= 0) { settingsScript.printf("%d,",ethernetBoards[ethernetType].eth_mdc); }
if (ethernetBoards[ethernetType].eth_mdio >= 0) { settingsScript.printf("%d,",ethernetBoards[ethernetType].eth_mdio); }
switch (ethernetBoards[ethernetType].eth_clk_mode) {
case ETH_CLOCK_GPIO0_IN:
case ETH_CLOCK_GPIO0_OUT:
settingsScript.print(F("0"));
settingsScript.print(0);
break;
case ETH_CLOCK_GPIO16_OUT:
settingsScript.print(F("16"));
settingsScript.print(16);
break;
case ETH_CLOCK_GPIO17_OUT:
settingsScript.print(F("17"));
settingsScript.print(17);
break;
}
}
@@ -128,11 +135,11 @@ void appendGPIOinfo(Print& settingsScript) {
// add info for read-only GPIO
settingsScript.print(F("d.ro_gpio=["));
bool firstPin = true;
firstPin = true;
for (unsigned i = 0; i < WLED_NUM_PINS; i++) {
if (PinManager::isReadOnlyPin(i)) {
// No comma before the first pin
if (!firstPin) settingsScript.print(F(","));
if (!firstPin) settingsScript.print(',');
settingsScript.print(i);
firstPin = false;
}
@@ -140,9 +147,7 @@ void appendGPIOinfo(Print& settingsScript) {
settingsScript.print(F("];"));
// add info about max. # of pins
settingsScript.print(F("d.max_gpio="));
settingsScript.print(WLED_NUM_PINS);
settingsScript.print(F(";"));
settingsScript.printf_P(PSTR("d.max_gpio=%d;"),WLED_NUM_PINS);
}
//get values for settings form in javascript
@@ -152,6 +157,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
DEBUG_PRINTF_P(PSTR("settings resp %u\n"), (unsigned)subPage);
if (subPage <0 || subPage >10) return;
char nS[32];
if (subPage == SUBPAGE_MENU)
{
@@ -259,11 +265,9 @@ void getSettingsJS(byte subPage, Print& settingsScript)
if (subPage == SUBPAGE_LEDS)
{
char nS[32];
appendGPIOinfo(settingsScript);
settingsScript.print(F("d.ledTypes=")); settingsScript.print(BusManager::getLEDTypesJSONString().c_str()); settingsScript.print(";");
settingsScript.printf_P(PSTR("d.ledTypes=%s;"), BusManager::getLEDTypesJSONString().c_str());
// set limits
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
@@ -399,7 +403,6 @@ void getSettingsJS(byte subPage, Print& settingsScript)
if (subPage == SUBPAGE_SYNC)
{
[[maybe_unused]] char nS[32];
printSetFormValue(settingsScript,PSTR("UP"),udpPort);
printSetFormValue(settingsScript,PSTR("U2"),udpPort2);
#ifndef WLED_DISABLE_ESPNOW
@@ -465,7 +468,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("MG"),mqttGroupTopic);
printSetFormCheckbox(settingsScript,PSTR("BM"),buttonPublishMqtt);
printSetFormCheckbox(settingsScript,PSTR("RT"),retainMqttMsg);
settingsScript.printf_P(PSTR("d.Sf.MD.maxlength=%d;d.Sf.MG.maxlength=%d;d.Sf.MS.maxlength=%d;"),
settingsScript.printf_P(PSTR("d.Sf.MD.maxLength=%d;d.Sf.MG.maxLength=%d;d.Sf.MS.maxLength=%d;"),
MQTT_MAX_TOPIC_LEN, MQTT_MAX_TOPIC_LEN, MQTT_MAX_SERVER_LEN);
#else
settingsScript.print(F("toggle('MQTT');")); // hide MQTT settings
@@ -637,7 +640,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
#if defined(ARDUINO_ARCH_ESP32)
ESP.getChipModel(),
#else
F("esp8266"),
"esp8266",
#endif
VERSION);
@@ -648,8 +651,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
{
printSetFormValue(settingsScript,PSTR("SOMP"),strip.isMatrix);
#ifndef WLED_DISABLE_2D
settingsScript.printf_P(PSTR("maxPanels=%d;"),WLED_MAX_PANELS);
settingsScript.print(F("resetPanels();"));
settingsScript.printf_P(PSTR("maxPanels=%d;resetPanels();"),WLED_MAX_PANELS);
if (strip.isMatrix) {
if(strip.panels>0){
printSetFormValue(settingsScript,PSTR("PW"),strip.panel[0].width); //Set generator Width and Height to first panel size for convenience
@@ -658,12 +660,9 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("MPC"),strip.panels);
// panels
for (unsigned i=0; i<strip.panels; i++) {
char n[5];
settingsScript.print(F("addPanel("));
settingsScript.print(itoa(i,n,10));
settingsScript.print(F(");"));
settingsScript.printf_P(PSTR("addPanel(%d);"), i);
char pO[8] = { '\0' };
snprintf_P(pO, 7, PSTR("P%d"), i); // MAX_PANELS is 64 so pO will always only be 4 characters or less
snprintf_P(pO, 7, PSTR("P%d"), i); // WLED_MAX_PANELS is 18 so pO will always only be 4 characters or less
pO[7] = '\0';
unsigned l = strlen(pO);
// create P0B, P1B, ..., P63B, etc for other PxxX