2016-12-17 23:43:07 +01:00
/*
2019-02-09 16:37:20 +01:00
WS2812FX . cpp contains all effect methods
2016-12-17 23:43:07 +01:00
Harm Aldick - 2016
www . aldick . org
LICENSE
The MIT License ( MIT )
2018-09-04 15:51:38 +02:00
Copyright ( c ) 2016 Harm Aldick
2016-12-17 23:43:07 +01:00
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the " Software " ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE .
2019-02-02 15:31:43 +01:00
Modified heavily for WLED
2016-12-17 23:43:07 +01:00
*/
2019-10-07 23:38:21 +02:00
# include "FX.h"
2016-12-17 23:43:07 +01:00
2019-02-02 15:31:43 +01:00
# define IBN 5100
2019-02-09 16:37:20 +01:00
# define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
2016-12-17 23:43:07 +01:00
/*
* No blinking . Just plain old static light .
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_static ( void ) {
2019-05-22 00:23:09 +02:00
fill ( SEGCOLOR ( 0 ) ) ;
2021-04-24 06:45:50 +02:00
return ( SEGMENT . getOption ( SEG_OPTION_TRANSITIONAL ) ) ? FRAMETIME : 350 ; //update faster if in transition
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Blink / strobe function
* Alternate between color1 and color2
* if ( strobe = = true ) then create a strobe effect
2016-12-17 23:43:07 +01:00
*/
2018-10-24 02:06:07 +02:00
uint16_t WS2812FX : : blink ( uint32_t color1 , uint32_t color2 , bool strobe , bool do_palette ) {
2019-10-02 01:17:26 +02:00
uint32_t cycleTime = ( 255 - SEGMENT . speed ) * 20 ;
2021-05-30 00:08:24 +02:00
uint32_t onTime = FRAMETIME ;
if ( ! strobe ) onTime + = ( ( cycleTime * SEGMENT . intensity ) > > 8 ) ;
cycleTime + = FRAMETIME * 2 ;
uint32_t it = now / cycleTime ;
uint32_t rem = now % cycleTime ;
2019-10-02 01:17:26 +02:00
2021-05-30 00:08:24 +02:00
bool on = false ;
if ( it ! = SEGENV . step //new iteration, force on state for one frame, even if set time is too brief
| | rem < = onTime ) {
on = true ;
2019-10-02 01:17:26 +02:00
}
2021-05-30 00:08:24 +02:00
SEGENV . step = it ; //save previous iteration
2019-10-02 01:17:26 +02:00
2021-05-30 00:08:24 +02:00
uint32_t color = on ? color1 : color2 ;
2018-10-24 02:06:07 +02:00
if ( color = = color1 & & do_palette )
{
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-10-24 02:06:07 +02:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
}
2019-10-02 01:17:26 +02:00
} else fill ( color ) ;
2018-09-04 15:51:38 +02:00
2019-10-03 16:33:37 +02:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Normal blinking . 50 % on / off time .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_blink ( void ) {
2019-05-22 00:23:09 +02:00
return blink ( SEGCOLOR ( 0 ) , SEGCOLOR ( 1 ) , false , true ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
* Classic Blink effect . Cycling through the rainbow .
*/
uint16_t WS2812FX : : mode_blink_rainbow ( void ) {
2019-05-22 00:23:09 +02:00
return blink ( color_wheel ( SEGENV . call & 0xFF ) , SEGCOLOR ( 1 ) , false , false ) ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Classic Strobe effect .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_strobe ( void ) {
2019-05-22 00:23:09 +02:00
return blink ( SEGCOLOR ( 0 ) , SEGCOLOR ( 1 ) , true , true ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
* Classic Strobe effect . Cycling through the rainbow .
*/
uint16_t WS2812FX : : mode_strobe_rainbow ( void ) {
2019-05-22 00:23:09 +02:00
return blink ( color_wheel ( SEGENV . call & 0xFF ) , SEGCOLOR ( 1 ) , true , false ) ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Color wipe function
* LEDs are turned on ( color1 ) in sequence , then turned off ( color2 ) in sequence .
* if ( bool rev = = true ) then LEDs are turned off in reverse order
2016-12-17 23:43:07 +01:00
*/
2019-10-03 20:57:22 +02:00
uint16_t WS2812FX : : color_wipe ( bool rev , bool useRandomColors ) {
2019-12-23 18:48:01 +01:00
uint32_t cycleTime = 750 + ( 255 - SEGMENT . speed ) * 150 ;
2019-10-03 16:33:37 +02:00
uint32_t perc = now % cycleTime ;
uint16_t prog = ( perc * 65535 ) / cycleTime ;
bool back = ( prog > 32767 ) ;
if ( back ) {
prog - = 32767 ;
if ( SEGENV . step = = 0 ) SEGENV . step = 1 ;
2018-09-04 15:51:38 +02:00
} else {
2019-10-03 16:33:37 +02:00
if ( SEGENV . step = = 2 ) SEGENV . step = 3 ; //trigger color change
2016-12-17 23:43:07 +01:00
}
2019-10-03 20:57:22 +02:00
if ( useRandomColors ) {
if ( SEGENV . call = = 0 ) {
SEGENV . aux0 = random8 ( ) ;
SEGENV . step = 3 ;
}
if ( SEGENV . step = = 1 ) { //if flag set, change to new random color
SEGENV . aux1 = get_random_wheel_index ( SEGENV . aux0 ) ;
SEGENV . step = 2 ;
}
if ( SEGENV . step = = 3 ) {
SEGENV . aux0 = get_random_wheel_index ( SEGENV . aux1 ) ;
SEGENV . step = 0 ;
}
}
2019-11-29 18:53:01 +01:00
2019-10-03 16:33:37 +02:00
uint16_t ledIndex = ( prog * SEGLEN ) > > 15 ;
uint16_t rem = 0 ;
rem = ( prog * SEGLEN ) * 2 ; //mod 0xFFFF
rem / = ( SEGMENT . intensity + 1 ) ;
if ( rem > 255 ) rem = 255 ;
2019-10-03 20:57:22 +02:00
uint32_t col1 = useRandomColors ? color_wheel ( SEGENV . aux1 ) : SEGCOLOR ( 1 ) ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2019-10-03 16:33:37 +02:00
{
2020-01-14 10:57:23 +01:00
uint16_t index = ( rev & & back ) ? SEGLEN - 1 - i : i ;
2019-10-03 20:57:22 +02:00
uint32_t col0 = useRandomColors ? color_wheel ( SEGENV . aux0 ) : color_from_palette ( index , true , PALETTE_SOLID_WRAP , 0 ) ;
2019-10-03 16:33:37 +02:00
2020-01-14 10:57:23 +01:00
if ( i < ledIndex )
2019-10-03 16:33:37 +02:00
{
2019-10-03 20:57:22 +02:00
setPixelColor ( index , back ? col1 : col0 ) ;
2019-10-03 16:33:37 +02:00
} else
{
2019-10-03 20:57:22 +02:00
setPixelColor ( index , back ? col0 : col1 ) ;
2020-01-14 10:57:23 +01:00
if ( i = = ledIndex ) setPixelColor ( index , color_blend ( back ? col0 : col1 , back ? col1 : col0 , rem ) ) ;
2019-10-03 16:33:37 +02:00
}
}
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Lights all LEDs one after another .
*/
uint16_t WS2812FX : : mode_color_wipe ( void ) {
2019-10-03 20:57:22 +02:00
return color_wipe ( false , false ) ;
2018-09-04 15:51:38 +02:00
}
/*
* Lights all LEDs one after another . Turns off opposite
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_color_sweep ( void ) {
2019-10-03 20:57:22 +02:00
return color_wipe ( true , false ) ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Turns all LEDs after each other to a random color .
* Then starts over with another color .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_color_wipe_random ( void ) {
2019-10-03 20:57:22 +02:00
return color_wipe ( false , true ) ;
2018-09-04 15:51:38 +02:00
}
/*
* Random color introduced alternating from start and end of strip .
*/
uint16_t WS2812FX : : mode_color_sweep_random ( void ) {
2019-10-03 20:57:22 +02:00
return color_wipe ( true , true ) ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Lights all LEDs in one random color up . Then switches them
* to the next random color .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_random_color ( void ) {
2019-10-03 16:33:37 +02:00
uint32_t cycleTime = 200 + ( 255 - SEGMENT . speed ) * 50 ;
uint32_t it = now / cycleTime ;
uint32_t rem = now % cycleTime ;
uint16_t fadedur = ( cycleTime * SEGMENT . intensity ) > > 8 ;
2016-12-17 23:43:07 +01:00
2019-10-03 16:33:37 +02:00
uint32_t fade = 255 ;
if ( fadedur ) {
fade = ( rem * 255 ) / fadedur ;
if ( fade > 255 ) fade = 255 ;
2016-12-17 23:43:07 +01:00
}
2019-10-03 16:33:37 +02:00
if ( SEGENV . call = = 0 ) {
SEGENV . aux0 = random8 ( ) ;
SEGENV . step = 2 ;
}
if ( it ! = SEGENV . step ) //new color
{
SEGENV . aux1 = SEGENV . aux0 ;
2019-10-03 20:57:22 +02:00
SEGENV . aux0 = get_random_wheel_index ( SEGENV . aux0 ) ; //aux0 will store our random color wheel index
2019-10-03 16:33:37 +02:00
SEGENV . step = it ;
}
fill ( color_blend ( color_wheel ( SEGENV . aux1 ) , color_wheel ( SEGENV . aux0 ) , fade ) ) ;
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
* Lights every LED in a random color . Changes all LED at the same time
2020-12-22 16:26:19 +01:00
* to new random colors .
2018-09-04 15:51:38 +02:00
*/
2020-12-22 16:26:19 +01:00
uint16_t WS2812FX : : dynamic ( boolean smooth = false ) {
2020-01-02 20:41:15 +01:00
if ( ! SEGENV . allocateData ( SEGLEN ) ) return mode_static ( ) ; //allocation failed
2019-10-05 01:56:55 +02:00
if ( SEGENV . call = = 0 ) {
2020-01-02 20:41:15 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) SEGENV . data [ i ] = random8 ( ) ;
2019-10-05 01:56:55 +02:00
}
uint32_t cycleTime = 50 + ( 255 - SEGMENT . speed ) * 15 ;
uint32_t it = now / cycleTime ;
2019-12-30 17:34:15 +01:00
if ( it ! = SEGENV . step & & SEGMENT . speed ! = 0 ) //new color
2019-10-05 01:56:55 +02:00
{
2020-01-02 20:41:15 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
if ( random8 ( ) < = SEGMENT . intensity ) SEGENV . data [ i ] = random8 ( ) ;
2018-09-04 15:51:38 +02:00
}
2019-10-05 01:56:55 +02:00
SEGENV . step = it ;
}
2020-12-22 16:26:19 +01:00
if ( smooth ) {
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
blendPixelColor ( i , color_wheel ( SEGENV . data [ i ] ) , 16 ) ;
}
} else {
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
setPixelColor ( i , color_wheel ( SEGENV . data [ i ] ) ) ;
}
}
2019-10-05 01:56:55 +02:00
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2020-12-22 16:26:19 +01:00
/*
* Original effect " Dynamic "
*/
uint16_t WS2812FX : : mode_dynamic ( void ) {
return dynamic ( false ) ;
}
/*
* effect " Dynamic " with smoth color - fading
*/
uint16_t WS2812FX : : mode_dynamic_smooth ( void ) {
return dynamic ( true ) ;
}
2018-09-04 15:51:38 +02:00
/*
2019-10-04 01:21:18 +02:00
* Does the " standby-breathing " of well known i - Devices .
2018-09-04 15:51:38 +02:00
*/
uint16_t WS2812FX : : mode_breath ( void ) {
2019-10-04 01:21:18 +02:00
uint16_t var = 0 ;
2019-12-04 12:15:12 +01:00
uint16_t counter = ( now * ( ( SEGMENT . speed > > 3 ) + 10 ) ) ;
2019-10-04 01:21:18 +02:00
counter = ( counter > > 2 ) + ( counter > > 4 ) ; //0-16384 + 0-2048
if ( counter < 16384 ) {
if ( counter > 8192 ) counter = 8192 - ( counter - 8192 ) ;
2020-03-30 12:56:51 +02:00
var = sin16 ( counter ) / 103 ; //close to parabolic in range 0-8192, max val. 23170
2019-10-04 01:21:18 +02:00
}
uint8_t lum = 30 + var ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-10-04 01:21:18 +02:00
setPixelColor ( i , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) , lum ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-10-04 01:21:18 +02:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Fades the LEDs between two colors
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_fade ( void ) {
2019-12-04 12:15:12 +01:00
uint16_t counter = ( now * ( ( SEGMENT . speed > > 3 ) + 10 ) ) ;
uint8_t lum = triwave16 ( counter ) > > 8 ;
2019-10-04 01:21:18 +02:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-10-04 01:21:18 +02:00
setPixelColor ( i , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) , lum ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-10-04 01:21:18 +02:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-10-24 02:06:07 +02:00
* Scan mode parent function
2016-12-17 23:43:07 +01:00
*/
2018-10-24 02:06:07 +02:00
uint16_t WS2812FX : : scan ( bool dual )
{
2019-12-23 18:48:01 +01:00
uint32_t cycleTime = 750 + ( 255 - SEGMENT . speed ) * 150 ;
uint32_t perc = now % cycleTime ;
uint16_t prog = ( perc * 65535 ) / cycleTime ;
2020-01-14 10:57:23 +01:00
uint16_t size = 1 + ( ( SEGMENT . intensity * SEGLEN ) > > 9 ) ;
2019-12-24 00:48:51 +01:00
uint16_t ledIndex = ( prog * ( ( SEGLEN * 2 ) - size * 2 ) ) > > 16 ;
2016-12-17 23:43:07 +01:00
2019-05-22 00:23:09 +02:00
fill ( SEGCOLOR ( 1 ) ) ;
2016-12-17 23:43:07 +01:00
2019-12-24 00:48:51 +01:00
int led_offset = ledIndex - ( SEGLEN - size ) ;
2018-10-24 02:06:07 +02:00
led_offset = abs ( led_offset ) ;
if ( dual ) {
2019-12-24 00:48:51 +01:00
for ( uint16_t j = led_offset ; j < led_offset + size ; j + + ) {
2020-01-14 10:57:23 +01:00
uint16_t i2 = SEGLEN - 1 - j ;
2019-12-24 00:48:51 +01:00
setPixelColor ( i2 , color_from_palette ( i2 , true , PALETTE_SOLID_WRAP , ( SEGCOLOR ( 2 ) ) ? 2 : 0 ) ) ;
2019-12-23 22:49:29 +01:00
}
2018-10-24 02:06:07 +02:00
}
2018-09-04 15:51:38 +02:00
2019-12-24 00:48:51 +01:00
for ( uint16_t j = led_offset ; j < led_offset + size ; j + + ) {
2020-01-14 10:57:23 +01:00
setPixelColor ( j , color_from_palette ( j , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2018-10-24 02:06:07 +02:00
}
2018-09-04 15:51:38 +02:00
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-10-24 02:06:07 +02:00
* Runs a single pixel back and forth .
2016-12-17 23:43:07 +01:00
*/
2018-10-24 02:06:07 +02:00
uint16_t WS2812FX : : mode_scan ( void ) {
return scan ( false ) ;
}
2018-09-04 15:51:38 +02:00
2018-10-24 02:06:07 +02:00
/*
* Runs two pixel back and forth in opposite directions .
*/
uint16_t WS2812FX : : mode_dual_scan ( void ) {
return scan ( true ) ;
2016-12-17 23:43:07 +01:00
}
/*
* Cycles all LEDs at once through a rainbow .
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_rainbow ( void ) {
2020-02-09 10:33:26 +01:00
uint16_t counter = ( now * ( ( SEGMENT . speed > > 2 ) + 2 ) ) & 0xFFFF ;
2019-10-05 01:56:55 +02:00
counter = counter > > 8 ;
2020-01-13 18:29:34 +01:00
if ( SEGMENT . intensity < 128 ) {
fill ( color_blend ( color_wheel ( counter ) , WHITE , 128 - SEGMENT . intensity ) ) ;
} else {
fill ( color_wheel ( counter ) ) ;
}
2016-12-17 23:43:07 +01:00
2019-10-05 01:56:55 +02:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
* Cycles a rainbow over the entire string of LEDs .
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_rainbow_cycle ( void ) {
2020-02-09 10:33:26 +01:00
uint16_t counter = ( now * ( ( SEGMENT . speed > > 2 ) + 2 ) ) & 0xFFFF ;
2019-10-05 01:56:55 +02:00
counter = counter > > 8 ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-10-05 01:56:55 +02:00
//intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16)
uint8_t index = ( i * ( 16 < < ( SEGMENT . intensity / 29 ) ) / SEGLEN ) + counter ;
2020-01-14 10:57:23 +01:00
setPixelColor ( i , color_wheel ( index ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-10-05 01:56:55 +02:00
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
/*
* Theatre - style crawling lights .
* Inspired by the Adafruit examples .
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_theater_chase ( void ) {
2021-02-24 20:23:32 +01:00
return running ( SEGCOLOR ( 0 ) , SEGCOLOR ( 1 ) , true ) ;
2016-12-17 23:43:07 +01:00
}
/*
* Theatre - style crawling lights with rainbow effect .
* Inspired by the Adafruit examples .
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_theater_chase_rainbow ( void ) {
2021-02-24 20:23:32 +01:00
return running ( color_wheel ( SEGENV . step ) , SEGCOLOR ( 1 ) , true ) ;
2016-12-17 23:43:07 +01:00
}
/*
2019-02-05 21:53:39 +01:00
* Running lights effect with smooth sine transition base .
2016-12-17 23:43:07 +01:00
*/
2020-02-18 21:28:46 +01:00
uint16_t WS2812FX : : running_base ( bool saw , bool dual = false ) {
2019-02-05 19:40:24 +01:00
uint8_t x_scale = SEGMENT . intensity > > 2 ;
2019-10-07 23:22:56 +02:00
uint32_t counter = ( now * SEGMENT . speed ) > > 9 ;
2016-12-17 23:43:07 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2021-04-11 00:50:14 +02:00
uint16_t a = i * x_scale - counter ;
2019-02-05 21:53:39 +01:00
if ( saw ) {
2021-04-11 00:50:14 +02:00
a & = 0xFF ;
2019-02-05 21:53:39 +01:00
if ( a < 16 )
{
a = 192 + a * 8 ;
} else {
a = map ( a , 16 , 255 , 64 , 192 ) ;
}
2021-04-11 00:50:14 +02:00
a = 255 - a ;
2019-02-05 21:53:39 +01:00
}
2021-04-11 00:50:14 +02:00
uint8_t s = dual ? sin_gap ( a ) : sin8 ( a ) ;
uint32_t ca = color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) , s ) ;
if ( dual ) {
uint16_t b = ( SEGLEN - 1 - i ) * x_scale - counter ;
uint8_t t = sin_gap ( b ) ;
uint32_t cb = color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 2 ) , t ) ;
ca = color_blend ( ca , cb , 127 ) ;
}
setPixelColor ( i , ca ) ;
2016-12-17 23:43:07 +01:00
}
2019-10-07 23:22:56 +02:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
2020-02-18 21:28:46 +01:00
/*
* Running lights in opposite directions .
2021-04-11 00:50:14 +02:00
* Idea : Make the gap width controllable with a third slider in the future
2020-02-18 21:28:46 +01:00
*/
uint16_t WS2812FX : : mode_running_dual ( void ) {
return running_base ( false , true ) ;
}
2016-12-17 23:43:07 +01:00
/*
2019-02-05 21:53:39 +01:00
* Running lights effect with smooth sine transition .
2016-12-17 23:43:07 +01:00
*/
2019-02-05 21:53:39 +01:00
uint16_t WS2812FX : : mode_running_lights ( void ) {
return running_base ( false ) ;
2016-12-17 23:43:07 +01:00
}
2019-02-05 21:53:39 +01:00
2018-09-04 15:51:38 +02:00
/*
2019-02-05 21:53:39 +01:00
* Running lights effect with sawtooth transition .
2018-09-04 15:51:38 +02:00
*/
2019-02-05 21:53:39 +01:00
uint16_t WS2812FX : : mode_saw ( void ) {
return running_base ( true ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2019-02-05 21:53:39 +01:00
2016-12-17 23:43:07 +01:00
/*
* Blink several LEDs in random colors on , reset , repeat .
* Inspired by www . tweaking4all . com / hardware / arduino / adruino - led - strip - effects /
*/
2019-02-05 21:53:39 +01:00
uint16_t WS2812FX : : mode_twinkle ( void ) {
2019-12-23 19:28:37 +01:00
fill ( SEGCOLOR ( 1 ) ) ;
uint32_t cycleTime = 20 + ( 255 - SEGMENT . speed ) * 5 ;
uint32_t it = now / cycleTime ;
if ( it ! = SEGENV . step )
{
2020-03-30 12:56:51 +02:00
uint16_t maxOn = map ( SEGMENT . intensity , 0 , 255 , 1 , SEGLEN ) ; // make sure at least one LED is on
if ( SEGENV . aux0 > = maxOn )
2019-12-23 19:28:37 +01:00
{
SEGENV . aux0 = 0 ;
SEGENV . aux1 = random16 ( ) ; //new seed for our PRNG
}
SEGENV . aux0 + + ;
SEGENV . step = it ;
2019-02-05 21:53:39 +01:00
}
2019-12-23 19:28:37 +01:00
uint16_t PRNG16 = SEGENV . aux1 ;
2019-02-05 21:53:39 +01:00
2019-12-23 19:28:37 +01:00
for ( uint16_t i = 0 ; i < SEGENV . aux0 ; i + + )
{
PRNG16 = ( uint16_t ) ( PRNG16 * 2053 ) + 13849 ; // next 'random' number
uint32_t p = ( uint32_t ) SEGLEN * ( uint32_t ) PRNG16 ;
2020-01-14 10:57:23 +01:00
uint16_t j = p > > 16 ;
2019-12-23 19:28:37 +01:00
setPixelColor ( j , color_from_palette ( j , true , PALETTE_SOLID_WRAP , 0 ) ) ;
}
2019-02-05 21:53:39 +01:00
2019-12-23 19:28:37 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
2018-09-04 15:51:38 +02:00
/*
2019-01-31 23:42:48 +01:00
* Dissolve function
2018-09-04 15:51:38 +02:00
*/
2019-01-31 23:42:48 +01:00
uint16_t WS2812FX : : dissolve ( uint32_t color ) {
2019-05-22 00:23:09 +02:00
bool wa = ( SEGCOLOR ( 1 ) ! = 0 & & _brightness < 255 ) ; //workaround, can't compare getPixel to color if not full brightness
2019-01-31 23:42:48 +01:00
2019-05-22 00:23:09 +02:00
for ( uint16_t j = 0 ; j < = SEGLEN / 15 ; j + + )
2019-01-31 23:42:48 +01:00
{
if ( random8 ( ) < = SEGMENT . intensity ) {
for ( uint8_t times = 0 ; times < 10 ; times + + ) //attempt to spawn a new pixel 5 times
{
2020-01-14 10:57:23 +01:00
uint16_t i = random16 ( SEGLEN ) ;
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux0 ) { //dissolve to primary/palette
if ( getPixelColor ( i ) = = SEGCOLOR ( 1 ) | | wa ) {
if ( color = = SEGCOLOR ( 0 ) )
2019-01-31 23:42:48 +01:00
{
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
} else { setPixelColor ( i , color ) ; }
break ; //only spawn 1 new pixel per frame per 50 LEDs
}
} else { //dissolve to secondary
2019-05-22 00:23:09 +02:00
if ( getPixelColor ( i ) ! = SEGCOLOR ( 1 ) ) { setPixelColor ( i , SEGCOLOR ( 1 ) ) ; break ; }
2019-01-31 23:42:48 +01:00
}
}
2018-10-24 02:06:07 +02:00
}
2016-12-17 23:43:07 +01:00
}
2019-01-31 23:42:48 +01:00
2021-02-27 01:50:08 +01:00
if ( SEGENV . call > ( 255 - SEGMENT . speed ) + 15U )
2019-01-31 23:42:48 +01:00
{
2019-05-22 00:23:09 +02:00
SEGENV . aux0 = ! SEGENV . aux0 ;
SEGENV . call = 0 ;
2019-01-31 23:42:48 +01:00
}
2018-10-24 02:06:07 +02:00
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
2019-01-31 23:42:48 +01:00
* Blink several LEDs on and then off
2018-09-04 15:51:38 +02:00
*/
2019-01-31 23:42:48 +01:00
uint16_t WS2812FX : : mode_dissolve ( void ) {
2019-05-22 00:23:09 +02:00
return dissolve ( SEGCOLOR ( 0 ) ) ;
2016-12-17 23:43:07 +01:00
}
/*
2019-01-31 23:42:48 +01:00
* Blink several LEDs on and then off in random colors
2016-12-17 23:43:07 +01:00
*/
2019-01-31 23:42:48 +01:00
uint16_t WS2812FX : : mode_dissolve_random ( void ) {
return dissolve ( color_wheel ( random8 ( ) ) ) ;
2016-12-17 23:43:07 +01:00
}
/*
* Blinks one LED at a time .
* Inspired by www . tweaking4all . com / hardware / arduino / adruino - led - strip - effects /
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_sparkle ( void ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-12-06 01:44:45 +01:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) ) ;
}
uint32_t cycleTime = 10 + ( 255 - SEGMENT . speed ) * 2 ;
uint32_t it = now / cycleTime ;
if ( it ! = SEGENV . step )
{
SEGENV . aux0 = random16 ( SEGLEN ) ; // aux0 stores the random led index
SEGENV . step = it ;
2018-10-24 02:06:07 +02:00
}
2019-12-06 01:44:45 +01:00
2020-01-14 10:57:23 +01:00
setPixelColor ( SEGENV . aux0 , SEGCOLOR ( 0 ) ) ;
2019-12-06 01:44:45 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2020-12-31 16:16:33 +01:00
* Lights all LEDs in the color . Flashes single col 1 pixels randomly . ( List name : Sparkle Dark )
2016-12-17 23:43:07 +01:00
* Inspired by www . tweaking4all . com / hardware / arduino / adruino - led - strip - effects /
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_flash_sparkle ( void ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-12-06 01:44:45 +01:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2016-12-17 23:43:07 +01:00
}
2020-12-31 16:16:33 +01:00
if ( now - SEGENV . aux0 > SEGENV . step ) {
if ( random8 ( ( 255 - SEGMENT . intensity ) > > 4 ) = = 0 ) {
setPixelColor ( random16 ( SEGLEN ) , SEGCOLOR ( 1 ) ) ; //flash
}
SEGENV . step = now ;
SEGENV . aux0 = 255 - SEGMENT . speed ;
}
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
* Like flash sparkle . With more flash .
* Inspired by www . tweaking4all . com / hardware / arduino / adruino - led - strip - effects /
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_hyper_sparkle ( void ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-10-24 02:06:07 +02:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2016-12-17 23:43:07 +01:00
}
2020-12-31 16:16:33 +01:00
if ( now - SEGENV . aux0 > SEGENV . step ) {
if ( random8 ( ( 255 - SEGMENT . intensity ) > > 4 ) = = 0 ) {
for ( uint16_t i = 0 ; i < MAX ( 1 , SEGLEN / 3 ) ; i + + ) {
setPixelColor ( random16 ( SEGLEN ) , SEGCOLOR ( 1 ) ) ;
}
2016-12-17 23:43:07 +01:00
}
2020-12-31 16:16:33 +01:00
SEGENV . step = now ;
SEGENV . aux0 = 255 - SEGMENT . speed ;
2016-12-17 23:43:07 +01:00
}
2020-12-31 16:16:33 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Strobe effect with different strobe count and pause , controlled by speed .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_multi_strobe ( void ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-10-24 02:06:07 +02:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) ) ;
2016-12-17 23:43:07 +01:00
}
2020-12-31 16:16:33 +01:00
SEGENV . aux0 = 50 + 20 * ( uint16_t ) ( 255 - SEGMENT . speed ) ;
uint16_t count = 2 * ( ( SEGMENT . intensity / 10 ) + 1 ) ;
if ( SEGENV . aux1 < count ) {
if ( ( SEGENV . aux1 & 1 ) = = 0 ) {
fill ( SEGCOLOR ( 0 ) ) ;
SEGENV . aux0 = 15 ;
2016-12-17 23:43:07 +01:00
} else {
2020-12-31 16:16:33 +01:00
SEGENV . aux0 = 50 ;
2017-11-28 15:09:58 +01:00
}
2016-12-17 23:43:07 +01:00
}
2020-12-31 16:16:33 +01:00
if ( now - SEGENV . aux0 > SEGENV . step ) {
SEGENV . aux1 + + ;
if ( SEGENV . aux1 > count ) SEGENV . aux1 = 0 ;
SEGENV . step = now ;
}
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-03-18 23:16:53 +01:00
* Android loading circle
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_android ( void ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-10-24 02:06:07 +02:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux1 > ( ( float ) SEGMENT . intensity / 255.0 ) * ( float ) SEGLEN )
2018-03-18 23:16:53 +01:00
{
2019-05-22 00:23:09 +02:00
SEGENV . aux0 = 1 ;
2018-03-18 23:16:53 +01:00
} else
{
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux1 < 2 ) SEGENV . aux0 = 0 ;
2018-03-18 23:16:53 +01:00
}
2018-09-04 15:51:38 +02:00
2019-05-22 00:23:09 +02:00
uint16_t a = SEGENV . step ;
2018-03-18 23:16:53 +01:00
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux0 = = 0 )
2018-03-18 23:16:53 +01:00
{
2019-05-22 00:23:09 +02:00
if ( SEGENV . call % 3 = = 1 ) { a + + ; }
else { SEGENV . aux1 + + ; }
2018-03-18 23:16:53 +01:00
} else
{
a + + ;
2019-05-22 00:23:09 +02:00
if ( SEGENV . call % 3 ! = 1 ) SEGENV . aux1 - - ;
2018-03-18 23:16:53 +01:00
}
2020-01-14 10:57:23 +01:00
if ( a > = SEGLEN ) a = 0 ;
2016-12-17 23:43:07 +01:00
2020-01-14 10:57:23 +01:00
if ( a + SEGENV . aux1 < SEGLEN )
2018-03-18 23:16:53 +01:00
{
2019-05-22 00:23:09 +02:00
for ( int i = a ; i < a + SEGENV . aux1 ; i + + ) {
setPixelColor ( i , SEGCOLOR ( 0 ) ) ;
2018-03-18 23:16:53 +01:00
}
} else
{
2020-01-14 10:57:23 +01:00
for ( int i = a ; i < SEGLEN ; i + + ) {
2019-05-22 00:23:09 +02:00
setPixelColor ( i , SEGCOLOR ( 0 ) ) ;
2018-03-18 23:16:53 +01:00
}
2020-01-14 10:57:23 +01:00
for ( int i = 0 ; i < SEGENV . aux1 - ( SEGLEN - a ) ; i + + ) {
2019-05-22 00:23:09 +02:00
setPixelColor ( i , SEGCOLOR ( 0 ) ) ;
2018-03-18 23:16:53 +01:00
}
}
2019-05-22 00:23:09 +02:00
SEGENV . step = a ;
2016-12-17 23:43:07 +01:00
2019-05-22 00:23:09 +02:00
return 3 + ( ( 8 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) / SEGLEN ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
/*
2018-09-04 15:51:38 +02:00
* color chase function .
* color1 = background color
* color2 and color3 = colors of two adjacent leds
2016-12-17 23:43:07 +01:00
*/
2020-02-29 18:24:51 +01:00
uint16_t WS2812FX : : chase ( uint32_t color1 , uint32_t color2 , uint32_t color3 , bool do_palette ) {
2020-02-09 10:33:26 +01:00
uint16_t counter = now * ( ( SEGMENT . speed > > 2 ) + 1 ) ;
2019-12-23 18:38:54 +01:00
uint16_t a = counter * SEGLEN > > 16 ;
2020-02-29 18:24:51 +01:00
bool chase_random = ( SEGMENT . mode = = FX_MODE_CHASE_RANDOM ) ;
if ( chase_random ) {
if ( a < SEGENV . step ) //we hit the start again, choose new color for Chase random
{
SEGENV . aux1 = SEGENV . aux0 ; //store previous random color
SEGENV . aux0 = get_random_wheel_index ( SEGENV . aux0 ) ;
}
color1 = color_wheel ( SEGENV . aux0 ) ;
}
2020-02-06 01:37:47 +01:00
SEGENV . step = a ;
2020-02-29 18:24:51 +01:00
2019-12-23 18:38:54 +01:00
// Use intensity setting to vary chase up to 1/2 string length
2020-02-29 18:24:51 +01:00
uint8_t size = 1 + ( SEGMENT . intensity * SEGLEN > > 10 ) ;
2018-10-24 02:06:07 +02:00
2020-02-29 18:24:51 +01:00
uint16_t b = a + size ; //"trail" of chase, filled with color1
if ( b > SEGLEN ) b - = SEGLEN ;
uint16_t c = b + size ;
if ( c > SEGLEN ) c - = SEGLEN ;
2018-10-24 02:06:07 +02:00
2020-02-29 18:24:51 +01:00
//background
if ( do_palette )
{
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) ) ;
2020-02-09 10:33:26 +01:00
}
2020-02-29 18:24:51 +01:00
} else fill ( color1 ) ;
//if random, fill old background between a and end
if ( chase_random )
{
color1 = color_wheel ( SEGENV . aux1 ) ;
for ( uint16_t i = a ; i < SEGLEN ; i + + )
setPixelColor ( i , color1 ) ;
2020-02-09 10:33:26 +01:00
}
2016-12-17 23:43:07 +01:00
2020-02-29 18:24:51 +01:00
//fill between points a and b with color2
if ( a < b )
{
for ( uint16_t i = a ; i < b ; i + + )
setPixelColor ( i , color2 ) ;
} else {
for ( uint16_t i = a ; i < SEGLEN ; i + + ) //fill until end
setPixelColor ( i , color2 ) ;
for ( uint16_t i = 0 ; i < b ; i + + ) //fill from start until b
setPixelColor ( i , color2 ) ;
}
//fill between points b and c with color2
if ( b < c )
{
for ( uint16_t i = b ; i < c ; i + + )
setPixelColor ( i , color3 ) ;
} else {
for ( uint16_t i = b ; i < SEGLEN ; i + + ) //fill until end
setPixelColor ( i , color3 ) ;
for ( uint16_t i = 0 ; i < c ; i + + ) //fill from start until c
setPixelColor ( i , color3 ) ;
2020-02-06 01:37:47 +01:00
}
2019-12-23 18:38:54 +01:00
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
* Bicolor chase , more primary color .
*/
uint16_t WS2812FX : : mode_chase_color ( void ) {
2020-02-29 18:24:51 +01:00
return chase ( SEGCOLOR ( 1 ) , ( SEGCOLOR ( 2 ) ) ? SEGCOLOR ( 2 ) : SEGCOLOR ( 0 ) , SEGCOLOR ( 0 ) , true ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-10-25 20:55:29 +02:00
2016-12-17 23:43:07 +01:00
/*
2018-09-04 15:51:38 +02:00
* Primary running followed by random color .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_chase_random ( void ) {
2020-02-29 18:24:51 +01:00
return chase ( SEGCOLOR ( 1 ) , ( SEGCOLOR ( 2 ) ) ? SEGCOLOR ( 2 ) : SEGCOLOR ( 0 ) , SEGCOLOR ( 0 ) , false ) ;
}
/*
* Primary , secondary running on rainbow .
*/
uint16_t WS2812FX : : mode_chase_rainbow ( void ) {
uint8_t color_sep = 256 / SEGLEN ;
2020-11-23 00:06:57 +01:00
if ( color_sep = = 0 ) color_sep = 1 ; // correction for segments longer than 256 LEDs
2020-02-29 18:24:51 +01:00
uint8_t color_index = SEGENV . call & 0xFF ;
uint32_t color = color_wheel ( ( ( SEGENV . step * color_sep ) + color_index ) & 0xFF ) ;
return chase ( color , SEGCOLOR ( 0 ) , SEGCOLOR ( 1 ) , false ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
* Primary running on rainbow .
*/
uint16_t WS2812FX : : mode_chase_rainbow_white ( void ) {
2019-05-22 00:23:09 +02:00
uint16_t n = SEGENV . step ;
uint16_t m = ( SEGENV . step + 1 ) % SEGLEN ;
uint32_t color2 = color_wheel ( ( ( n * 256 / SEGLEN ) + ( SEGENV . call & 0xFF ) ) & 0xFF ) ;
uint32_t color3 = color_wheel ( ( ( m * 256 / SEGLEN ) + ( SEGENV . call & 0xFF ) ) & 0xFF ) ;
2018-09-04 15:51:38 +02:00
2019-05-22 00:23:09 +02:00
return chase ( SEGCOLOR ( 0 ) , color2 , color3 , false ) ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
2018-09-04 15:51:38 +02:00
/*
* Red - Amber - Green - Blue lights running
*/
uint16_t WS2812FX : : mode_colorful ( void ) {
2020-10-29 01:23:04 +01:00
uint8_t numColors = 4 ; //3, 4, or 5
uint32_t cols [ 9 ] { 0x00FF0000 , 0x00EEBB00 , 0x0000EE00 , 0x000077CC } ;
if ( SEGMENT . intensity > 160 | | SEGMENT . palette ) { //palette or color
if ( ! SEGMENT . palette ) {
numColors = 3 ;
for ( uint8_t i = 0 ; i < 3 ; i + + ) cols [ i ] = SEGCOLOR ( i ) ;
} else {
uint16_t fac = 80 ;
if ( SEGMENT . palette = = 52 ) { numColors = 5 ; fac = 61 ; } //C9 2 has 5 colors
for ( uint8_t i = 0 ; i < numColors ; i + + ) {
cols [ i ] = color_from_palette ( i * fac , false , true , 255 ) ;
}
}
} else if ( SEGMENT . intensity < 80 ) //pastel (easter) colors
2018-09-04 15:51:38 +02:00
{
cols [ 0 ] = 0x00FF8040 ;
cols [ 1 ] = 0x00E5D241 ;
cols [ 2 ] = 0x0077FF77 ;
cols [ 3 ] = 0x0077F0F0 ;
}
2020-10-29 01:23:04 +01:00
for ( uint8_t i = numColors ; i < numColors * 2 - 1 ; i + + ) cols [ i ] = cols [ i - numColors ] ;
2019-12-21 03:17:54 +01:00
2020-10-29 01:23:04 +01:00
uint32_t cycleTime = 50 + ( 8 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ;
2019-12-21 03:17:54 +01:00
uint32_t it = now / cycleTime ;
if ( it ! = SEGENV . step )
{
if ( SEGMENT . speed > 0 ) SEGENV . aux0 + + ;
2020-10-29 01:23:04 +01:00
if ( SEGENV . aux0 > = numColors ) SEGENV . aux0 = 0 ;
2019-12-21 03:17:54 +01:00
SEGENV . step = it ;
}
2020-10-29 01:23:04 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + = numColors )
2018-09-04 15:51:38 +02:00
{
2020-10-29 01:23:04 +01:00
for ( uint16_t j = 0 ; j < numColors ; j + + ) setPixelColor ( i + j , cols [ SEGENV . aux0 + j ] ) ;
2018-09-04 15:51:38 +02:00
}
2019-12-21 03:17:54 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Emulates a traffic light .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_traffic_light ( void ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2018-10-24 02:06:07 +02:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) ) ;
2018-09-04 15:51:38 +02:00
uint32_t mdelay = 500 ;
2020-01-14 10:57:23 +01:00
for ( int i = 0 ; i < SEGLEN - 2 ; i + = 3 )
2018-09-04 15:51:38 +02:00
{
2019-12-21 03:17:54 +01:00
switch ( SEGENV . aux0 )
2018-09-04 15:51:38 +02:00
{
case 0 : setPixelColor ( i , 0x00FF0000 ) ; mdelay = 150 + ( 100 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ; break ;
case 1 : setPixelColor ( i , 0x00FF0000 ) ; mdelay = 150 + ( 20 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ; setPixelColor ( i + 1 , 0x00EECC00 ) ; break ;
case 2 : setPixelColor ( i + 2 , 0x0000FF00 ) ; mdelay = 150 + ( 100 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ; break ;
case 3 : setPixelColor ( i + 1 , 0x00EECC00 ) ; mdelay = 150 + ( 20 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ; break ;
}
2016-12-17 23:43:07 +01:00
}
2019-12-21 03:17:54 +01:00
if ( now - SEGENV . step > mdelay )
{
SEGENV . aux0 + + ;
2020-06-06 00:57:34 +02:00
if ( SEGENV . aux0 = = 1 & & SEGMENT . intensity > 140 ) SEGENV . aux0 = 2 ; //skip Red + Amber, to get US-style sequence
2019-12-21 03:17:54 +01:00
if ( SEGENV . aux0 > 3 ) SEGENV . aux0 = 0 ;
SEGENV . step = now ;
}
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2016-12-17 23:43:07 +01:00
/*
2018-09-04 15:51:38 +02:00
* Sec flashes running on prim .
2016-12-17 23:43:07 +01:00
*/
2019-03-11 19:30:49 +01:00
# define FLASH_COUNT 4
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_chase_flash ( void ) {
2019-05-22 00:23:09 +02:00
uint8_t flash_step = SEGENV . call % ( ( FLASH_COUNT * 2 ) + 1 ) ;
2016-12-17 23:43:07 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-10-24 02:06:07 +02:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-05-22 00:23:09 +02:00
uint16_t delay = 10 + ( ( 30 * ( uint16_t ) ( 255 - SEGMENT . speed ) ) / SEGLEN ) ;
2019-03-11 19:30:49 +01:00
if ( flash_step < ( FLASH_COUNT * 2 ) ) {
2016-12-17 23:43:07 +01:00
if ( flash_step % 2 = = 0 ) {
2019-05-22 00:23:09 +02:00
uint16_t n = SEGENV . step ;
uint16_t m = ( SEGENV . step + 1 ) % SEGLEN ;
2020-01-14 10:57:23 +01:00
setPixelColor ( n , SEGCOLOR ( 1 ) ) ;
setPixelColor ( m , SEGCOLOR ( 1 ) ) ;
2018-09-04 15:51:38 +02:00
delay = 20 ;
2016-12-17 23:43:07 +01:00
} else {
2018-09-04 15:51:38 +02:00
delay = 30 ;
2016-12-17 23:43:07 +01:00
}
} else {
2019-05-22 00:23:09 +02:00
SEGENV . step = ( SEGENV . step + 1 ) % SEGLEN ;
2016-12-17 23:43:07 +01:00
}
2018-09-04 15:51:38 +02:00
return delay ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Prim flashes running , followed by random color .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_chase_flash_random ( void ) {
2019-05-22 00:23:09 +02:00
uint8_t flash_step = SEGENV . call % ( ( FLASH_COUNT * 2 ) + 1 ) ;
2016-12-17 23:43:07 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGENV . step ; i + + ) {
setPixelColor ( i , color_wheel ( SEGENV . aux0 ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-05-22 00:23:09 +02:00
uint16_t delay = 1 + ( ( 10 * ( uint16_t ) ( 255 - SEGMENT . speed ) ) / SEGLEN ) ;
2019-03-11 19:30:49 +01:00
if ( flash_step < ( FLASH_COUNT * 2 ) ) {
2019-05-22 00:23:09 +02:00
uint16_t n = SEGENV . step ;
uint16_t m = ( SEGENV . step + 1 ) % SEGLEN ;
2016-12-17 23:43:07 +01:00
if ( flash_step % 2 = = 0 ) {
2020-01-14 10:57:23 +01:00
setPixelColor ( n , SEGCOLOR ( 0 ) ) ;
setPixelColor ( m , SEGCOLOR ( 0 ) ) ;
2018-09-04 15:51:38 +02:00
delay = 20 ;
2016-12-17 23:43:07 +01:00
} else {
2020-01-14 10:57:23 +01:00
setPixelColor ( n , color_wheel ( SEGENV . aux0 ) ) ;
setPixelColor ( m , SEGCOLOR ( 1 ) ) ;
2018-09-04 15:51:38 +02:00
delay = 30 ;
2016-12-17 23:43:07 +01:00
}
} else {
2019-05-22 00:23:09 +02:00
SEGENV . step = ( SEGENV . step + 1 ) % SEGLEN ;
2016-12-17 23:43:07 +01:00
2019-05-22 00:23:09 +02:00
if ( SEGENV . step = = 0 ) {
SEGENV . aux0 = get_random_wheel_index ( SEGENV . aux0 ) ;
2016-12-17 23:43:07 +01:00
}
}
2018-09-04 15:51:38 +02:00
return delay ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Alternating pixels running function .
2016-12-17 23:43:07 +01:00
*/
2021-02-24 20:23:32 +01:00
uint16_t WS2812FX : : running ( uint32_t color1 , uint32_t color2 , bool theatre ) {
uint8_t width = ( theatre ? 3 : 1 ) + ( SEGMENT . intensity > > 4 ) ; // window
uint32_t cycleTime = 50 + ( 255 - SEGMENT . speed ) ;
2019-10-07 23:22:56 +02:00
uint32_t it = now / cycleTime ;
2021-02-24 20:23:32 +01:00
bool usePalette = color1 = = SEGCOLOR ( 0 ) ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2021-02-24 20:23:32 +01:00
uint32_t col = color2 ;
if ( usePalette ) color1 = color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ;
if ( theatre ) {
if ( ( i % width ) = = SEGENV . aux0 ) col = color1 ;
2018-09-04 15:51:38 +02:00
} else {
2021-02-24 20:23:32 +01:00
int8_t pos = ( i % ( width < < 1 ) ) ;
if ( ( pos < SEGENV . aux0 - width ) | | ( ( pos > = SEGENV . aux0 ) & & ( pos < SEGENV . aux0 + width ) ) ) col = color1 ;
2018-09-04 15:51:38 +02:00
}
2021-02-24 20:23:32 +01:00
setPixelColor ( i , col ) ;
2016-12-17 23:43:07 +01:00
}
2019-10-07 23:22:56 +02:00
if ( it ! = SEGENV . step )
{
2021-02-24 20:23:32 +01:00
SEGENV . aux0 = ( SEGENV . aux0 + 1 ) % ( theatre ? width : ( width < < 1 ) ) ;
2019-10-07 23:22:56 +02:00
SEGENV . step = it ;
}
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-10-24 02:06:07 +02:00
* Alternating color / sec pixels running .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_running_color ( void ) {
2019-05-22 00:23:09 +02:00
return running ( SEGCOLOR ( 0 ) , SEGCOLOR ( 1 ) ) ;
2018-04-01 00:08:50 +02:00
}
2020-12-07 08:33:08 +01:00
/*
* Alternating red / white pixels running .
*/
uint16_t WS2812FX : : mode_candy_cane ( void ) {
return running ( RED , WHITE ) ;
}
2019-02-10 23:05:06 +01:00
2016-12-17 23:43:07 +01:00
/*
2018-09-04 15:51:38 +02:00
* Alternating orange / purple pixels running .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_halloween ( void ) {
return running ( PURPLE , ORANGE ) ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Random colored pixels running .
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_running_random ( void ) {
2020-01-14 10:57:23 +01:00
uint32_t cycleTime = 25 + ( 3 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ;
uint32_t it = now / cycleTime ;
if ( SEGENV . aux1 = = it ) return FRAMETIME ;
2019-05-22 00:23:09 +02:00
for ( uint16_t i = SEGLEN - 1 ; i > 0 ; i - - ) {
2020-01-14 10:57:23 +01:00
setPixelColor ( i , getPixelColor ( i - 1 ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-05-22 00:23:09 +02:00
if ( SEGENV . step = = 0 ) {
SEGENV . aux0 = get_random_wheel_index ( SEGENV . aux0 ) ;
2020-01-14 10:57:23 +01:00
setPixelColor ( 0 , color_wheel ( SEGENV . aux0 ) ) ;
2016-12-17 23:43:07 +01:00
}
2019-05-22 00:23:09 +02:00
SEGENV . step + + ;
2021-02-27 01:50:08 +01:00
if ( SEGENV . step > ( uint8_t ) ( ( 255 - SEGMENT . intensity ) > > 4 ) )
2019-02-10 23:05:06 +01:00
{
2019-05-22 00:23:09 +02:00
SEGENV . step = 0 ;
2019-02-10 23:05:06 +01:00
}
2020-01-14 10:57:23 +01:00
SEGENV . aux1 = it ;
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* K . I . T . T .
2016-12-17 23:43:07 +01:00
*/
2019-12-23 18:38:54 +01:00
uint16_t WS2812FX : : mode_larson_scanner ( void ) {
return larson_scanner ( false ) ;
}
uint16_t WS2812FX : : larson_scanner ( bool dual ) {
uint16_t counter = now * ( ( SEGMENT . speed > > 2 ) + 8 ) ;
uint16_t index = counter * SEGLEN > > 16 ;
2019-12-22 17:41:32 +01:00
2019-02-11 23:49:04 +01:00
fade_out ( SEGMENT . intensity ) ;
2016-12-17 23:43:07 +01:00
2019-12-23 18:38:54 +01:00
if ( SEGENV . step > index & & SEGENV . step - index > SEGLEN / 2 ) {
SEGENV . aux0 = ! SEGENV . aux0 ;
2016-12-17 23:43:07 +01:00
}
2019-12-23 18:38:54 +01:00
for ( uint16_t i = SEGENV . step ; i < index ; i + + ) {
uint16_t j = ( SEGENV . aux0 ) ? i : SEGLEN - 1 - i ;
2020-01-14 10:57:23 +01:00
setPixelColor ( j , color_from_palette ( j , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2019-12-23 18:38:54 +01:00
}
if ( dual ) {
uint32_t c ;
if ( SEGCOLOR ( 2 ) ! = 0 ) {
c = SEGCOLOR ( 2 ) ;
} else {
c = color_from_palette ( index , true , PALETTE_SOLID_WRAP , 0 ) ;
}
2016-12-17 23:43:07 +01:00
2019-12-23 18:38:54 +01:00
for ( uint16_t i = SEGENV . step ; i < index ; i + + ) {
uint16_t j = ( SEGENV . aux0 ) ? SEGLEN - 1 - i : i ;
2020-01-14 10:57:23 +01:00
setPixelColor ( j , c ) ;
2019-12-23 18:38:54 +01:00
}
}
SEGENV . step = index ;
2019-12-22 17:41:32 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2020-01-28 13:48:59 +01:00
* Firing comets from one end . " Lighthouse "
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_comet ( void ) {
2020-01-28 13:48:59 +01:00
uint16_t counter = now * ( ( SEGMENT . speed > > 2 ) + 1 ) ;
2019-12-22 17:41:32 +01:00
uint16_t index = counter * SEGLEN > > 16 ;
2020-01-28 13:48:59 +01:00
if ( SEGENV . call = = 0 ) SEGENV . aux0 = index ;
2019-12-22 17:41:32 +01:00
2019-02-11 23:49:04 +01:00
fade_out ( SEGMENT . intensity ) ;
2016-12-17 23:43:07 +01:00
2020-01-14 10:57:23 +01:00
setPixelColor ( index , color_from_palette ( index , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2020-01-28 13:48:59 +01:00
if ( index > SEGENV . aux0 ) {
for ( uint16_t i = SEGENV . aux0 ; i < index ; i + + ) {
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
}
2020-01-29 23:20:32 +01:00
} else if ( index < SEGENV . aux0 & & index < 10 ) {
for ( uint16_t i = 0 ; i < index ; i + + ) {
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
}
2020-01-28 13:48:59 +01:00
}
SEGENV . aux0 = index + + ;
2016-12-17 23:43:07 +01:00
2019-12-22 17:41:32 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Fireworks function .
2016-12-17 23:43:07 +01:00
*/
2019-02-11 23:49:04 +01:00
uint16_t WS2812FX : : mode_fireworks ( ) {
fade_out ( 0 ) ;
2019-05-22 00:23:09 +02:00
if ( SEGENV . call = = 0 ) {
SEGENV . aux0 = UINT16_MAX ;
SEGENV . aux1 = UINT16_MAX ;
2019-02-11 23:49:04 +01:00
}
2020-01-14 11:46:21 +01:00
bool valid1 = ( SEGENV . aux0 < SEGLEN ) ;
bool valid2 = ( SEGENV . aux1 < SEGLEN ) ;
2019-02-11 23:49:04 +01:00
uint32_t sv1 = 0 , sv2 = 0 ;
2019-05-22 00:23:09 +02:00
if ( valid1 ) sv1 = getPixelColor ( SEGENV . aux0 ) ;
if ( valid2 ) sv2 = getPixelColor ( SEGENV . aux1 ) ;
2019-02-11 23:49:04 +01:00
blur ( 255 - SEGMENT . speed ) ;
2019-05-22 00:23:09 +02:00
if ( valid1 ) setPixelColor ( SEGENV . aux0 , sv1 ) ;
if ( valid2 ) setPixelColor ( SEGENV . aux1 , sv2 ) ;
2016-12-17 23:43:07 +01:00
2020-03-26 10:18:19 +01:00
for ( uint16_t i = 0 ; i < MAX ( 1 , SEGLEN / 20 ) ; i + + ) {
2019-02-11 23:49:04 +01:00
if ( random8 ( 129 - ( SEGMENT . intensity > > 1 ) ) = = 0 ) {
2020-01-14 10:57:23 +01:00
uint16_t index = random ( SEGLEN ) ;
2019-02-11 23:49:04 +01:00
setPixelColor ( index , color_from_palette ( random8 ( ) , false , false , 0 ) ) ;
2019-05-22 00:23:09 +02:00
SEGENV . aux1 = SEGENV . aux0 ;
SEGENV . aux0 = index ;
2018-09-04 15:51:38 +02:00
}
}
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
2019-02-11 23:49:04 +01:00
//Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h
uint16_t WS2812FX : : mode_rain ( )
{
2019-12-23 18:48:01 +01:00
SEGENV . step + = FRAMETIME ;
2019-05-22 00:23:09 +02:00
if ( SEGENV . step > SPEED_FORMULA_L ) {
SEGENV . step = 0 ;
2020-11-04 23:20:34 +01:00
//shift all leds left
uint32_t ctemp = getPixelColor ( 0 ) ;
for ( uint16_t i = 0 ; i < SEGLEN - 1 ; i + + ) {
setPixelColor ( i , getPixelColor ( i + 1 ) ) ;
2019-02-11 23:49:04 +01:00
}
2020-11-04 23:20:34 +01:00
setPixelColor ( SEGLEN - 1 , ctemp ) ;
2019-05-22 00:23:09 +02:00
SEGENV . aux0 + + ;
SEGENV . aux1 + + ;
if ( SEGENV . aux0 = = 0 ) SEGENV . aux0 = UINT16_MAX ;
if ( SEGENV . aux1 = = 0 ) SEGENV . aux0 = UINT16_MAX ;
2020-01-14 10:57:23 +01:00
if ( SEGENV . aux0 = = SEGLEN ) SEGENV . aux0 = 0 ;
if ( SEGENV . aux1 = = SEGLEN ) SEGENV . aux1 = 0 ;
2019-02-11 23:49:04 +01:00
}
return mode_fireworks ( ) ;
2016-12-17 23:43:07 +01:00
}
/*
2018-09-04 15:51:38 +02:00
* Fire flicker function
2016-12-17 23:43:07 +01:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_fire_flicker ( void ) {
2020-01-14 10:57:23 +01:00
uint32_t cycleTime = 40 + ( 255 - SEGMENT . speed ) ;
uint32_t it = now / cycleTime ;
if ( SEGENV . step = = it ) return FRAMETIME ;
2019-05-22 00:23:09 +02:00
byte w = ( SEGCOLOR ( 0 ) > > 24 ) & 0xFF ;
byte r = ( SEGCOLOR ( 0 ) > > 16 ) & 0xFF ;
byte g = ( SEGCOLOR ( 0 ) > > 8 ) & 0xFF ;
byte b = ( SEGCOLOR ( 0 ) & 0xFF ) ;
2020-03-26 10:18:19 +01:00
byte lum = ( SEGMENT . palette = = 0 ) ? MAX ( w , MAX ( r , MAX ( g , b ) ) ) : 255 ;
2018-10-24 02:06:07 +02:00
lum / = ( ( ( 256 - SEGMENT . intensity ) / 16 ) + 1 ) ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-10-24 02:06:07 +02:00
byte flicker = random8 ( lum ) ;
if ( SEGMENT . palette = = 0 ) {
2020-03-26 10:18:19 +01:00
setPixelColor ( i , MAX ( r - flicker , 0 ) , MAX ( g - flicker , 0 ) , MAX ( b - flicker , 0 ) , MAX ( w - flicker , 0 ) ) ;
2018-10-24 02:06:07 +02:00
} else {
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 , 255 - flicker ) ) ;
}
2016-12-17 23:43:07 +01:00
}
2020-01-14 10:57:23 +01:00
SEGENV . step = it ;
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
2019-02-05 19:40:24 +01:00
/*
* Gradient run base function
*/
uint16_t WS2812FX : : gradient_base ( bool loading ) {
2020-02-11 22:43:15 +01:00
uint16_t counter = now * ( ( SEGMENT . speed > > 2 ) + 1 ) ;
uint16_t pp = counter * SEGLEN > > 16 ;
if ( SEGENV . call = = 0 ) pp = 0 ;
float val ; //0.0 = sec 1.0 = pri
float brd = loading ? SEGMENT . intensity : SEGMENT . intensity / 2 ;
2019-02-05 19:40:24 +01:00
if ( brd < 1.0 ) brd = 1.0 ;
2019-05-22 00:23:09 +02:00
int p1 = pp - SEGLEN ;
int p2 = pp + SEGLEN ;
2019-12-22 17:41:32 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2019-02-05 19:40:24 +01:00
{
if ( loading )
{
val = abs ( ( ( i > pp ) ? p2 : pp ) - i ) ;
} else {
2020-03-26 10:18:19 +01:00
val = MIN ( abs ( pp - i ) , MIN ( abs ( p1 - i ) , abs ( p2 - i ) ) ) ;
2019-02-05 19:40:24 +01:00
}
2020-02-11 22:43:15 +01:00
val = ( brd > val ) ? val / brd * 255 : 255 ;
setPixelColor ( i , color_blend ( SEGCOLOR ( 0 ) , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) , val ) ) ;
2019-02-05 19:40:24 +01:00
}
2019-12-22 17:41:32 +01:00
2019-12-23 18:38:54 +01:00
return FRAMETIME ;
2019-02-05 19:40:24 +01:00
}
2018-03-18 23:16:53 +01:00
/*
* Gradient run
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_gradient ( void ) {
2019-02-05 19:40:24 +01:00
return gradient_base ( false ) ;
2018-03-18 23:16:53 +01:00
}
2016-12-31 17:36:07 +01:00
2018-09-04 15:51:38 +02:00
2018-03-18 23:16:53 +01:00
/*
* Gradient run with hard transition
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_loading ( void ) {
2019-02-05 19:40:24 +01:00
return gradient_base ( true ) ;
2018-09-04 15:51:38 +02:00
}
2019-12-04 12:15:12 +01:00
//American Police Light with all LEDs Red and Blue
2021-09-07 22:59:31 +02:00
uint16_t WS2812FX : : police_base ( uint32_t color1 , uint32_t color2 , uint16_t width )
2019-12-04 12:15:12 +01:00
{
2021-09-08 23:26:41 +02:00
uint16_t delay = 1 + ( FRAMETIME < < 3 ) / SEGLEN ; // longer segments should change faster
uint32_t it = now / map ( SEGMENT . speed , 0 , 255 , delay < < 4 , delay ) ;
2021-09-07 22:59:31 +02:00
uint16_t offset = it % SEGLEN ;
2020-01-28 13:48:59 +01:00
2021-09-08 23:26:41 +02:00
if ( ! width ) width = 1 ;
2021-09-07 22:59:31 +02:00
for ( uint16_t i = 0 ; i < width ; i + + ) {
uint16_t indexR = ( offset + i ) % SEGLEN ;
uint16_t indexB = ( offset + i + ( SEGLEN > > 1 ) ) % SEGLEN ;
setPixelColor ( indexR , color1 ) ;
setPixelColor ( indexB , color2 ) ;
}
2019-12-04 12:15:12 +01:00
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2019-12-04 12:15:12 +01:00
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX : : mode_police_all ( )
{
2021-09-07 22:59:31 +02:00
return police_base ( RED , BLUE , ( SEGLEN > > 1 ) ) ;
2018-09-04 15:51:38 +02:00
}
2019-12-04 12:15:12 +01:00
//Police Lights Red and Blue
uint16_t WS2812FX : : mode_police ( )
{
2021-09-08 07:54:40 +02:00
fill ( SEGCOLOR ( 1 ) ) ;
2021-09-08 23:26:41 +02:00
return police_base ( RED , BLUE , ( ( SEGLEN * ( SEGMENT . intensity + 1 ) ) > > 9 ) ) ; // max width is half the strip
2019-12-04 12:15:12 +01:00
}
//Police All with custom colors
uint16_t WS2812FX : : mode_two_areas ( )
{
2021-09-08 07:54:40 +02:00
fill ( SEGCOLOR ( 2 ) ) ;
2021-09-08 23:26:41 +02:00
return police_base ( SEGCOLOR ( 0 ) , SEGCOLOR ( 1 ) , ( ( SEGLEN * ( SEGMENT . intensity + 1 ) ) > > 9 ) ) ; // max width is half the strip
2019-12-04 12:15:12 +01:00
}
//Police Lights with custom colors
uint16_t WS2812FX : : mode_two_dots ( )
{
fill ( SEGCOLOR ( 2 ) ) ;
uint32_t color2 = ( SEGCOLOR ( 1 ) = = SEGCOLOR ( 2 ) ) ? SEGCOLOR ( 0 ) : SEGCOLOR ( 1 ) ;
2021-09-08 23:26:41 +02:00
return police_base ( SEGCOLOR ( 0 ) , color2 , ( ( SEGLEN * ( SEGMENT . intensity + 1 ) ) > > 9 ) ) ; // max width is half the strip
2018-09-04 15:51:38 +02:00
}
2018-10-25 20:55:29 +02:00
/*
* Tricolor chase function
*/
2019-02-11 23:49:04 +01:00
uint16_t WS2812FX : : tricolor_chase ( uint32_t color1 , uint32_t color2 ) {
2021-09-07 22:59:31 +02:00
uint32_t cycleTime = 50 + ( ( 255 - SEGMENT . speed ) < < 1 ) ;
uint32_t it = now / cycleTime ; // iterator
uint8_t width = ( 1 + ( SEGMENT . intensity > > 4 ) ) ; // value of 1-16 for each colour
uint8_t index = it % ( width * 3 ) ;
2019-12-23 18:38:54 +01:00
2021-09-07 22:59:31 +02:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + , index + + ) {
if ( index > ( width * 3 ) - 1 ) index = 0 ;
2018-10-25 20:55:29 +02:00
uint32_t color = color1 ;
2021-09-07 22:59:31 +02:00
if ( index > ( width < < 1 ) - 1 ) color = color_from_palette ( i , true , PALETTE_SOLID_WRAP , 1 ) ;
else if ( index > width - 1 ) color = color2 ;
2018-10-25 20:55:29 +02:00
2020-01-14 10:57:23 +01:00
setPixelColor ( SEGLEN - i - 1 , color ) ;
2018-10-25 20:55:29 +02:00
}
2019-12-23 18:38:54 +01:00
return FRAMETIME ;
2018-10-25 20:55:29 +02:00
}
/*
* Tricolor chase mode
*/
uint16_t WS2812FX : : mode_tricolor_chase ( void ) {
2019-05-22 00:23:09 +02:00
return tricolor_chase ( SEGCOLOR ( 2 ) , SEGCOLOR ( 0 ) ) ;
2018-09-04 15:51:38 +02:00
}
/*
* ICU mode
*/
uint16_t WS2812FX : : mode_icu ( void ) {
2019-05-22 00:23:09 +02:00
uint16_t dest = SEGENV . step & 0xFFFF ;
2020-02-18 20:52:12 +01:00
uint8_t space = ( SEGMENT . intensity > > 3 ) + 2 ;
2018-09-08 16:21:44 +02:00
2019-05-22 00:23:09 +02:00
fill ( SEGCOLOR ( 1 ) ) ;
2018-10-24 02:06:07 +02:00
2020-02-18 20:52:12 +01:00
byte pindex = map ( dest , 0 , SEGLEN - SEGLEN / space , 0 , 255 ) ;
2018-10-24 02:06:07 +02:00
uint32_t col = color_from_palette ( pindex , false , false , 0 ) ;
2019-12-22 17:41:32 +01:00
2020-02-18 20:52:12 +01:00
setPixelColor ( dest , col ) ;
setPixelColor ( dest + SEGLEN / space , col ) ;
2018-09-04 15:51:38 +02:00
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux0 = = dest ) { // pause between eye movements
2018-09-04 15:51:38 +02:00
if ( random8 ( 6 ) = = 0 ) { // blink once in a while
2020-02-18 20:52:12 +01:00
setPixelColor ( dest , SEGCOLOR ( 1 ) ) ;
setPixelColor ( dest + SEGLEN / space , SEGCOLOR ( 1 ) ) ;
2018-09-04 15:51:38 +02:00
return 200 ;
2017-09-18 09:50:18 +02:00
}
2020-02-18 20:52:12 +01:00
SEGENV . aux0 = random16 ( SEGLEN - SEGLEN / space ) ;
2019-01-31 23:42:48 +01:00
return 1000 + random16 ( 2000 ) ;
2017-09-18 09:50:18 +02:00
}
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux0 > SEGENV . step ) {
SEGENV . step + + ;
2018-09-04 15:51:38 +02:00
dest + + ;
2019-05-22 00:23:09 +02:00
} else if ( SEGENV . aux0 < SEGENV . step ) {
SEGENV . step - - ;
2018-09-04 15:51:38 +02:00
dest - - ;
2017-09-18 09:50:18 +02:00
}
2020-01-14 10:57:23 +01:00
setPixelColor ( dest , col ) ;
2020-02-18 20:52:12 +01:00
setPixelColor ( dest + SEGLEN / space , col ) ;
2017-09-18 09:50:18 +02:00
2018-09-04 15:51:38 +02:00
return SPEED_FORMULA_L ;
2017-09-18 09:50:18 +02:00
}
2018-09-04 15:51:38 +02:00
2017-09-18 09:50:18 +02:00
/*
2018-09-04 15:51:38 +02:00
* Custom mode by Aircoookie . Color Wipe , but with 3 colors
2017-09-18 09:50:18 +02:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_tricolor_wipe ( void )
{
2019-12-30 01:40:38 +01:00
uint32_t cycleTime = 1000 + ( 255 - SEGMENT . speed ) * 200 ;
uint32_t perc = now % cycleTime ;
uint16_t prog = ( perc * 65535 ) / cycleTime ;
uint16_t ledIndex = ( prog * SEGLEN * 3 ) > > 16 ;
uint16_t ledOffset = ledIndex ;
2019-12-30 01:18:19 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2018-09-04 15:51:38 +02:00
{
2019-12-30 01:18:19 +01:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 2 ) ) ;
}
2019-12-30 01:40:38 +01:00
if ( ledIndex < SEGLEN ) { //wipe from 0 to 1
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2019-12-30 01:18:19 +01:00
{
2020-01-14 10:57:23 +01:00
setPixelColor ( i , ( i > ledOffset ) ? SEGCOLOR ( 0 ) : SEGCOLOR ( 1 ) ) ;
2019-12-30 01:18:19 +01:00
}
2019-12-30 01:40:38 +01:00
} else if ( ledIndex < SEGLEN * 2 ) { //wipe from 1 to 2
ledOffset = ledIndex - SEGLEN ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = ledOffset + 1 ; i < SEGLEN ; i + + )
2019-12-30 01:18:19 +01:00
{
setPixelColor ( i , SEGCOLOR ( 1 ) ) ;
}
} else //wipe from 2 to 0
2018-09-04 15:51:38 +02:00
{
2019-12-30 01:40:38 +01:00
ledOffset = ledIndex - SEGLEN * 2 ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < = ledOffset ; i + + )
2019-12-30 01:18:19 +01:00
{
setPixelColor ( i , SEGCOLOR ( 0 ) ) ;
}
2017-09-18 09:50:18 +02:00
}
2019-12-24 14:46:07 +01:00
return FRAMETIME ;
2017-09-18 09:50:18 +02:00
}
2018-09-04 15:51:38 +02:00
2017-09-18 09:50:18 +02:00
/*
2018-09-04 15:51:38 +02:00
* Fades between 3 colors
* Custom mode by Keith Lord : https : //github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h
* Modified by Aircoookie
2017-09-18 09:50:18 +02:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_tricolor_fade ( void )
{
2019-12-24 14:46:07 +01:00
uint16_t counter = now * ( ( SEGMENT . speed > > 3 ) + 1 ) ;
2019-12-30 01:18:19 +01:00
uint32_t prog = ( counter * 768 ) > > 16 ;
2019-12-24 14:46:07 +01:00
2018-10-24 02:06:07 +02:00
uint32_t color1 = 0 , color2 = 0 ;
2018-10-25 20:55:29 +02:00
byte stage = 0 ;
2017-09-18 09:50:18 +02:00
2019-12-30 01:18:19 +01:00
if ( prog < 256 ) {
2019-05-22 00:23:09 +02:00
color1 = SEGCOLOR ( 0 ) ;
color2 = SEGCOLOR ( 1 ) ;
2018-10-25 20:55:29 +02:00
stage = 0 ;
2019-12-30 01:18:19 +01:00
} else if ( prog < 512 ) {
2019-05-22 00:23:09 +02:00
color1 = SEGCOLOR ( 1 ) ;
color2 = SEGCOLOR ( 2 ) ;
2018-10-25 20:55:29 +02:00
stage = 1 ;
2018-10-24 02:06:07 +02:00
} else {
2019-05-22 00:23:09 +02:00
color1 = SEGCOLOR ( 2 ) ;
color2 = SEGCOLOR ( 0 ) ;
2018-10-25 20:55:29 +02:00
stage = 2 ;
2017-09-18 09:50:18 +02:00
}
2019-12-30 01:18:19 +01:00
byte stp = prog ; // % 256
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2021-05-11 14:54:03 +02:00
uint32_t color ;
2018-10-25 20:55:29 +02:00
if ( stage = = 2 ) {
2019-12-22 17:41:32 +01:00
color = color_blend ( color_from_palette ( i , true , PALETTE_SOLID_WRAP , 2 ) , color2 , stp ) ;
2018-10-25 20:55:29 +02:00
} else if ( stage = = 1 ) {
2019-12-22 17:41:32 +01:00
color = color_blend ( color1 , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 2 ) , stp ) ;
2018-10-24 02:06:07 +02:00
} else {
color = color_blend ( color1 , color2 , stp ) ;
}
2018-09-04 15:51:38 +02:00
setPixelColor ( i , color ) ;
2017-09-18 09:50:18 +02:00
}
2020-01-14 10:57:23 +01:00
return FRAMETIME ;
2017-09-18 09:50:18 +02:00
}
2018-09-04 15:51:38 +02:00
2017-09-18 09:50:18 +02:00
/*
2018-09-04 15:51:38 +02:00
* Creates random comets
* Custom mode by Keith Lord : https : //github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h
2017-09-18 09:50:18 +02:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_multi_comet ( void )
{
2020-02-15 14:42:09 +01:00
uint32_t cycleTime = 10 + ( uint32_t ) ( 255 - SEGMENT . speed ) ;
2020-01-14 10:57:23 +01:00
uint32_t it = now / cycleTime ;
if ( SEGENV . step = = it ) return FRAMETIME ;
2020-02-15 14:42:09 +01:00
if ( ! SEGENV . allocateData ( sizeof ( uint16_t ) * 8 ) ) return mode_static ( ) ; //allocation failed
2019-02-11 23:49:04 +01:00
fade_out ( SEGMENT . intensity ) ;
2020-02-15 14:42:09 +01:00
uint16_t * comets = reinterpret_cast < uint16_t * > ( SEGENV . data ) ;
2018-09-04 15:51:38 +02:00
2019-02-11 23:49:04 +01:00
for ( uint8_t i = 0 ; i < 8 ; i + + ) {
2019-05-22 00:23:09 +02:00
if ( comets [ i ] < SEGLEN ) {
2020-01-14 10:57:23 +01:00
uint16_t index = comets [ i ] ;
2019-05-22 00:23:09 +02:00
if ( SEGCOLOR ( 2 ) ! = 0 )
2018-09-04 15:51:38 +02:00
{
2019-05-22 00:23:09 +02:00
setPixelColor ( index , i % 2 ? color_from_palette ( index , true , PALETTE_SOLID_WRAP , 0 ) : SEGCOLOR ( 2 ) ) ;
2018-09-04 15:51:38 +02:00
} else
{
2018-10-24 02:06:07 +02:00
setPixelColor ( index , color_from_palette ( index , true , PALETTE_SOLID_WRAP , 0 ) ) ;
2018-09-04 15:51:38 +02:00
}
comets [ i ] + + ;
2017-09-18 09:50:18 +02:00
} else {
2019-05-22 00:23:09 +02:00
if ( ! random ( SEGLEN ) ) {
2018-09-04 15:51:38 +02:00
comets [ i ] = 0 ;
}
2017-09-18 09:50:18 +02:00
}
}
2020-01-14 10:57:23 +01:00
SEGENV . step = it ;
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2017-09-18 09:50:18 +02:00
2018-09-04 15:51:38 +02:00
/*
* Creates two Larson scanners moving in opposite directions
* Custom mode by Keith Lord : https : //github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h
*/
uint16_t WS2812FX : : mode_dual_larson_scanner ( void ) {
2019-12-23 18:38:54 +01:00
return larson_scanner ( true ) ;
2017-09-18 09:50:18 +02:00
}
2018-09-04 15:51:38 +02:00
2017-09-18 09:50:18 +02:00
/*
2018-09-04 15:51:38 +02:00
* Running random pixels
* Custom mode by Keith Lord : https : //github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h
2017-09-18 09:50:18 +02:00
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_random_chase ( void )
{
2019-12-23 18:38:54 +01:00
uint32_t cycleTime = 25 + ( 3 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ;
uint32_t it = now / cycleTime ;
if ( SEGENV . step = = it ) return FRAMETIME ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = SEGLEN - 1 ; i > 0 ; i - - ) {
2018-09-04 15:51:38 +02:00
setPixelColor ( i , getPixelColor ( i - 1 ) ) ;
2017-09-18 09:50:18 +02:00
}
2020-01-14 10:57:23 +01:00
uint32_t color = getPixelColor ( 0 ) ;
if ( SEGLEN > 1 ) color = getPixelColor ( 1 ) ;
2019-02-10 23:05:06 +01:00
uint8_t r = random8 ( 6 ) ! = 0 ? ( color > > 16 & 0xFF ) : random8 ( ) ;
uint8_t g = random8 ( 6 ) ! = 0 ? ( color > > 8 & 0xFF ) : random8 ( ) ;
uint8_t b = random8 ( 6 ) ! = 0 ? ( color & 0xFF ) : random8 ( ) ;
2020-01-14 10:57:23 +01:00
setPixelColor ( 0 , r , g , b ) ;
2017-09-18 09:50:18 +02:00
2019-12-23 18:38:54 +01:00
SEGENV . step = it ;
return FRAMETIME ;
2017-09-18 09:50:18 +02:00
}
2021-08-22 22:16:25 +02:00
//7 bytes
2018-09-04 15:51:38 +02:00
typedef struct Oscillator {
int16_t pos ;
int8_t size ;
int8_t dir ;
int8_t speed ;
} oscillator ;
2019-12-22 17:41:32 +01:00
/*
/ Oscillating bars of color , updated with standard framerate
*/
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_oscillate ( void )
2017-11-29 23:56:02 +01:00
{
2020-01-02 20:41:15 +01:00
uint8_t numOscillators = 3 ;
uint16_t dataSize = sizeof ( oscillator ) * numOscillators ;
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
Oscillator * oscillators = reinterpret_cast < Oscillator * > ( SEGENV . data ) ;
if ( SEGENV . call = = 0 )
{
2021-02-27 00:20:31 +01:00
oscillators [ 0 ] = { ( int16_t ) ( SEGLEN / 4 ) , ( int8_t ) ( SEGLEN / 8 ) , 1 , 1 } ;
oscillators [ 1 ] = { ( int16_t ) ( SEGLEN / 4 * 3 ) , ( int8_t ) ( SEGLEN / 8 ) , 1 , 2 } ;
oscillators [ 2 ] = { ( int16_t ) ( SEGLEN / 4 * 2 ) , ( int8_t ) ( SEGLEN / 8 ) , - 1 , 1 } ;
2020-01-02 20:41:15 +01:00
}
2018-09-04 15:51:38 +02:00
2019-12-23 18:38:54 +01:00
uint32_t cycleTime = 20 + ( 2 * ( uint32_t ) ( 255 - SEGMENT . speed ) ) ;
uint32_t it = now / cycleTime ;
2020-01-14 10:57:23 +01:00
for ( uint8_t i = 0 ; i < numOscillators ; i + + ) {
2019-12-23 18:38:54 +01:00
// if the counter has increased, move the oscillator by the random step
if ( it ! = SEGENV . step ) oscillators [ i ] . pos + = oscillators [ i ] . dir * oscillators [ i ] . speed ;
2019-12-22 17:41:32 +01:00
oscillators [ i ] . size = SEGLEN / ( 3 + SEGMENT . intensity / 8 ) ;
2018-09-04 15:51:38 +02:00
if ( ( oscillators [ i ] . dir = = - 1 ) & & ( oscillators [ i ] . pos < = 0 ) ) {
oscillators [ i ] . pos = 0 ;
oscillators [ i ] . dir = 1 ;
2019-12-23 18:38:54 +01:00
// make bigger steps for faster speeds
oscillators [ i ] . speed = SEGMENT . speed > 100 ? random8 ( 2 , 4 ) : random8 ( 1 , 3 ) ;
2018-09-04 15:51:38 +02:00
}
2019-05-22 00:23:09 +02:00
if ( ( oscillators [ i ] . dir = = 1 ) & & ( oscillators [ i ] . pos > = ( SEGLEN - 1 ) ) ) {
oscillators [ i ] . pos = SEGLEN - 1 ;
2018-09-04 15:51:38 +02:00
oscillators [ i ] . dir = - 1 ;
2019-12-23 18:38:54 +01:00
oscillators [ i ] . speed = SEGMENT . speed > 100 ? random8 ( 2 , 4 ) : random8 ( 1 , 3 ) ;
2017-11-29 23:56:02 +01:00
}
}
2020-01-02 20:41:15 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-09-04 15:51:38 +02:00
uint32_t color = BLACK ;
2020-01-02 20:41:15 +01:00
for ( uint8_t j = 0 ; j < numOscillators ; j + + ) {
2018-09-04 15:51:38 +02:00
if ( i > = oscillators [ j ] . pos - oscillators [ j ] . size & & i < = oscillators [ j ] . pos + oscillators [ j ] . size ) {
2020-02-18 20:52:12 +01:00
color = ( color = = BLACK ) ? SEGCOLOR ( j ) : color_blend ( color , SEGCOLOR ( j ) , 128 ) ;
2018-09-04 15:51:38 +02:00
}
}
2020-01-14 10:57:23 +01:00
setPixelColor ( i , color ) ;
2018-09-04 15:51:38 +02:00
}
2019-12-23 18:38:54 +01:00
SEGENV . step = it ;
2019-12-22 17:41:32 +01:00
return FRAMETIME ;
2018-09-04 15:51:38 +02:00
}
2018-09-06 02:05:56 +02:00
uint16_t WS2812FX : : mode_lightning ( void )
{
2020-03-30 12:56:51 +02:00
uint16_t ledstart = random16 ( SEGLEN ) ; // Determine starting location of flash
2020-12-31 16:16:33 +01:00
uint16_t ledlen = 1 + random16 ( SEGLEN - ledstart ) ; // Determine length of flash (not to go beyond NUM_LEDS-1)
2019-12-22 17:41:32 +01:00
uint8_t bri = 255 / random8 ( 1 , 3 ) ;
2018-09-06 02:05:56 +02:00
2020-12-31 16:16:33 +01:00
if ( SEGENV . aux1 = = 0 ) //init, leader flash
2018-09-06 02:05:56 +02:00
{
2020-12-31 16:16:33 +01:00
SEGENV . aux1 = random8 ( 4 , 4 + SEGMENT . intensity / 20 ) ; //number of flashes
SEGENV . aux1 * = 2 ;
bri = 52 ; //leader has lower brightness
SEGENV . aux0 = 200 ; //200ms delay after leader
2018-09-06 02:05:56 +02:00
}
2019-05-22 00:23:09 +02:00
fill ( SEGCOLOR ( 1 ) ) ;
2019-12-22 17:41:32 +01:00
2020-12-31 16:16:33 +01:00
if ( SEGENV . aux1 > 3 & & ! ( SEGENV . aux1 & 0x01 ) ) { //flash on even number >2
2018-09-06 02:05:56 +02:00
for ( int i = ledstart ; i < ledstart + ledlen ; i + + )
{
2020-12-31 16:16:33 +01:00
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 , bri ) ) ;
2018-09-06 02:05:56 +02:00
}
2020-12-31 16:16:33 +01:00
SEGENV . aux1 - - ;
2018-09-06 02:05:56 +02:00
2020-12-31 16:16:33 +01:00
SEGENV . step = millis ( ) ;
//return random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds
} else {
if ( millis ( ) - SEGENV . step > SEGENV . aux0 ) {
SEGENV . aux1 - - ;
if ( SEGENV . aux1 < 2 ) SEGENV . aux1 = 0 ;
2018-09-06 02:05:56 +02:00
2020-12-31 16:16:33 +01:00
SEGENV . aux0 = ( 50 + random8 ( 100 ) ) ; //delay between flashes
if ( SEGENV . aux1 = = 2 ) {
SEGENV . aux0 = ( random8 ( 255 - SEGMENT . speed ) * 100 ) ; // delay between strikes
}
SEGENV . step = millis ( ) ;
}
}
return FRAMETIME ;
2018-09-06 02:05:56 +02:00
}
2018-09-08 16:21:44 +02:00
// Pride2015
// Animated, ever-changing rainbows.
// by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5
uint16_t WS2812FX : : mode_pride_2015 ( void )
{
uint16_t duration = 10 + SEGMENT . speed ;
2019-05-22 00:23:09 +02:00
uint16_t sPseudotime = SEGENV . step ;
uint16_t sHue16 = SEGENV . aux0 ;
2019-12-22 17:41:32 +01:00
2018-09-08 16:21:44 +02:00
uint8_t sat8 = beatsin88 ( 87 , 220 , 250 ) ;
uint8_t brightdepth = beatsin88 ( 341 , 96 , 224 ) ;
uint16_t brightnessthetainc16 = beatsin88 ( 203 , ( 25 * 256 ) , ( 40 * 256 ) ) ;
uint8_t msmultiplier = beatsin88 ( 147 , 23 , 60 ) ;
uint16_t hue16 = sHue16 ; //gHue * 256;
uint16_t hueinc16 = beatsin88 ( 113 , 1 , 3000 ) ;
2019-12-22 17:41:32 +01:00
2018-09-08 16:21:44 +02:00
sPseudotime + = duration * msmultiplier ;
sHue16 + = duration * beatsin88 ( 400 , 5 , 9 ) ;
uint16_t brightnesstheta16 = sPseudotime ;
CRGB fastled_col ;
2019-12-22 17:41:32 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-09-08 16:21:44 +02:00
hue16 + = hueinc16 ;
uint8_t hue8 = hue16 > > 8 ;
brightnesstheta16 + = brightnessthetainc16 ;
uint16_t b16 = sin16 ( brightnesstheta16 ) + 32768 ;
uint16_t bri16 = ( uint32_t ) ( ( uint32_t ) b16 * ( uint32_t ) b16 ) / 65536 ;
uint8_t bri8 = ( uint32_t ) ( ( ( uint32_t ) bri16 ) * brightdepth ) / 65536 ;
bri8 + = ( 255 - brightdepth ) ;
2019-12-22 17:41:32 +01:00
2018-09-08 16:21:44 +02:00
CRGB newcolor = CHSV ( hue8 , sat8 , bri8 ) ;
2019-08-30 15:39:34 +02:00
fastled_col = col_to_crgb ( getPixelColor ( i ) ) ;
2019-12-22 17:41:32 +01:00
2020-01-14 10:57:23 +01:00
nblend ( fastled_col , newcolor , 64 ) ;
2018-09-08 16:21:44 +02:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
}
2019-05-22 00:23:09 +02:00
SEGENV . step = sPseudotime ;
SEGENV . aux0 = sHue16 ;
2019-12-22 17:41:32 +01:00
return FRAMETIME ;
2018-09-08 16:21:44 +02:00
}
2019-02-09 16:37:20 +01:00
//eight colored dots, weaving in and out of sync with each other
2018-09-04 15:51:38 +02:00
uint16_t WS2812FX : : mode_juggle ( void ) {
2019-02-11 23:49:04 +01:00
fade_out ( SEGMENT . intensity ) ;
2018-09-04 15:51:38 +02:00
CRGB fastled_col ;
byte dothue = 0 ;
for ( byte i = 0 ; i < 8 ; i + + ) {
2020-01-14 10:57:23 +01:00
uint16_t index = 0 + beatsin88 ( ( 128 + SEGMENT . speed ) * ( i + 7 ) , 0 , SEGLEN - 1 ) ;
2019-08-30 15:39:34 +02:00
fastled_col = col_to_crgb ( getPixelColor ( index ) ) ;
2019-02-11 23:49:04 +01:00
fastled_col | = ( SEGMENT . palette = = 0 ) ? CHSV ( dothue , 220 , 255 ) : ColorFromPalette ( currentPalette , dothue , 255 ) ;
2018-09-04 15:51:38 +02:00
setPixelColor ( index , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
dothue + = 32 ;
2017-11-30 23:35:22 +01:00
}
2019-12-23 19:28:37 +01:00
return FRAMETIME ;
2017-11-30 23:35:22 +01:00
}
2017-11-20 00:07:37 +01:00
2018-09-08 16:21:44 +02:00
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_palette ( )
2016-12-17 23:43:07 +01:00
{
2019-10-07 20:17:52 +02:00
uint16_t counter = 0 ;
if ( SEGMENT . speed ! = 0 )
{
counter = ( now * ( ( SEGMENT . speed > > 3 ) + 1 ) ) & 0xFFFF ;
counter = counter > > 8 ;
}
2018-09-11 00:20:12 +02:00
bool noWrap = ( paletteBlend = = 2 | | ( paletteBlend = = 0 & & SEGMENT . speed = = 0 ) ) ;
2019-12-06 01:44:45 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2016-12-31 00:38:51 +01:00
{
2019-10-07 20:17:52 +02:00
uint8_t colorIndex = ( i * 255 / SEGLEN ) - counter ;
2018-09-11 00:20:12 +02:00
if ( noWrap ) colorIndex = map ( colorIndex , 0 , 255 , 0 , 240 ) ; //cut off blend at palette "end"
2020-01-14 10:57:23 +01:00
setPixelColor ( i , color_from_palette ( colorIndex , false , true , 255 ) ) ;
2016-12-31 00:38:51 +01:00
}
2019-10-07 20:17:52 +02:00
return FRAMETIME ;
2018-09-06 02:05:56 +02:00
}
2018-09-11 00:20:12 +02:00
// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active
// Fire2012 by Mark Kriegsman, July 2012
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
////
// This basic one-dimensional 'fire' simulation works roughly as follows:
// There's a underlying array of 'heat' cells, that model the temperature
// at each point along the line. Every cycle through the simulation,
// four steps are performed:
// 1) All cells cool down a little bit, losing heat to the air
// 2) The heat from each cell drifts 'up' and diffuses a little
// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
// 4) The heat from each cell is rendered as a color into the leds array
// The heat-to-color mapping uses a black-body radiation approximation.
//
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
//
// This simulation scales it self a bit depending on NUM_LEDS; it should look
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
//
// I recommend running this simulation at anywhere from 30-100 frames per second,
// meaning an interframe delay of about 10-35 milliseconds.
//
// Looks best on a high-density LED setup (60+ pixels/meter).
//
//
// There are two main parameters you can play with to control the look and
2019-10-07 20:17:52 +02:00
// feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used
2018-09-11 00:20:12 +02:00
// in step 3 above) (Effect Intensity = Sparking).
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_fire_2012 ( )
2018-09-11 00:20:12 +02:00
{
2019-10-07 20:17:52 +02:00
uint32_t it = now > > 5 ; //div 32
2018-09-11 00:20:12 +02:00
2020-01-02 20:41:15 +01:00
if ( ! SEGENV . allocateData ( SEGLEN ) ) return mode_static ( ) ; //allocation failed
byte * heat = SEGENV . data ;
2019-10-07 20:17:52 +02:00
if ( it ! = SEGENV . step )
{
2020-12-25 17:53:00 +01:00
uint8_t ignition = max ( 7 , SEGLEN / 10 ) ; // ignition area: 10% of segment length or minimum 7 pixels
2019-10-07 20:17:52 +02:00
// Step 1. Cool down every cell a little
2020-01-02 20:41:15 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2020-12-25 17:53:00 +01:00
uint8_t temp = qsub8 ( heat [ i ] , random8 ( 0 , ( ( ( 20 + SEGMENT . speed / 3 ) * 10 ) / SEGLEN ) + 2 ) ) ;
2021-02-24 20:23:32 +01:00
heat [ i ] = ( temp = = 0 & & i < ignition ) ? 16 : temp ; // prevent ignition area from becoming black
2019-10-07 20:17:52 +02:00
}
2018-09-11 00:20:12 +02:00
2019-10-07 20:17:52 +02:00
// Step 2. Heat from each cell drifts 'up' and diffuses a little
2020-01-02 20:41:15 +01:00
for ( uint16_t k = SEGLEN - 1 ; k > 1 ; k - - ) {
2020-12-25 17:53:00 +01:00
heat [ k ] = ( heat [ k - 1 ] + ( heat [ k - 2 ] < < 1 ) ) / 3 ; // heat[k-2] multiplied by 2
2019-10-07 20:17:52 +02:00
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
2020-01-02 20:41:15 +01:00
if ( random8 ( ) < = SEGMENT . intensity ) {
2020-12-25 17:53:00 +01:00
uint8_t y = random8 ( ignition ) ;
2020-01-02 20:41:15 +01:00
if ( y < SEGLEN ) heat [ y ] = qadd8 ( heat [ y ] , random8 ( 160 , 255 ) ) ;
2019-10-07 20:17:52 +02:00
}
SEGENV . step = it ;
2018-09-11 00:20:12 +02:00
}
// Step 4. Map from heat cells to LED colors
2020-01-02 20:41:15 +01:00
for ( uint16_t j = 0 ; j < SEGLEN ; j + + ) {
2020-03-26 10:18:19 +01:00
CRGB color = ColorFromPalette ( currentPalette , MIN ( heat [ j ] , 240 ) , 255 , LINEARBLEND ) ;
2020-01-14 10:57:23 +01:00
setPixelColor ( j , color . red , color . green , color . blue ) ;
2018-09-11 00:20:12 +02:00
}
2019-10-07 20:17:52 +02:00
return FRAMETIME ;
2018-09-11 00:20:12 +02:00
}
2018-09-08 16:21:44 +02:00
// ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb
// This function draws color waves with an ever-changing,
// widely-varying set of parameters, using a color palette.
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_colorwaves ( )
2018-09-08 16:21:44 +02:00
{
uint16_t duration = 10 + SEGMENT . speed ;
2019-05-22 00:23:09 +02:00
uint16_t sPseudotime = SEGENV . step ;
uint16_t sHue16 = SEGENV . aux0 ;
2018-09-08 16:21:44 +02:00
2020-01-14 10:57:23 +01:00
uint8_t brightdepth = beatsin88 ( 341 , 96 , 224 ) ;
2018-09-08 16:21:44 +02:00
uint16_t brightnessthetainc16 = beatsin88 ( 203 , ( 25 * 256 ) , ( 40 * 256 ) ) ;
uint8_t msmultiplier = beatsin88 ( 147 , 23 , 60 ) ;
uint16_t hue16 = sHue16 ; //gHue * 256;
2020-01-19 00:06:17 +01:00
// uint16_t hueinc16 = beatsin88(113, 300, 1500);
uint16_t hueinc16 = beatsin88 ( 113 , 60 , 300 ) * SEGMENT . intensity * 10 / 255 ; // Use the Intensity Slider for the hues
2018-09-08 16:21:44 +02:00
sPseudotime + = duration * msmultiplier ;
2020-01-14 10:57:23 +01:00
sHue16 + = duration * beatsin88 ( 400 , 5 , 9 ) ;
2018-09-08 16:21:44 +02:00
uint16_t brightnesstheta16 = sPseudotime ;
CRGB fastled_col ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2018-09-08 16:21:44 +02:00
hue16 + = hueinc16 ;
2019-02-02 15:31:43 +01:00
uint8_t hue8 = hue16 > > 8 ;
2018-09-08 16:21:44 +02:00
uint16_t h16_128 = hue16 > > 7 ;
if ( h16_128 & 0x100 ) {
hue8 = 255 - ( h16_128 > > 1 ) ;
} else {
hue8 = h16_128 > > 1 ;
}
brightnesstheta16 + = brightnessthetainc16 ;
uint16_t b16 = sin16 ( brightnesstheta16 ) + 32768 ;
uint16_t bri16 = ( uint32_t ) ( ( uint32_t ) b16 * ( uint32_t ) b16 ) / 65536 ;
uint8_t bri8 = ( uint32_t ) ( ( ( uint32_t ) bri16 ) * brightdepth ) / 65536 ;
bri8 + = ( 255 - brightdepth ) ;
2019-02-10 23:05:06 +01:00
CRGB newcolor = ColorFromPalette ( currentPalette , hue8 , bri8 ) ;
2019-08-30 15:39:34 +02:00
fastled_col = col_to_crgb ( getPixelColor ( i ) ) ;
2018-09-08 16:21:44 +02:00
nblend ( fastled_col , newcolor , 128 ) ;
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
}
2019-05-22 00:23:09 +02:00
SEGENV . step = sPseudotime ;
SEGENV . aux0 = sHue16 ;
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2018-09-08 16:21:44 +02:00
}
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_bpm ( )
2018-09-06 02:05:56 +02:00
{
CRGB fastled_col ;
2019-10-07 20:17:52 +02:00
uint32_t stp = ( now / 20 ) & 0xFF ;
2018-09-06 02:05:56 +02:00
uint8_t beat = beatsin8 ( SEGMENT . speed , 64 , 255 ) ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-10-07 20:17:52 +02:00
fastled_col = ColorFromPalette ( currentPalette , stp + ( i * 2 ) , beat - stp + ( i * 10 ) ) ;
2018-09-06 02:05:56 +02:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
}
2019-10-07 20:17:52 +02:00
return FRAMETIME ;
2016-12-17 23:43:07 +01:00
}
2018-09-04 15:51:38 +02:00
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_fillnoise8 ( )
2016-12-31 00:38:51 +01:00
{
2019-05-22 00:23:09 +02:00
if ( SEGENV . call = = 0 ) SEGENV . step = random16 ( 12345 ) ;
2018-09-04 15:51:38 +02:00
CRGB fastled_col ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
uint8_t index = inoise8 ( i * SEGLEN , SEGENV . step + i * SEGLEN ) ;
2018-09-04 15:51:38 +02:00
fastled_col = ColorFromPalette ( currentPalette , index , 255 , LINEARBLEND ) ;
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
2016-12-31 00:38:51 +01:00
}
2019-05-22 00:23:09 +02:00
SEGENV . step + = beatsin8 ( SEGMENT . speed , 1 , 6 ) ; //10,1,4
2016-12-31 00:38:51 +01:00
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2016-12-31 00:38:51 +01:00
}
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_noise16_1 ( )
2016-12-31 00:38:51 +01:00
{
2018-09-08 16:21:44 +02:00
uint16_t scale = 320 ; // the "zoom factor" for the noise
2018-09-04 15:51:38 +02:00
CRGB fastled_col ;
2019-05-22 00:23:09 +02:00
SEGENV . step + = ( 1 + SEGMENT . speed / 16 ) ;
2016-12-31 00:38:51 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2016-12-31 00:38:51 +01:00
2018-09-08 16:21:44 +02:00
uint16_t shift_x = beatsin8 ( 11 ) ; // the x position of the noise field swings @ 17 bpm
2019-05-22 00:23:09 +02:00
uint16_t shift_y = SEGENV . step / 42 ; // the y position becomes slowly incremented
2018-09-04 15:51:38 +02:00
uint16_t real_x = ( i + shift_x ) * scale ; // the x position of the noise field swings @ 17 bpm
uint16_t real_y = ( i + shift_y ) * scale ; // the y position becomes slowly incremented
2019-05-22 00:23:09 +02:00
uint32_t real_z = SEGENV . step ; // the z position becomes quickly incremented
2018-09-04 15:51:38 +02:00
uint8_t noise = inoise16 ( real_x , real_y , real_z ) > > 8 ; // get the noise data and scale it down
uint8_t index = sin8 ( noise * 3 ) ; // map LED color based on noise data
2018-09-06 02:05:56 +02:00
fastled_col = ColorFromPalette ( currentPalette , index , 255 , LINEARBLEND ) ; // With that value, look up the 8 bit colour palette value and assign it to the current LED.
2018-09-04 15:51:38 +02:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
2016-12-31 00:38:51 +01:00
}
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2016-12-31 00:38:51 +01:00
}
2017-11-19 15:31:17 +01:00
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_noise16_2 ( )
2018-03-06 23:47:08 +01:00
{
2018-09-08 16:21:44 +02:00
uint16_t scale = 1000 ; // the "zoom factor" for the noise
2018-09-04 15:51:38 +02:00
CRGB fastled_col ;
2019-10-07 20:17:52 +02:00
SEGENV . step + = ( 1 + ( SEGMENT . speed > > 1 ) ) ;
2018-03-06 23:47:08 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2017-12-14 00:12:02 +01:00
2019-05-22 00:23:09 +02:00
uint16_t shift_x = SEGENV . step > > 6 ; // x as a function of time
2017-12-19 16:12:51 +01:00
2018-09-04 15:51:38 +02:00
uint32_t real_x = ( i + shift_x ) * scale ; // calculate the coordinates within the noise field
2017-12-14 00:12:02 +01:00
2018-09-08 16:21:44 +02:00
uint8_t noise = inoise16 ( real_x , 0 , 4223 ) > > 8 ; // get the noise data and scale it down
2017-12-14 00:12:02 +01:00
2018-09-04 15:51:38 +02:00
uint8_t index = sin8 ( noise * 3 ) ; // map led color based on noise data
2018-09-06 02:05:56 +02:00
fastled_col = ColorFromPalette ( currentPalette , index , noise , LINEARBLEND ) ; // With that value, look up the 8 bit colour palette value and assign it to the current LED.
2018-09-04 15:51:38 +02:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
2017-12-14 00:12:02 +01:00
}
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2017-11-29 23:56:02 +01:00
}
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_noise16_3 ( )
2017-11-29 23:56:02 +01:00
{
2018-09-08 16:21:44 +02:00
uint16_t scale = 800 ; // the "zoom factor" for the noise
2018-09-04 15:51:38 +02:00
CRGB fastled_col ;
2019-05-22 00:23:09 +02:00
SEGENV . step + = ( 1 + SEGMENT . speed ) ;
2017-11-29 23:56:02 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2017-11-29 23:56:02 +01:00
2018-09-04 15:51:38 +02:00
uint16_t shift_x = 4223 ; // no movement along x and y
uint16_t shift_y = 1234 ;
2017-11-29 23:56:02 +01:00
2018-09-04 15:51:38 +02:00
uint32_t real_x = ( i + shift_x ) * scale ; // calculate the coordinates within the noise field
uint32_t real_y = ( i + shift_y ) * scale ; // based on the precalculated positions
2019-05-22 00:23:09 +02:00
uint32_t real_z = SEGENV . step * 8 ;
2017-11-29 23:56:02 +01:00
2018-09-08 16:21:44 +02:00
uint8_t noise = inoise16 ( real_x , real_y , real_z ) > > 8 ; // get the noise data and scale it down
2017-11-29 23:56:02 +01:00
2018-09-04 15:51:38 +02:00
uint8_t index = sin8 ( noise * 3 ) ; // map led color based on noise data
2017-11-29 23:56:02 +01:00
2018-09-06 02:05:56 +02:00
fastled_col = ColorFromPalette ( currentPalette , index , noise , LINEARBLEND ) ; // With that value, look up the 8 bit colour palette value and assign it to the current LED.
2018-09-04 15:51:38 +02:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
2018-04-13 00:28:29 +02:00
}
2018-09-04 15:51:38 +02:00
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2017-09-18 12:24:31 +02:00
}
2018-09-04 15:51:38 +02:00
//https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino
2019-10-07 20:17:52 +02:00
uint16_t WS2812FX : : mode_noise16_4 ( )
2017-09-27 21:45:58 +02:00
{
2018-09-04 15:51:38 +02:00
CRGB fastled_col ;
2019-12-23 19:28:37 +01:00
uint32_t stp = ( now * SEGMENT . speed ) > > 7 ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
int16_t index = inoise16 ( uint32_t ( i ) < < 12 , stp ) ;
2018-09-06 02:05:56 +02:00
fastled_col = ColorFromPalette ( currentPalette , index ) ;
2018-09-04 15:51:38 +02:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
2017-12-14 00:12:02 +01:00
}
2019-10-07 20:17:52 +02:00
return FRAMETIME ;
2017-09-27 21:45:58 +02:00
}
2018-11-04 20:14:23 +01:00
2018-11-07 20:22:05 +01:00
//based on https://gist.github.com/kriegsman/5408ecd397744ba0393e
2018-11-04 20:14:23 +01:00
uint16_t WS2812FX : : mode_colortwinkle ( )
{
2020-01-02 20:41:15 +01:00
uint16_t dataSize = ( SEGLEN + 7 ) > > 3 ; //1 bit per LED
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
2018-11-04 20:14:23 +01:00
CRGB fastled_col , prev ;
2021-02-07 14:45:34 +01:00
fract8 fadeUpAmount = _brightness > 28 ? 8 + ( SEGMENT . speed > > 2 ) : 68 - _brightness , fadeDownAmount = _brightness > 28 ? 8 + ( SEGMENT . speed > > 3 ) : 68 - _brightness ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-08-30 15:39:34 +02:00
fastled_col = col_to_crgb ( getPixelColor ( i ) ) ;
2018-11-04 20:14:23 +01:00
prev = fastled_col ;
2020-01-14 10:57:23 +01:00
uint16_t index = i > > 3 ;
uint8_t bitNum = i & 0x07 ;
2020-01-02 20:41:15 +01:00
bool fadeUp = bitRead ( SEGENV . data [ index ] , bitNum ) ;
if ( fadeUp ) {
2018-11-04 20:14:23 +01:00
CRGB incrementalColor = fastled_col ;
2021-02-24 20:23:32 +01:00
incrementalColor . nscale8_video ( fadeUpAmount ) ;
2018-11-04 20:14:23 +01:00
fastled_col + = incrementalColor ;
2020-01-02 20:41:15 +01:00
if ( fastled_col . red = = 255 | | fastled_col . green = = 255 | | fastled_col . blue = = 255 ) {
bitWrite ( SEGENV . data [ index ] , bitNum , false ) ;
2018-11-04 20:14:23 +01:00
}
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
2021-02-24 20:23:32 +01:00
if ( col_to_crgb ( getPixelColor ( i ) ) = = prev ) { //fix "stuck" pixels
2018-11-04 20:14:23 +01:00
fastled_col + = fastled_col ;
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
}
} else {
2021-02-24 20:23:32 +01:00
fastled_col . nscale8 ( 255 - fadeDownAmount ) ;
2018-11-04 20:14:23 +01:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
}
}
2018-12-16 20:38:00 +01:00
2021-02-24 20:23:32 +01:00
for ( uint16_t j = 0 ; j < = SEGLEN / 50 ; j + + ) {
2020-01-02 20:41:15 +01:00
if ( random8 ( ) < = SEGMENT . intensity ) {
2021-02-24 20:23:32 +01:00
for ( uint8_t times = 0 ; times < 5 ; times + + ) { //attempt to spawn a new pixel 5 times
2020-01-14 10:57:23 +01:00
int i = random16 ( SEGLEN ) ;
2021-02-24 20:23:32 +01:00
if ( getPixelColor ( i ) = = 0 ) {
2018-12-16 20:38:00 +01:00
fastled_col = ColorFromPalette ( currentPalette , random8 ( ) , 64 , NOBLEND ) ;
2020-01-14 10:57:23 +01:00
uint16_t index = i > > 3 ;
uint8_t bitNum = i & 0x07 ;
2020-01-02 20:41:15 +01:00
bitWrite ( SEGENV . data [ index ] , bitNum , true ) ;
2018-12-16 20:38:00 +01:00
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
break ; //only spawn 1 new pixel per frame per 50 LEDs
}
2018-11-04 20:14:23 +01:00
}
}
}
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2018-11-04 20:14:23 +01:00
}
2018-11-07 20:22:05 +01:00
//Calm effect, like a lake at night
uint16_t WS2812FX : : mode_lake ( ) {
uint8_t sp = SEGMENT . speed / 10 ;
int wave1 = beatsin8 ( sp + 2 , - 64 , 64 ) ;
int wave2 = beatsin8 ( sp + 1 , - 64 , 64 ) ;
uint8_t wave3 = beatsin8 ( sp + 2 , 0 , 80 ) ;
CRGB fastled_col ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2018-11-07 20:22:05 +01:00
{
int index = cos8 ( ( i * 15 ) + wave1 ) / 2 + cubicwave8 ( ( i * 23 ) + wave2 ) / 2 ;
uint8_t lum = ( index > wave3 ) ? index - wave3 : 0 ;
fastled_col = ColorFromPalette ( currentPalette , map ( index , 0 , 255 , 0 , 240 ) , lum , LINEARBLEND ) ;
setPixelColor ( i , fastled_col . red , fastled_col . green , fastled_col . blue ) ;
}
2019-10-07 20:17:52 +02:00
return FRAMETIME ;
2018-11-07 20:22:05 +01:00
}
2018-11-20 20:32:21 +01:00
2018-11-28 12:24:32 +01:00
2018-11-20 20:32:21 +01:00
// meteor effect
2018-11-28 12:24:32 +01:00
// send a meteor from begining to to the end of the strip with a trail that randomly decays.
2018-11-20 20:32:21 +01:00
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
uint16_t WS2812FX : : mode_meteor ( ) {
2020-01-02 20:41:15 +01:00
if ( ! SEGENV . allocateData ( SEGLEN ) ) return mode_static ( ) ; //allocation failed
byte * trail = SEGENV . data ;
2019-05-22 00:23:09 +02:00
byte meteorSize = 1 + SEGLEN / 10 ;
2019-12-23 19:23:57 +01:00
uint16_t counter = now * ( ( SEGMENT . speed > > 2 ) + 8 ) ;
uint16_t in = counter * SEGLEN > > 16 ;
2018-11-28 12:24:32 +01:00
2018-11-20 20:32:21 +01:00
// fade all leds to colors[1] in LEDs one step
2020-01-02 20:41:15 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-02-02 15:31:43 +01:00
if ( random8 ( ) < = 255 - SEGMENT . intensity )
2018-11-28 12:24:32 +01:00
{
byte meteorTrailDecay = 128 + random8 ( 127 ) ;
2020-01-02 20:41:15 +01:00
trail [ i ] = scale8 ( trail [ i ] , meteorTrailDecay ) ;
2020-01-14 10:57:23 +01:00
setPixelColor ( i , color_from_palette ( trail [ i ] , false , true , 255 ) ) ;
2018-11-20 20:32:21 +01:00
}
}
2019-12-23 19:23:57 +01:00
2018-11-20 20:32:21 +01:00
// draw meteor
2019-12-23 19:23:57 +01:00
for ( int j = 0 ; j < meteorSize ; j + + ) {
uint16_t index = in + j ;
2020-01-02 20:41:15 +01:00
if ( index > = SEGLEN ) {
index = ( in + j - SEGLEN ) ;
2018-11-20 20:32:21 +01:00
}
2018-11-28 12:24:32 +01:00
2020-01-02 20:41:15 +01:00
trail [ index ] = 240 ;
2020-01-14 10:57:23 +01:00
setPixelColor ( index , color_from_palette ( trail [ index ] , false , true , 255 ) ) ;
2018-11-20 20:32:21 +01:00
}
2019-12-23 19:23:57 +01:00
return FRAMETIME ;
2018-11-20 20:32:21 +01:00
}
2018-11-28 12:24:32 +01:00
2018-12-06 00:27:36 +01:00
2018-12-02 02:49:05 +01:00
// smooth meteor effect
// send a meteor from begining to to the end of the strip with a trail that randomly decays.
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
uint16_t WS2812FX : : mode_meteor_smooth ( ) {
2020-01-02 20:41:15 +01:00
if ( ! SEGENV . allocateData ( SEGLEN ) ) return mode_static ( ) ; //allocation failed
byte * trail = SEGENV . data ;
2019-05-22 00:23:09 +02:00
byte meteorSize = 1 + SEGLEN / 10 ;
2020-01-02 20:41:15 +01:00
uint16_t in = map ( ( SEGENV . step > > 6 & 0xFF ) , 0 , 255 , 0 , SEGLEN - 1 ) ;
2018-12-02 02:49:05 +01:00
// fade all leds to colors[1] in LEDs one step
2020-01-02 20:41:15 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
if ( trail [ i ] ! = 0 & & random8 ( ) < = 255 - SEGMENT . intensity )
2018-12-02 02:49:05 +01:00
{
int change = 3 - random8 ( 12 ) ; //change each time between -8 and +3
2020-01-02 20:41:15 +01:00
trail [ i ] + = change ;
if ( trail [ i ] > 245 ) trail [ i ] = 0 ;
if ( trail [ i ] > 240 ) trail [ i ] = 240 ;
2020-01-14 10:57:23 +01:00
setPixelColor ( i , color_from_palette ( trail [ i ] , false , true , 255 ) ) ;
2018-12-02 02:49:05 +01:00
}
}
// draw meteor
for ( int j = 0 ; j < meteorSize ; j + + ) {
uint16_t index = in + j ;
2020-01-02 20:41:15 +01:00
if ( in + j > = SEGLEN ) {
index = ( in + j - SEGLEN ) ;
2018-12-02 02:49:05 +01:00
}
2020-01-14 10:57:23 +01:00
setPixelColor ( index , color_blend ( getPixelColor ( index ) , color_from_palette ( 240 , false , true , 255 ) , 48 ) ) ;
2020-01-02 20:41:15 +01:00
trail [ index ] = 240 ;
2018-12-02 02:49:05 +01:00
}
2019-05-22 00:23:09 +02:00
SEGENV . step + = SEGMENT . speed + 1 ;
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2018-12-02 02:49:05 +01:00
}
//Railway Crossing / Christmas Fairy lights
uint16_t WS2812FX : : mode_railway ( )
{
uint16_t dur = 40 + ( 255 - SEGMENT . speed ) * 10 ;
uint16_t rampdur = ( dur * SEGMENT . intensity ) > > 8 ;
2019-05-22 00:23:09 +02:00
if ( SEGENV . step > dur )
2018-12-02 02:49:05 +01:00
{
//reverse direction
2019-05-22 00:23:09 +02:00
SEGENV . step = 0 ;
SEGENV . aux0 = ! SEGENV . aux0 ;
2018-12-02 02:49:05 +01:00
}
uint8_t pos = 255 ;
if ( rampdur ! = 0 )
{
2019-05-22 00:23:09 +02:00
uint16_t p0 = ( SEGENV . step * 255 ) / rampdur ;
2018-12-02 02:49:05 +01:00
if ( p0 < 255 ) pos = p0 ;
}
2019-05-22 00:23:09 +02:00
if ( SEGENV . aux0 ) pos = 255 - pos ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + = 2 )
2018-12-02 02:49:05 +01:00
{
setPixelColor ( i , color_from_palette ( 255 - pos , false , false , 255 ) ) ;
2020-01-14 10:57:23 +01:00
if ( i < SEGLEN - 1 )
2018-12-02 02:49:05 +01:00
{
setPixelColor ( i + 1 , color_from_palette ( pos , false , false , 255 ) ) ;
}
}
2019-10-07 20:17:52 +02:00
SEGENV . step + = FRAMETIME ;
return FRAMETIME ;
2018-12-02 02:49:05 +01:00
}
2019-01-31 23:42:48 +01:00
//Water ripple
//propagation velocity from speed
//drop rate from intensity
2020-01-02 20:41:15 +01:00
//4 bytes
typedef struct Ripple {
uint8_t state ;
uint8_t color ;
uint16_t pos ;
} ripple ;
2021-08-22 22:16:25 +02:00
# ifdef ESP8266
# define MAX_RIPPLES 56
# else
# define MAX_RIPPLES 100
# endif
2020-02-15 20:07:15 +01:00
uint16_t WS2812FX : : ripple_base ( bool rainbow )
2019-01-31 23:42:48 +01:00
{
2021-09-12 13:31:47 +02:00
uint16_t maxRipples = min ( 1 + ( SEGLEN > > 2 ) , MAX_RIPPLES ) ; // 56 max for 16 segment ESP8266
2020-03-30 12:56:51 +02:00
uint16_t dataSize = sizeof ( ripple ) * maxRipples ;
2020-01-02 20:41:15 +01:00
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
Ripple * ripples = reinterpret_cast < Ripple * > ( SEGENV . data ) ;
2019-01-31 23:42:48 +01:00
2020-02-15 20:07:15 +01:00
// ranbow background or chosen background, all very dim.
if ( rainbow ) {
if ( SEGENV . call = = 0 ) {
SEGENV . aux0 = random8 ( ) ;
SEGENV . aux1 = random8 ( ) ;
}
if ( SEGENV . aux0 = = SEGENV . aux1 ) {
SEGENV . aux1 = random8 ( ) ;
}
else if ( SEGENV . aux1 > SEGENV . aux0 ) {
SEGENV . aux0 + + ;
} else {
SEGENV . aux0 - - ;
}
fill ( color_blend ( color_wheel ( SEGENV . aux0 ) , BLACK , 235 ) ) ;
} else {
fill ( SEGCOLOR ( 1 ) ) ;
}
2019-01-31 23:42:48 +01:00
//draw wave
2020-03-30 12:56:51 +02:00
for ( uint16_t i = 0 ; i < maxRipples ; i + + )
2019-01-31 23:42:48 +01:00
{
2020-01-02 20:41:15 +01:00
uint16_t ripplestate = ripples [ i ] . state ;
2019-01-31 23:42:48 +01:00
if ( ripplestate )
{
2019-02-02 15:31:43 +01:00
uint8_t rippledecay = ( SEGMENT . speed > > 4 ) + 1 ; //faster decay if faster propagation
2020-01-02 20:41:15 +01:00
uint16_t rippleorigin = ripples [ i ] . pos ;
uint32_t col = color_from_palette ( ripples [ i ] . color , false , false , 255 ) ;
2019-02-02 15:31:43 +01:00
uint16_t propagation = ( ( ripplestate / rippledecay - 1 ) * SEGMENT . speed ) ;
int16_t propI = propagation > > 8 ;
uint8_t propF = propagation & 0xFF ;
int16_t left = rippleorigin - propI - 1 ;
uint8_t amp = ( ripplestate < 17 ) ? triwave8 ( ( ripplestate - 1 ) * 8 ) : map ( ripplestate , 17 , 255 , 255 , 2 ) ;
for ( int16_t v = left ; v < left + 4 ; v + + )
{
uint8_t mag = scale8 ( cubicwave8 ( ( propF > > 2 ) + ( v - left ) * 64 ) , amp ) ;
2020-01-14 10:57:23 +01:00
if ( v < SEGLEN & & v > = 0 )
2019-02-02 15:31:43 +01:00
{
setPixelColor ( v , color_blend ( getPixelColor ( v ) , col , mag ) ) ;
}
int16_t w = left + propI * 2 + 3 - ( v - left ) ;
2020-01-14 10:57:23 +01:00
if ( w < SEGLEN & & w > = 0 )
2019-02-02 15:31:43 +01:00
{
setPixelColor ( w , color_blend ( getPixelColor ( w ) , col , mag ) ) ;
}
}
ripplestate + = rippledecay ;
2020-01-02 20:41:15 +01:00
ripples [ i ] . state = ( ripplestate > 254 ) ? 0 : ripplestate ;
2019-02-02 15:31:43 +01:00
} else //randomly create new wave
2019-01-31 23:42:48 +01:00
{
2019-02-02 15:31:43 +01:00
if ( random16 ( IBN + 10000 ) < = SEGMENT . intensity )
2019-01-31 23:42:48 +01:00
{
2020-01-02 20:41:15 +01:00
ripples [ i ] . state = 1 ;
2020-01-14 10:57:23 +01:00
ripples [ i ] . pos = random16 ( SEGLEN ) ;
2020-01-02 20:41:15 +01:00
ripples [ i ] . color = random8 ( ) ; //color
2019-01-31 23:42:48 +01:00
}
}
}
2019-12-23 18:48:01 +01:00
return FRAMETIME ;
2019-01-31 23:42:48 +01:00
}
2021-08-22 22:16:25 +02:00
# undef MAX_RIPPLES
2019-08-30 15:39:34 +02:00
2020-02-15 20:07:15 +01:00
uint16_t WS2812FX : : mode_ripple ( void ) {
return ripple_base ( false ) ;
}
uint16_t WS2812FX : : mode_ripple_rainbow ( void ) {
return ripple_base ( true ) ;
}
2019-08-30 15:39:34 +02:00
// TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a
//
// TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
// Colors are chosen from a palette. Read more about this effect using the link above!
// If COOL_LIKE_INCANDESCENT is set to 1, colors will
// fade out slighted 'reddened', similar to how
// incandescent bulbs change color as they get dim down.
# define COOL_LIKE_INCANDESCENT 1
2019-10-02 01:17:26 +02:00
CRGB WS2812FX : : twinklefox_one_twinkle ( uint32_t ms , uint8_t salt , bool cat )
2019-08-30 15:39:34 +02:00
{
// Overall twinkle speed (changed)
2020-06-26 17:28:35 +02:00
uint16_t ticks = ms / SEGENV . aux0 ;
2019-08-30 15:39:34 +02:00
uint8_t fastcycle8 = ticks ;
uint16_t slowcycle16 = ( ticks > > 8 ) + salt ;
slowcycle16 + = sin8 ( slowcycle16 ) ;
slowcycle16 = ( slowcycle16 * 2053 ) + 1384 ;
uint8_t slowcycle8 = ( slowcycle16 & 0xFF ) + ( slowcycle16 > > 8 ) ;
// Overall twinkle density.
// 0 (NONE lit) to 8 (ALL lit at once).
// Default is 5.
uint8_t twinkleDensity = ( SEGMENT . intensity > > 5 ) + 1 ;
uint8_t bright = 0 ;
if ( ( ( slowcycle8 & 0x0E ) / 2 ) < twinkleDensity ) {
uint8_t ph = fastcycle8 ;
// This is like 'triwave8', which produces a
// symmetrical up-and-down triangle sawtooth waveform, except that this
// function produces a triangle wave with a faster attack and a slower decay
2019-10-02 01:17:26 +02:00
if ( cat ) //twinklecat, variant where the leds instantly turn on
{
bright = 255 - ph ;
} else { //vanilla twinklefox
if ( ph < 86 ) {
bright = ph * 3 ;
} else {
ph - = 86 ;
bright = 255 - ( ph + ( ph / 2 ) ) ;
}
2019-08-30 15:39:34 +02:00
}
}
uint8_t hue = slowcycle8 - salt ;
CRGB c ;
if ( bright > 0 ) {
c = ColorFromPalette ( currentPalette , hue , bright , NOBLEND ) ;
if ( COOL_LIKE_INCANDESCENT = = 1 ) {
// This code takes a pixel, and if its in the 'fading down'
// part of the cycle, it adjusts the color a little bit like the
// way that incandescent bulbs fade toward 'red' as they dim.
if ( fastcycle8 > = 128 )
{
uint8_t cooling = ( fastcycle8 - 128 ) > > 4 ;
c . g = qsub8 ( c . g , cooling ) ;
c . b = qsub8 ( c . b , cooling * 2 ) ;
}
}
} else {
c = CRGB : : Black ;
}
return c ;
}
// This function loops over each pixel, calculates the
// adjusted 'clock' that this pixel should use, and calls
// "CalculateOneTwinkle" on each pixel. It then displays
// either the twinkle color of the background color,
// whichever is brighter.
2019-12-04 12:15:12 +01:00
uint16_t WS2812FX : : twinklefox_base ( bool cat )
2019-08-30 15:39:34 +02:00
{
// "PRNG16" is the pseudorandom number generator
// It MUST be reset to the same starting value each time
// this function is called, so that the sequence of 'random'
// numbers that it generates is (paradoxically) stable.
uint16_t PRNG16 = 11337 ;
2020-06-26 17:28:35 +02:00
// Calculate speed
if ( SEGMENT . speed > 100 ) SEGENV . aux0 = 3 + ( ( 255 - SEGMENT . speed ) > > 3 ) ;
else SEGENV . aux0 = 22 + ( ( 100 - SEGMENT . speed ) > > 1 ) ;
2019-08-30 15:39:34 +02:00
// Set up the background color, "bg".
CRGB bg ;
bg = col_to_crgb ( SEGCOLOR ( 1 ) ) ;
uint8_t bglight = bg . getAverageLight ( ) ;
if ( bglight > 64 ) {
bg . nscale8_video ( 16 ) ; // very bright, so scale to 1/16th
} else if ( bglight > 16 ) {
bg . nscale8_video ( 64 ) ; // not that bright, so scale to 1/4th
} else {
bg . nscale8_video ( 86 ) ; // dim, scale to 1/3rd.
}
uint8_t backgroundBrightness = bg . getAverageLight ( ) ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-08-30 15:39:34 +02:00
PRNG16 = ( uint16_t ) ( PRNG16 * 2053 ) + 1384 ; // next 'random' number
uint16_t myclockoffset16 = PRNG16 ; // use that number as clock offset
PRNG16 = ( uint16_t ) ( PRNG16 * 2053 ) + 1384 ; // next 'random' number
// use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths)
uint8_t myspeedmultiplierQ5_3 = ( ( ( ( PRNG16 & 0xFF ) > > 4 ) + ( PRNG16 & 0x0F ) ) & 0x0F ) + 0x08 ;
2019-10-02 01:17:26 +02:00
uint32_t myclock30 = ( uint32_t ) ( ( now * myspeedmultiplierQ5_3 ) > > 3 ) + myclockoffset16 ;
2019-08-30 15:39:34 +02:00
uint8_t myunique8 = PRNG16 > > 8 ; // get 'salt' value for this pixel
// We now have the adjusted 'clock' for this pixel, now we call
// the function that computes what color the pixel should be based
// on the "brightness = f( time )" idea.
2019-10-02 01:17:26 +02:00
CRGB c = twinklefox_one_twinkle ( myclock30 , myunique8 , cat ) ;
2019-08-30 15:39:34 +02:00
uint8_t cbright = c . getAverageLight ( ) ;
int16_t deltabright = cbright - backgroundBrightness ;
if ( deltabright > = 32 | | ( ! bg ) ) {
// If the new pixel is significantly brighter than the background color,
// use the new color.
2019-10-07 20:17:52 +02:00
setPixelColor ( i , c . red , c . green , c . blue ) ;
2019-08-30 15:39:34 +02:00
} else if ( deltabright > 0 ) {
// If the new pixel is just slightly brighter than the background color,
// mix a blend of the new color and the background color
setPixelColor ( i , color_blend ( crgb_to_col ( bg ) , crgb_to_col ( c ) , deltabright * 8 ) ) ;
} else {
// if the new pixel is not at all brighter than the background color,
// just use the background color.
2019-10-07 20:17:52 +02:00
setPixelColor ( i , bg . r , bg . g , bg . b ) ;
2019-08-30 15:39:34 +02:00
}
}
2019-12-04 12:15:12 +01:00
return FRAMETIME ;
2019-10-02 01:17:26 +02:00
}
uint16_t WS2812FX : : mode_twinklefox ( )
{
2019-12-04 12:15:12 +01:00
return twinklefox_base ( false ) ;
2019-10-02 01:17:26 +02:00
}
uint16_t WS2812FX : : mode_twinklecat ( )
{
2019-12-04 12:15:12 +01:00
return twinklefox_base ( true ) ;
2019-10-02 01:17:26 +02:00
}
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
# define HALLOWEEN_EYE_SPACE 3
# define HALLOWEEN_EYE_WIDTH 1
uint16_t WS2812FX : : mode_halloween_eyes ( )
{
uint16_t eyeLength = ( 2 * HALLOWEEN_EYE_WIDTH ) + HALLOWEEN_EYE_SPACE ;
if ( eyeLength > SEGLEN ) return mode_static ( ) ; //bail if segment too short
fill ( SEGCOLOR ( 1 ) ) ; //fill background
uint8_t state = SEGENV . aux1 > > 8 ;
uint16_t stateTime = SEGENV . call ;
if ( stateTime = = 0 ) stateTime = 2000 ;
if ( state = = 0 ) { //spawn eyes
2020-01-14 10:57:23 +01:00
SEGENV . aux0 = random16 ( 0 , SEGLEN - eyeLength ) ; //start pos
2019-10-02 01:17:26 +02:00
SEGENV . aux1 = random8 ( ) ; //color
state = 1 ;
}
if ( state < 2 ) { //fade eyes
uint16_t startPos = SEGENV . aux0 ;
uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE ;
uint32_t fadestage = ( now - SEGENV . step ) * 255 / stateTime ;
if ( fadestage > 255 ) fadestage = 255 ;
uint32_t c = color_blend ( color_from_palette ( SEGENV . aux1 & 0xFF , false , false , 0 ) , SEGCOLOR ( 1 ) , fadestage ) ;
for ( uint16_t i = 0 ; i < HALLOWEEN_EYE_WIDTH ; i + + )
{
setPixelColor ( startPos + i , c ) ;
setPixelColor ( start2ndEye + i , c ) ;
}
}
if ( now - SEGENV . step > stateTime )
{
state + + ;
if ( state > 2 ) state = 0 ;
if ( state < 2 )
{
stateTime = 100 + ( 255 - SEGMENT . intensity ) * 10 ; //eye fade time
} else {
uint16_t eyeOffTimeBase = ( 255 - SEGMENT . speed ) * 10 ;
2019-10-03 16:33:37 +02:00
stateTime = eyeOffTimeBase + random16 ( eyeOffTimeBase ) ;
2019-10-02 01:17:26 +02:00
}
SEGENV . step = now ;
SEGENV . call = stateTime ;
}
SEGENV . aux1 = ( SEGENV . aux1 & 0xFF ) + ( state < < 8 ) ; //save state
2019-10-03 16:33:37 +02:00
return FRAMETIME ;
2019-08-30 15:39:34 +02:00
}
2019-11-18 12:29:36 +01:00
//Speed slider sets amount of LEDs lit, intensity sets unlit
uint16_t WS2812FX : : mode_static_pattern ( )
{
uint16_t lit = 1 + SEGMENT . speed ;
uint16_t unlit = 1 + SEGMENT . intensity ;
bool drawingLit = true ;
uint16_t cnt = 0 ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-11-18 12:29:36 +01:00
setPixelColor ( i , ( drawingLit ) ? color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) : SEGCOLOR ( 1 ) ) ;
cnt + + ;
if ( cnt > = ( ( drawingLit ) ? lit : unlit ) ) {
cnt = 0 ;
drawingLit = ! drawingLit ;
}
}
return FRAMETIME ;
}
2019-11-29 18:53:01 +01:00
uint16_t WS2812FX : : mode_tri_static_pattern ( )
{
uint8_t segSize = ( SEGMENT . intensity > > 5 ) + 1 ;
uint8_t currSeg = 0 ;
uint16_t currSegCount = 0 ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-11-29 18:53:01 +01:00
if ( currSeg % 3 = = 0 ) {
setPixelColor ( i , SEGCOLOR ( 0 ) ) ;
} else if ( currSeg % 3 = = 1 ) {
setPixelColor ( i , SEGCOLOR ( 1 ) ) ;
} else {
setPixelColor ( i , ( SEGCOLOR ( 2 ) > 0 ? SEGCOLOR ( 2 ) : WHITE ) ) ;
}
currSegCount + = 1 ;
if ( currSegCount > = segSize ) {
currSeg + = 1 ;
currSegCount = 0 ;
}
}
return FRAMETIME ;
}
2019-11-22 19:19:48 +01:00
2019-12-04 12:15:12 +01:00
uint16_t WS2812FX : : spots_base ( uint16_t threshold )
{
fill ( SEGCOLOR ( 1 ) ) ;
2020-03-30 12:56:51 +02:00
uint16_t maxZones = SEGLEN > > 2 ;
uint16_t zones = 1 + ( ( SEGMENT . intensity * maxZones ) > > 8 ) ;
2019-12-04 12:15:12 +01:00
uint16_t zoneLen = SEGLEN / zones ;
uint16_t offset = ( SEGLEN - zones * zoneLen ) > > 1 ;
2019-11-22 19:19:48 +01:00
2019-12-04 12:15:12 +01:00
for ( uint16_t z = 0 ; z < zones ; z + + )
{
uint16_t pos = offset + z * zoneLen ;
for ( uint16_t i = 0 ; i < zoneLen ; i + + )
{
uint16_t wave = triwave16 ( ( i * 0xFFFF ) / zoneLen ) ;
if ( wave > threshold ) {
2020-01-14 10:57:23 +01:00
uint16_t index = 0 + pos + i ;
2019-12-04 12:15:12 +01:00
uint8_t s = ( wave - threshold ) * 255 / ( 0xFFFF - threshold ) ;
setPixelColor ( index , color_blend ( color_from_palette ( index , true , PALETTE_SOLID_WRAP , 0 ) , SEGCOLOR ( 1 ) , 255 - s ) ) ;
}
}
}
return FRAMETIME ;
2019-11-22 19:19:48 +01:00
}
2019-11-25 01:20:03 +01:00
2019-12-04 12:15:12 +01:00
//Intensity slider sets number of "lights", speed sets LEDs per light
uint16_t WS2812FX : : mode_spots ( )
2019-11-22 19:19:48 +01:00
{
2019-12-04 12:15:12 +01:00
return spots_base ( ( 255 - SEGMENT . speed ) < < 8 ) ;
2019-11-25 01:20:03 +01:00
}
2019-11-22 19:19:48 +01:00
2019-11-18 12:29:36 +01:00
2019-12-04 12:15:12 +01:00
//Intensity slider sets number of "lights", LEDs per light fade in and out
uint16_t WS2812FX : : mode_spots_fade ( )
2019-11-18 12:29:36 +01:00
{
2019-12-04 12:15:12 +01:00
uint16_t counter = now * ( ( SEGMENT . speed > > 2 ) + 8 ) ;
uint16_t t = triwave16 ( counter ) ;
uint16_t tr = ( t > > 1 ) + ( t > > 2 ) ;
return spots_base ( tr ) ;
}
2019-12-10 20:06:00 +01:00
2020-01-05 21:26:14 +01:00
//each needs 12 bytes
typedef struct Ball {
unsigned long lastBounceTime ;
float impactVelocity ;
float height ;
} ball ;
2019-12-24 15:05:12 +01:00
/*
* Bouncing Balls Effect
*/
2020-01-05 21:26:14 +01:00
uint16_t WS2812FX : : mode_bouncing_balls ( void ) {
//allocate segment data
2020-03-30 12:56:51 +02:00
uint16_t maxNumBalls = 16 ;
uint16_t dataSize = sizeof ( ball ) * maxNumBalls ;
2020-01-05 21:26:14 +01:00
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
2019-12-30 00:20:05 +01:00
2020-01-05 21:26:14 +01:00
Ball * balls = reinterpret_cast < Ball * > ( SEGENV . data ) ;
2019-12-30 00:20:05 +01:00
2020-03-30 12:56:51 +02:00
// number of balls based on intensity setting to max of 7 (cycles colors)
2020-01-05 21:26:14 +01:00
// non-chosen color is a random color
2020-03-30 12:56:51 +02:00
uint8_t numBalls = int ( ( ( SEGMENT . intensity * ( maxNumBalls - 0.8f ) ) / 255 ) + 1 ) ;
2019-12-24 15:05:12 +01:00
2020-01-05 21:26:14 +01:00
float gravity = - 9.81 ; // standard value of gravity
float impactVelocityStart = sqrt ( - 2 * gravity ) ;
2019-12-24 15:05:12 +01:00
2020-01-05 21:26:14 +01:00
unsigned long time = millis ( ) ;
2019-12-24 15:05:12 +01:00
2020-01-05 21:26:14 +01:00
if ( SEGENV . call = = 0 ) {
2020-03-30 12:56:51 +02:00
for ( uint8_t i = 0 ; i < maxNumBalls ; i + + ) balls [ i ] . lastBounceTime = time ;
2019-12-24 15:05:12 +01:00
}
2020-01-05 21:26:14 +01:00
bool hasCol2 = SEGCOLOR ( 2 ) ;
fill ( hasCol2 ? BLACK : SEGCOLOR ( 1 ) ) ;
for ( uint8_t i = 0 ; i < numBalls ; i + + ) {
float timeSinceLastBounce = ( time - balls [ i ] . lastBounceTime ) / ( ( 255 - SEGMENT . speed ) * 8 / 256 + 1 ) ;
balls [ i ] . height = 0.5 * gravity * pow ( timeSinceLastBounce / 1000 , 2.0 ) + balls [ i ] . impactVelocity * timeSinceLastBounce / 1000 ;
if ( balls [ i ] . height < 0 ) { //start bounce
balls [ i ] . height = 0 ;
//damping for better effect using multiple balls
float dampening = 0.90 - float ( i ) / pow ( numBalls , 2 ) ;
balls [ i ] . impactVelocity = dampening * balls [ i ] . impactVelocity ;
balls [ i ] . lastBounceTime = time ;
if ( balls [ i ] . impactVelocity < 0.015 ) {
balls [ i ] . impactVelocity = impactVelocityStart ;
}
2019-12-24 15:05:12 +01:00
}
2020-01-05 21:26:14 +01:00
uint32_t color = SEGCOLOR ( 0 ) ;
if ( SEGMENT . palette ) {
2020-03-26 10:18:19 +01:00
color = color_wheel ( i * ( 256 / MAX ( numBalls , 8 ) ) ) ;
2020-01-05 21:26:14 +01:00
} else if ( hasCol2 ) {
color = SEGCOLOR ( i % NUM_COLORS ) ;
}
uint16_t pos = round ( balls [ i ] . height * ( SEGLEN - 1 ) ) ;
2020-01-14 10:57:23 +01:00
setPixelColor ( pos , color ) ;
2019-12-24 15:05:12 +01:00
}
2019-12-24 20:41:03 +01:00
return FRAMETIME ;
2019-12-24 15:05:12 +01:00
}
2020-01-05 21:26:14 +01:00
2019-12-24 15:05:12 +01:00
/*
* Sinelon stolen from FASTLED examples
*/
2019-12-24 20:41:03 +01:00
uint16_t WS2812FX : : sinelon_base ( bool dual , bool rainbow = false ) {
2019-12-24 15:05:12 +01:00
fade_out ( SEGMENT . intensity ) ;
2020-01-28 13:48:59 +01:00
uint16_t pos = beatsin16 ( SEGMENT . speed / 10 , 0 , SEGLEN - 1 ) ;
if ( SEGENV . call = = 0 ) SEGENV . aux0 = pos ;
2019-12-24 20:41:03 +01:00
uint32_t color1 = color_from_palette ( pos , true , false , 0 ) ;
2020-01-28 13:48:59 +01:00
uint32_t color2 = SEGCOLOR ( 2 ) ;
2019-12-24 20:41:03 +01:00
if ( rainbow ) {
2020-01-05 21:26:14 +01:00
color1 = color_wheel ( ( pos & 0x07 ) * 32 ) ;
2019-12-24 20:41:03 +01:00
}
2020-01-14 10:57:23 +01:00
setPixelColor ( pos , color1 ) ;
2019-12-24 20:41:03 +01:00
if ( dual ) {
2020-01-05 21:26:14 +01:00
if ( ! color2 ) color2 = color_from_palette ( pos , true , false , 0 ) ;
if ( rainbow ) color2 = color1 ; //rainbow
2020-01-14 10:57:23 +01:00
setPixelColor ( SEGLEN - 1 - pos , color2 ) ;
2019-12-24 15:05:12 +01:00
}
2020-01-28 13:48:59 +01:00
if ( SEGENV . aux0 ! = pos ) {
if ( SEGENV . aux0 < pos ) {
for ( uint16_t i = SEGENV . aux0 ; i < pos ; i + + ) {
setPixelColor ( i , color1 ) ;
if ( dual ) setPixelColor ( SEGLEN - 1 - i , color2 ) ;
}
} else {
for ( uint16_t i = SEGENV . aux0 ; i > pos ; i - - ) {
setPixelColor ( i , color1 ) ;
if ( dual ) setPixelColor ( SEGLEN - 1 - i , color2 ) ;
}
}
SEGENV . aux0 = pos ;
}
2019-12-30 00:20:05 +01:00
2019-12-24 15:05:12 +01:00
return FRAMETIME ;
}
2019-12-24 20:41:03 +01:00
uint16_t WS2812FX : : mode_sinelon ( void ) {
return sinelon_base ( false ) ;
}
uint16_t WS2812FX : : mode_sinelon_dual ( void ) {
return sinelon_base ( true ) ;
}
uint16_t WS2812FX : : mode_sinelon_rainbow ( void ) {
2021-02-24 20:23:32 +01:00
return sinelon_base ( false , true ) ;
2019-12-24 20:41:03 +01:00
}
2019-12-24 15:05:12 +01:00
2019-12-06 01:44:45 +01:00
//Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
uint16_t WS2812FX : : mode_glitter ( )
{
mode_palette ( ) ;
if ( SEGMENT . intensity > random8 ( ) )
{
2020-01-14 10:57:23 +01:00
setPixelColor ( random16 ( SEGLEN ) , ULTRAWHITE ) ;
2019-12-06 01:44:45 +01:00
}
return FRAMETIME ;
}
2020-01-05 21:26:14 +01:00
2021-02-28 22:54:30 +01:00
//each needs 11 bytes
2020-01-12 15:04:49 +01:00
//Spark type is used for popcorn, 1D fireworks, and drip
2020-01-05 21:26:14 +01:00
typedef struct Spark {
float pos ;
float vel ;
uint16_t col ;
uint8_t colIndex ;
} spark ;
2019-12-24 15:05:12 +01:00
/*
* POPCORN
2020-01-05 21:26:14 +01:00
* modified from https : //github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h
2019-12-24 15:05:12 +01:00
*/
uint16_t WS2812FX : : mode_popcorn ( void ) {
2020-01-05 21:26:14 +01:00
//allocate segment data
2021-09-12 13:31:47 +02:00
uint16_t maxNumPopcorn = 22 ; // max 22 on 16 segment ESP8266
2020-03-30 12:56:51 +02:00
uint16_t dataSize = sizeof ( spark ) * maxNumPopcorn ;
2020-01-05 21:26:14 +01:00
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
Spark * popcorn = reinterpret_cast < Spark * > ( SEGENV . data ) ;
float gravity = - 0.0001 - ( SEGMENT . speed / 200000.0 ) ; // m/s/s
gravity * = SEGLEN ;
2019-12-30 00:20:05 +01:00
2020-01-05 21:26:14 +01:00
bool hasCol2 = SEGCOLOR ( 2 ) ;
fill ( hasCol2 ? BLACK : SEGCOLOR ( 1 ) ) ;
2019-12-24 15:05:12 +01:00
2020-03-30 12:56:51 +02:00
uint8_t numPopcorn = SEGMENT . intensity * maxNumPopcorn / 255 ;
2020-01-05 21:26:14 +01:00
if ( numPopcorn = = 0 ) numPopcorn = 1 ;
2019-12-24 15:05:12 +01:00
2020-01-05 21:26:14 +01:00
for ( uint8_t i = 0 ; i < numPopcorn ; i + + ) {
bool isActive = popcorn [ i ] . pos > = 0.0f ;
2019-12-24 15:05:12 +01:00
2020-01-14 10:57:23 +01:00
if ( isActive ) { // if kernel is active, update its position
2020-01-05 21:26:14 +01:00
popcorn [ i ] . pos + = popcorn [ i ] . vel ;
popcorn [ i ] . vel + = gravity ;
uint32_t col = color_wheel ( popcorn [ i ] . colIndex ) ;
if ( ! SEGMENT . palette & & popcorn [ i ] . colIndex < NUM_COLORS ) col = SEGCOLOR ( popcorn [ i ] . colIndex ) ;
2019-12-30 00:20:05 +01:00
2020-01-14 10:57:23 +01:00
uint16_t ledIndex = popcorn [ i ] . pos ;
if ( ledIndex < SEGLEN ) setPixelColor ( ledIndex , col ) ;
2019-12-24 15:05:12 +01:00
} else { // if kernel is inactive, randomly pop it
2020-01-14 10:57:23 +01:00
if ( random8 ( ) < 2 ) { // POP!!!
2020-01-05 21:26:14 +01:00
popcorn [ i ] . pos = 0.01f ;
2019-12-30 00:20:05 +01:00
2020-01-05 21:26:14 +01:00
uint16_t peakHeight = 128 + random8 ( 128 ) ; //0-255
peakHeight = ( peakHeight * ( SEGLEN - 1 ) ) > > 8 ;
popcorn [ i ] . vel = sqrt ( - 2.0 * gravity * peakHeight ) ;
if ( SEGMENT . palette )
{
popcorn [ i ] . colIndex = random8 ( ) ;
} else {
byte col = random8 ( 0 , NUM_COLORS ) ;
if ( ! hasCol2 | | ! SEGCOLOR ( col ) ) col = 0 ;
popcorn [ i ] . colIndex = col ;
}
2019-12-24 15:05:12 +01:00
}
}
}
2019-12-24 20:41:03 +01:00
return FRAMETIME ;
2019-12-24 15:05:12 +01:00
}
2019-12-11 00:59:15 +01:00
//values close to 100 produce 5Hz flicker, which looks very candle-y
//Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel
//and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/
2020-04-22 00:51:00 +02:00
uint16_t WS2812FX : : candle ( bool multi )
2019-12-06 01:44:45 +01:00
{
2020-04-22 00:51:00 +02:00
if ( multi )
{
//allocate segment data
2021-09-12 13:31:47 +02:00
uint16_t dataSize = ( SEGLEN - 1 ) * 3 ; // max length of segment on 16 segment ESP8266 is 75 pixels
2020-04-22 00:51:00 +02:00
if ( ! SEGENV . allocateData ( dataSize ) ) return candle ( false ) ; //allocation failed
2019-12-06 01:44:45 +01:00
}
2020-04-22 00:51:00 +02:00
//max. flicker range controlled by intensity
uint8_t valrange = SEGMENT . intensity ;
2020-10-07 17:48:22 +02:00
uint8_t rndval = valrange > > 1 ; //max 127
2020-04-22 00:51:00 +02:00
//step (how much to move closer to target per frame) coarsely set by speed
uint8_t speedFactor = 4 ;
if ( SEGMENT . speed > 252 ) { //epilepsy
speedFactor = 1 ;
} else if ( SEGMENT . speed > 99 ) { //regular candle (mode called every ~25 ms, so 4 frames to have a new target every 100ms)
speedFactor = 2 ;
} else if ( SEGMENT . speed > 49 ) { //slower fade
speedFactor = 3 ;
} //else 4 (slowest)
2019-12-06 01:44:45 +01:00
2020-04-22 00:51:00 +02:00
uint16_t numCandles = ( multi ) ? SEGLEN : 1 ;
for ( uint16_t i = 0 ; i < numCandles ; i + + )
2019-12-06 01:44:45 +01:00
{
2020-04-22 00:51:00 +02:00
uint16_t d = 0 ; //data location
uint8_t s = SEGENV . aux0 , s_target = SEGENV . aux1 , fadeStep = SEGENV . step ;
if ( i > 0 ) {
d = ( i - 1 ) * 3 ;
s = SEGENV . data [ d ] ; s_target = SEGENV . data [ d + 1 ] ; fadeStep = SEGENV . data [ d + 2 ] ;
}
if ( fadeStep = = 0 ) { //init vals
s = 128 ; s_target = 130 + random8 ( 4 ) ; fadeStep = 1 ;
}
bool newTarget = false ;
if ( s_target > s ) { //fade up
s = qadd8 ( s , fadeStep ) ;
if ( s > = s_target ) newTarget = true ;
} else {
s = qsub8 ( s , fadeStep ) ;
if ( s < = s_target ) newTarget = true ;
}
if ( newTarget ) {
2020-10-07 17:48:22 +02:00
s_target = random8 ( rndval ) + random8 ( rndval ) ; //between 0 and rndval*2 -2 = 252
2020-04-22 00:51:00 +02:00
if ( s_target < ( rndval > > 1 ) ) s_target = ( rndval > > 1 ) + random8 ( rndval ) ;
2020-10-07 17:48:22 +02:00
uint8_t offset = ( 255 - valrange ) ;
2020-04-22 00:51:00 +02:00
s_target + = offset ;
uint8_t dif = ( s_target > s ) ? s_target - s : s - s_target ;
2019-12-06 01:44:45 +01:00
2020-04-22 00:51:00 +02:00
fadeStep = dif > > speedFactor ;
if ( fadeStep = = 0 ) fadeStep = 1 ;
}
if ( i > 0 ) {
setPixelColor ( i , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) , s ) ) ;
SEGENV . data [ d ] = s ; SEGENV . data [ d + 1 ] = s_target ; SEGENV . data [ d + 2 ] = fadeStep ;
} else {
for ( uint16_t j = 0 ; j < SEGLEN ; j + + ) {
setPixelColor ( j , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( j , true , PALETTE_SOLID_WRAP , 0 ) , s ) ) ;
}
SEGENV . aux0 = s ; SEGENV . aux1 = s_target ; SEGENV . step = fadeStep ;
}
2019-12-06 01:44:45 +01:00
}
return FRAMETIME ;
}
2019-12-27 20:58:06 +01:00
2020-04-22 00:51:00 +02:00
uint16_t WS2812FX : : mode_candle ( )
{
return candle ( false ) ;
}
uint16_t WS2812FX : : mode_candle_multi ( )
{
return candle ( true ) ;
}
2019-12-27 20:58:06 +01:00
2019-12-30 00:04:56 +01:00
/*
/ Fireworks in starburst effect
/ based on the video : https : //www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
/ Speed sets frequency of new starbursts , intensity is the intensity of the burst
*/
2021-08-22 22:16:25 +02:00
# ifdef ESP8266
# define STARBURST_MAX_FRAG 4
# define STARBURST_MAX_STARS 6
# else
# define STARBURST_MAX_FRAG 10
# define STARBURST_MAX_STARS 11
# endif
//each needs 18+STARBURST_MAX_FRAG*4 bytes
2019-12-31 19:01:37 +01:00
typedef struct particle {
2019-12-28 15:43:55 +01:00
CRGB color ;
uint32_t birth = 0 ;
uint32_t last = 0 ;
2019-12-31 13:47:17 +01:00
float vel = 0 ;
2019-12-30 00:08:49 +01:00
uint16_t pos = - 1 ;
2019-12-31 12:35:18 +01:00
float fragment [ STARBURST_MAX_FRAG ] ;
2019-12-28 15:43:55 +01:00
} star ;
uint16_t WS2812FX : : mode_starburst ( void ) {
2021-09-12 13:31:47 +02:00
uint8_t numStars = min ( 1 + ( SEGLEN > > 3 ) , STARBURST_MAX_STARS ) ; // 11 * 58 * 32 = 19k (ESP32), 6 * 34 * 16 = 3.2k (ESP8266)
2020-01-02 02:12:10 +01:00
uint16_t dataSize = sizeof ( star ) * numStars ;
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
2019-12-28 15:43:55 +01:00
uint32_t it = millis ( ) ;
2020-01-02 02:12:10 +01:00
star * stars = reinterpret_cast < star * > ( SEGENV . data ) ;
2020-03-30 12:56:51 +02:00
float maxSpeed = 375.0f ; // Max velocity
2019-12-31 19:01:37 +01:00
float particleIgnition = 250.0f ; // How long to "flash"
float particleFadeTime = 1500.0f ; // Fade out time
2019-12-28 15:43:55 +01:00
for ( int j = 0 ; j < numStars ; j + + )
{
2020-03-30 12:56:51 +02:00
// speed to adjust chance of a burst, max is nearly always.
2019-12-31 16:37:44 +01:00
if ( random8 ( ( 144 - ( SEGMENT . speed > > 1 ) ) ) = = 0 & & stars [ j ] . birth = = 0 )
2019-12-28 15:43:55 +01:00
{
// Pick a random color and location.
2019-12-31 12:35:18 +01:00
uint16_t startPos = random16 ( SEGLEN - 1 ) ;
2019-12-31 19:01:37 +01:00
float multiplier = ( float ) ( random8 ( ) ) / 255.0 * 1.0 ;
2019-12-28 15:43:55 +01:00
2019-12-31 16:37:44 +01:00
stars [ j ] . color = col_to_crgb ( color_wheel ( random8 ( ) ) ) ;
2019-12-28 15:43:55 +01:00
stars [ j ] . pos = startPos ;
2020-03-30 12:56:51 +02:00
stars [ j ] . vel = maxSpeed * ( float ) ( random8 ( ) ) / 255.0 * multiplier ;
2019-12-28 15:43:55 +01:00
stars [ j ] . birth = it ;
stars [ j ] . last = it ;
2019-12-30 00:08:49 +01:00
// more fragments means larger burst effect
2019-12-31 19:01:37 +01:00
int num = random8 ( 3 , 6 + ( SEGMENT . intensity > > 5 ) ) ;
2019-12-28 15:43:55 +01:00
2019-12-31 12:35:18 +01:00
for ( int i = 0 ; i < STARBURST_MAX_FRAG ; i + + ) {
2019-12-28 15:43:55 +01:00
if ( i < num ) stars [ j ] . fragment [ i ] = startPos ;
else stars [ j ] . fragment [ i ] = - 1 ;
}
}
}
2019-12-31 16:37:44 +01:00
fill ( SEGCOLOR ( 1 ) ) ;
2019-12-28 15:43:55 +01:00
for ( int j = 0 ; j < numStars ; j + + )
{
2019-12-30 00:08:49 +01:00
if ( stars [ j ] . birth ! = 0 ) {
2019-12-28 15:43:55 +01:00
float dt = ( it - stars [ j ] . last ) / 1000.0 ;
2019-12-31 12:35:18 +01:00
for ( int i = 0 ; i < STARBURST_MAX_FRAG ; i + + ) {
2019-12-31 16:37:44 +01:00
int var = i > > 1 ;
2019-12-30 00:08:49 +01:00
2019-12-28 15:43:55 +01:00
if ( stars [ j ] . fragment [ i ] > 0 ) {
2019-12-31 19:01:37 +01:00
//all fragments travel right, will be mirrored on other side
stars [ j ] . fragment [ i ] + = stars [ j ] . vel * dt * ( float ) var / 3.0 ;
2019-12-28 15:43:55 +01:00
}
}
stars [ j ] . last = it ;
2019-12-30 00:08:49 +01:00
stars [ j ] . vel - = 3 * stars [ j ] . vel * dt ;
2019-12-28 15:43:55 +01:00
}
CRGB c = stars [ j ] . color ;
// If the star is brand new, it flashes white briefly.
// Otherwise it just fades over time.
float fade = 0.0f ;
2019-12-31 16:37:44 +01:00
float age = it - stars [ j ] . birth ;
2019-12-30 00:08:49 +01:00
2019-12-31 19:01:37 +01:00
if ( age < particleIgnition ) {
c = col_to_crgb ( color_blend ( WHITE , crgb_to_col ( c ) , 254.5f * ( ( age / particleIgnition ) ) ) ) ;
2019-12-28 15:43:55 +01:00
} else {
// Figure out how much to fade and shrink the star based on
// its age relative to its lifetime
2019-12-31 19:01:37 +01:00
if ( age > particleIgnition + particleFadeTime ) {
2019-12-31 16:37:44 +01:00
fade = 1.0f ; // Black hole, all faded out
stars [ j ] . birth = 0 ;
c = col_to_crgb ( SEGCOLOR ( 1 ) ) ;
2019-12-28 15:43:55 +01:00
} else {
2019-12-31 19:01:37 +01:00
age - = particleIgnition ;
fade = ( age / particleFadeTime ) ; // Fading star
2019-12-31 16:37:44 +01:00
byte f = 254.5f * fade ;
c = col_to_crgb ( color_blend ( crgb_to_col ( c ) , SEGCOLOR ( 1 ) , f ) ) ;
2019-12-28 15:43:55 +01:00
}
}
2019-12-31 16:37:44 +01:00
2019-12-31 19:01:37 +01:00
float particleSize = ( 1.0 - fade ) * 2 ;
2019-12-28 15:43:55 +01:00
2019-12-31 19:01:37 +01:00
for ( uint8_t index = 0 ; index < STARBURST_MAX_FRAG * 2 ; index + + ) {
bool mirrored = index & 0x1 ;
uint8_t i = index > > 1 ;
2019-12-28 15:43:55 +01:00
if ( stars [ j ] . fragment [ i ] > 0 ) {
2019-12-31 19:01:37 +01:00
float loc = stars [ j ] . fragment [ i ] ;
if ( mirrored ) loc - = ( loc - stars [ j ] . pos ) * 2 ;
int start = loc - particleSize ;
int end = loc + particleSize ;
2019-12-31 16:37:44 +01:00
if ( start < 0 ) start = 0 ;
if ( start = = end ) end + + ;
2019-12-31 12:35:18 +01:00
if ( end > SEGLEN ) end = SEGLEN ;
for ( int p = start ; p < end ; p + + ) {
2020-01-14 10:57:23 +01:00
setPixelColor ( p , c . r , c . g , c . b ) ;
2019-12-28 15:43:55 +01:00
}
}
}
}
return FRAMETIME ;
2019-12-31 12:35:18 +01:00
}
2021-08-22 22:16:25 +02:00
# undef STARBURST_MAX_FRAG
# undef STARBURST_MAX_STARS
2020-01-03 12:58:31 +01:00
2019-12-27 20:58:06 +01:00
/*
* Exploding fireworks effect
* adapted from : http : //www.anirama.com/1000leds/1d-fireworks/
*/
2021-08-22 22:16:25 +02:00
# ifdef ESP8266
2021-09-12 13:31:47 +02:00
# define MAX_SPARKS 20 // number of fragments (11 bytes per fragment)
2021-08-22 22:16:25 +02:00
# else
# define MAX_SPARKS 58 // number of fragments
# endif
2019-12-27 20:58:06 +01:00
uint16_t WS2812FX : : mode_exploding_fireworks ( void )
{
2020-01-03 17:47:06 +01:00
//allocate segment data
2021-09-18 13:15:19 +02:00
uint16_t numSparks = min ( 2 + ( SEGLEN > > 1 ) , MAX_SPARKS ) ; // max 58 for 32 segment ESP32, 20 for 16 segment ESP8266
2020-01-03 17:47:06 +01:00
uint16_t dataSize = sizeof ( spark ) * numSparks ;
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
2019-12-27 20:58:06 +01:00
2020-01-03 17:47:06 +01:00
fill ( BLACK ) ;
2020-04-24 23:35:39 +02:00
bool actuallyReverse = SEGMENT . getOption ( SEG_OPTION_REVERSED ) ;
2020-01-03 22:56:56 +01:00
//have fireworks start in either direction based on intensity
2020-04-24 23:35:39 +02:00
SEGMENT . setOption ( SEG_OPTION_REVERSED , SEGENV . step ) ;
2020-01-03 22:56:56 +01:00
2020-01-03 17:47:06 +01:00
Spark * sparks = reinterpret_cast < Spark * > ( SEGENV . data ) ;
Spark * flare = sparks ; //first spark is flare data
2019-12-27 20:58:06 +01:00
2020-01-03 22:56:56 +01:00
float gravity = - 0.0004 - ( SEGMENT . speed / 800000.0 ) ; // m/s/s
gravity * = SEGLEN ;
2020-01-03 17:47:06 +01:00
2020-01-03 22:56:56 +01:00
if ( SEGENV . aux0 < 2 ) { //FLARE
2020-01-03 17:47:06 +01:00
if ( SEGENV . aux0 = = 0 ) { //init flare
flare - > pos = 0 ;
2020-01-03 22:56:56 +01:00
uint16_t peakHeight = 75 + random8 ( 180 ) ; //0-255
peakHeight = ( peakHeight * ( SEGLEN - 1 ) ) > > 8 ;
flare - > vel = sqrt ( - 2.0 * gravity * peakHeight ) ;
flare - > col = 255 ; //brightness
2020-01-03 17:47:06 +01:00
SEGENV . aux0 = 1 ;
2019-12-27 20:58:06 +01:00
}
2020-01-03 17:47:06 +01:00
// launch
2020-01-03 22:56:56 +01:00
if ( flare - > vel > 12 * gravity ) {
2020-01-03 17:47:06 +01:00
// flare
2020-01-14 10:57:23 +01:00
setPixelColor ( int ( flare - > pos ) , flare - > col , flare - > col , flare - > col ) ;
2019-12-27 20:58:06 +01:00
2020-01-03 17:47:06 +01:00
flare - > pos + = flare - > vel ;
flare - > pos = constrain ( flare - > pos , 0 , SEGLEN - 1 ) ;
flare - > vel + = gravity ;
2020-01-03 22:56:56 +01:00
flare - > col - = 2 ;
2020-01-03 17:47:06 +01:00
} else {
2020-01-03 22:56:56 +01:00
SEGENV . aux0 = 2 ; // ready to explode
2019-12-27 20:58:06 +01:00
}
2020-01-03 22:56:56 +01:00
} else if ( SEGENV . aux0 < 4 ) {
2020-01-03 17:47:06 +01:00
/*
* Explode !
*
* Explosion happens where the flare ended .
* Size is proportional to the height .
*/
2020-01-03 22:56:56 +01:00
int nSparks = flare - > pos ;
2020-01-03 17:47:06 +01:00
nSparks = constrain ( nSparks , 0 , numSparks ) ;
static float dying_gravity ;
// initialize sparks
2020-01-03 22:56:56 +01:00
if ( SEGENV . aux0 = = 2 ) {
2020-01-03 17:47:06 +01:00
for ( int i = 1 ; i < nSparks ; i + + ) {
sparks [ i ] . pos = flare - > pos ;
2020-01-03 22:56:56 +01:00
sparks [ i ] . vel = ( float ( random16 ( 0 , 20000 ) ) / 10000.0 ) - 0.9 ; // from -0.9 to 1.1
sparks [ i ] . col = 345 ; //abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
//sparks[i].col = constrain(sparks[i].col, 0, 345);
sparks [ i ] . colIndex = random8 ( ) ;
sparks [ i ] . vel * = flare - > pos / SEGLEN ; // proportional to height
sparks [ i ] . vel * = - gravity * 50 ;
2020-01-03 17:47:06 +01:00
}
2020-01-03 22:56:56 +01:00
//sparks[1].col = 345; // this will be our known spark
dying_gravity = gravity / 2 ;
SEGENV . aux0 = 3 ;
2020-01-03 17:47:06 +01:00
}
2020-01-03 22:56:56 +01:00
if ( sparks [ 1 ] . col > 4 ) { //&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks
2020-01-03 17:47:06 +01:00
for ( int i = 1 ; i < nSparks ; i + + ) {
sparks [ i ] . pos + = sparks [ i ] . vel ;
sparks [ i ] . vel + = dying_gravity ;
2020-01-03 22:56:56 +01:00
if ( sparks [ i ] . col > 3 ) sparks [ i ] . col - = 4 ;
if ( sparks [ i ] . pos > 0 & & sparks [ i ] . pos < SEGLEN ) {
uint16_t prog = sparks [ i ] . col ;
uint32_t spColor = ( SEGMENT . palette ) ? color_wheel ( sparks [ i ] . colIndex ) : SEGCOLOR ( 0 ) ;
CRGB c = CRGB : : Black ; //HeatColor(sparks[i].col);
if ( prog > 300 ) { //fade from white to spark color
c = col_to_crgb ( color_blend ( spColor , WHITE , ( prog - 300 ) * 5 ) ) ;
} else if ( prog > 45 ) { //fade from spark color to black
c = col_to_crgb ( color_blend ( BLACK , spColor , prog - 45 ) ) ;
uint8_t cooling = ( 300 - prog ) > > 5 ;
c . g = qsub8 ( c . g , cooling ) ;
c . b = qsub8 ( c . b , cooling * 2 ) ;
}
2020-01-14 10:57:23 +01:00
setPixelColor ( int ( sparks [ i ] . pos ) , c . red , c . green , c . blue ) ;
2020-01-03 17:47:06 +01:00
}
2019-12-27 20:58:06 +01:00
}
2020-01-03 22:56:56 +01:00
dying_gravity * = .99 ; // as sparks burn out they fall slower
2020-01-03 17:47:06 +01:00
} else {
2020-01-03 22:56:56 +01:00
SEGENV . aux0 = 6 + random8 ( 10 ) ; //wait for this many frames
}
} else {
SEGENV . aux0 - - ;
if ( SEGENV . aux0 < 4 ) {
SEGENV . aux0 = 0 ; //back to flare
2021-02-24 20:23:32 +01:00
SEGENV . step = actuallyReverse ^ ( SEGMENT . intensity > random8 ( ) ) ; //decide firing side
2019-12-27 20:58:06 +01:00
}
}
2020-01-03 22:56:56 +01:00
2020-04-24 23:35:39 +02:00
SEGMENT . setOption ( SEG_OPTION_REVERSED , actuallyReverse ) ;
2020-01-03 22:56:56 +01:00
2020-01-03 17:47:06 +01:00
return FRAMETIME ;
2020-01-03 12:58:31 +01:00
}
2021-08-22 22:16:25 +02:00
# undef MAX_SPARKS
2020-01-12 15:04:49 +01:00
/*
* Drip Effect
* ported of : https : //www.youtube.com/watch?v=sru2fXh4r7k
*/
uint16_t WS2812FX : : mode_drip ( void )
{
//allocate segment data
2021-04-06 22:30:23 +02:00
uint8_t numDrops = 4 ;
2020-01-12 15:04:49 +01:00
uint16_t dataSize = sizeof ( spark ) * numDrops ;
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
fill ( SEGCOLOR ( 1 ) ) ;
Spark * drops = reinterpret_cast < Spark * > ( SEGENV . data ) ;
2021-04-06 22:30:23 +02:00
numDrops = 1 + ( SEGMENT . intensity > > 6 ) ; // 255>>6 = 3
2020-01-19 00:06:17 +01:00
2021-05-11 23:21:57 +02:00
float gravity = - 0.0005 - ( SEGMENT . speed / 50000.0 ) ;
2020-01-12 15:04:49 +01:00
gravity * = SEGLEN ;
int sourcedrop = 12 ;
2021-04-06 22:30:23 +02:00
for ( uint8_t j = 0 ; j < numDrops ; j + + ) {
2020-01-12 15:04:49 +01:00
if ( drops [ j ] . colIndex = = 0 ) { //init
drops [ j ] . pos = SEGLEN - 1 ; // start at end
drops [ j ] . vel = 0 ; // speed
drops [ j ] . col = sourcedrop ; // brightness
2020-03-30 12:56:51 +02:00
drops [ j ] . colIndex = 1 ; // drop state (0 init, 1 forming, 2 falling, 5 bouncing)
2020-01-12 15:04:49 +01:00
}
2020-01-14 19:12:23 +01:00
setPixelColor ( SEGLEN - 1 , color_blend ( BLACK , SEGCOLOR ( 0 ) , sourcedrop ) ) ; // water source
2020-01-12 15:04:49 +01:00
if ( drops [ j ] . colIndex = = 1 ) {
if ( drops [ j ] . col > 255 ) drops [ j ] . col = 255 ;
2021-04-06 22:30:23 +02:00
setPixelColor ( uint16_t ( drops [ j ] . pos ) , color_blend ( BLACK , SEGCOLOR ( 0 ) , drops [ j ] . col ) ) ;
2020-01-12 15:04:49 +01:00
2020-01-19 00:06:17 +01:00
drops [ j ] . col + = map ( SEGMENT . speed , 0 , 255 , 1 , 6 ) ; // swelling
2020-01-12 15:04:49 +01:00
if ( random8 ( ) < drops [ j ] . col / 10 ) { // random drop
drops [ j ] . colIndex = 2 ; //fall
drops [ j ] . col = 255 ;
}
}
if ( drops [ j ] . colIndex > 1 ) { // falling
if ( drops [ j ] . pos > 0 ) { // fall until end of segment
drops [ j ] . pos + = drops [ j ] . vel ;
if ( drops [ j ] . pos < 0 ) drops [ j ] . pos = 0 ;
2021-04-06 22:30:23 +02:00
drops [ j ] . vel + = gravity ; // gravity is negative
2020-01-12 15:04:49 +01:00
2021-04-03 17:18:29 +02:00
for ( uint16_t i = 1 ; i < 7 - drops [ j ] . colIndex ; i + + ) { // some minor math so we don't expand bouncing droplets
2021-04-05 16:08:35 +02:00
uint16_t pos = constrain ( uint16_t ( drops [ j ] . pos ) + i , 0 , SEGLEN - 1 ) ; //this is BAD, returns a pos >= SEGLEN occasionally
2021-04-03 17:18:29 +02:00
setPixelColor ( pos , color_blend ( BLACK , SEGCOLOR ( 0 ) , drops [ j ] . col / i ) ) ; //spread pixel with fade while falling
2020-01-12 15:04:49 +01:00
}
2021-04-06 22:30:23 +02:00
2020-01-12 15:04:49 +01:00
if ( drops [ j ] . colIndex > 2 ) { // during bounce, some water is on the floor
2020-01-14 19:12:23 +01:00
setPixelColor ( 0 , color_blend ( SEGCOLOR ( 0 ) , BLACK , drops [ j ] . col ) ) ;
2020-01-12 15:04:49 +01:00
}
} else { // we hit bottom
2020-03-30 12:56:51 +02:00
if ( drops [ j ] . colIndex > 2 ) { // already hit once, so back to forming
2020-01-12 15:04:49 +01:00
drops [ j ] . colIndex = 0 ;
drops [ j ] . col = sourcedrop ;
} else {
if ( drops [ j ] . colIndex = = 2 ) { // init bounce
drops [ j ] . vel = - drops [ j ] . vel / 4 ; // reverse velocity with damping
drops [ j ] . pos + = drops [ j ] . vel ;
}
drops [ j ] . col = sourcedrop * 2 ;
drops [ j ] . colIndex = 5 ; // bouncing
}
}
}
}
return FRAMETIME ;
}
2020-01-19 00:06:17 +01:00
2021-02-07 14:45:34 +01:00
/*
* Tetris or Stacking ( falling bricks ) Effect
* by Blaz Kristan ( https : //github.com/blazoncek, https://blaz.at/home)
*/
2021-08-22 22:16:25 +02:00
//12 bytes
2021-02-07 14:45:34 +01:00
typedef struct Tetris {
float pos ;
float speed ;
uint32_t col ;
} tetris ;
2021-02-10 00:37:05 +01:00
uint16_t WS2812FX : : mode_tetrix ( void ) {
2021-02-07 14:45:34 +01:00
uint16_t dataSize = sizeof ( tetris ) ;
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
Tetris * drop = reinterpret_cast < Tetris * > ( SEGENV . data ) ;
// initialize dropping on first call or segment full
if ( SEGENV . call = = 0 | | SEGENV . aux1 > = SEGLEN ) {
SEGENV . aux1 = 0 ; // reset brick stack size
SEGENV . step = 0 ;
fill ( SEGCOLOR ( 1 ) ) ;
return 250 ; // short wait
}
if ( SEGENV . step = = 0 ) { //init
2021-03-02 11:00:07 +01:00
drop - > speed = 0.0238 * ( SEGMENT . speed ? ( SEGMENT . speed > > 2 ) + 1 : random8 ( 6 , 64 ) ) ; // set speed
2021-02-24 20:23:32 +01:00
drop - > pos = SEGLEN ; // start at end of segment (no need to subtract 1)
2021-02-07 14:45:34 +01:00
drop - > col = color_from_palette ( random8 ( 0 , 15 ) < < 4 , false , false , 0 ) ; // limit color choices so there is enough HUE gap
SEGENV . step = 1 ; // drop state (0 init, 1 forming, 2 falling)
SEGENV . aux0 = ( SEGMENT . intensity ? ( SEGMENT . intensity > > 5 ) + 1 : random8 ( 1 , 5 ) ) * ( 1 + ( SEGLEN > > 6 ) ) ; // size of brick
}
if ( SEGENV . step = = 1 ) { // forming
if ( random8 ( ) > > 6 ) { // random drop
SEGENV . step = 2 ; // fall
}
}
if ( SEGENV . step > 1 ) { // falling
if ( drop - > pos > SEGENV . aux1 ) { // fall until top of stack
drop - > pos - = drop - > speed ; // may add gravity as: speed += gravity
if ( int ( drop - > pos ) < SEGENV . aux1 ) drop - > pos = SEGENV . aux1 ;
for ( uint16_t i = int ( drop - > pos ) ; i < SEGLEN ; i + + ) setPixelColor ( i , i < int ( drop - > pos ) + SEGENV . aux0 ? drop - > col : SEGCOLOR ( 1 ) ) ;
} else { // we hit bottom
SEGENV . step = 0 ; // go back to init
SEGENV . aux1 + = SEGENV . aux0 ; // increase the stack size
if ( SEGENV . aux1 > = SEGLEN ) return 1000 ; // wait for a second
}
}
return FRAMETIME ;
}
2020-01-19 00:06:17 +01:00
/*
/ Plasma Effect
/ adapted from https : //github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
*/
uint16_t WS2812FX : : mode_plasma ( void ) {
2021-03-01 19:50:14 +01:00
// initialize phases on start
if ( SEGENV . call = = 0 ) {
SEGENV . aux0 = random8 ( 0 , 2 ) ; // add a bit of randomness
}
uint8_t thisPhase = beatsin8 ( 6 + SEGENV . aux0 , - 64 , 64 ) ;
uint8_t thatPhase = beatsin8 ( 7 + SEGENV . aux0 , - 64 , 64 ) ;
2020-01-19 00:06:17 +01:00
for ( int i = 0 ; i < SEGLEN ; i + + ) { // For each of the LED's in the strand, set color & brightness based on a wave as follows:
2021-03-01 19:50:14 +01:00
uint8_t colorIndex = cubicwave8 ( ( i * ( 2 + 3 * ( SEGMENT . speed > > 5 ) ) + thisPhase ) & 0xFF ) / 2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8 ( ( i * ( 1 + 2 * ( SEGMENT . speed > > 5 ) ) + thatPhase ) & 0xFF ) / 2 ; // factor=15 // Hey, you can even change the frequencies if you wish.
uint8_t thisBright = qsub8 ( colorIndex , beatsin8 ( 7 , 0 , ( 128 - ( SEGMENT . intensity > > 1 ) ) ) ) ;
2020-01-24 23:17:30 +01:00
CRGB color = ColorFromPalette ( currentPalette , colorIndex , thisBright , LINEARBLEND ) ;
setPixelColor ( i , color . red , color . green , color . blue ) ;
2020-01-19 00:06:17 +01:00
}
return FRAMETIME ;
}
2020-01-19 13:51:49 +01:00
2020-03-25 11:17:45 +01:00
2020-01-19 13:51:49 +01:00
/*
* Percentage display
* Intesity values from 0 - 100 turn on the leds .
*/
uint16_t WS2812FX : : mode_percent ( void ) {
2020-03-26 10:18:19 +01:00
uint8_t percent = MAX ( 0 , MIN ( 200 , SEGMENT . intensity ) ) ;
2020-02-06 01:36:01 +01:00
uint16_t active_leds = ( percent < 100 ) ? SEGLEN * percent / 100.0
: SEGLEN * ( 200 - percent ) / 100.0 ;
2020-01-28 13:47:18 +01:00
2020-02-18 18:52:47 +01:00
uint8_t size = ( 1 + ( ( SEGMENT . speed * SEGLEN ) > > 11 ) ) ;
if ( SEGMENT . speed = = 255 ) size = 255 ;
2020-01-28 13:47:18 +01:00
2020-02-06 01:36:01 +01:00
if ( percent < 100 ) {
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
if ( i < SEGENV . step ) {
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
}
else {
setPixelColor ( i , SEGCOLOR ( 1 ) ) ;
}
}
} else {
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
if ( i < ( SEGLEN - SEGENV . step ) ) {
setPixelColor ( i , SEGCOLOR ( 1 ) ) ;
}
else {
setPixelColor ( i , color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) ) ;
}
}
}
if ( active_leds > SEGENV . step ) { // smooth transition to the target value
2020-01-28 13:47:18 +01:00
SEGENV . step + = size ;
if ( SEGENV . step > active_leds ) SEGENV . step = active_leds ;
} else if ( active_leds < SEGENV . step ) {
2020-01-29 23:10:02 +01:00
if ( SEGENV . step > size ) SEGENV . step - = size ; else SEGENV . step = 0 ;
2020-01-28 13:47:18 +01:00
if ( SEGENV . step < active_leds ) SEGENV . step = active_leds ;
}
2020-01-19 13:51:49 +01:00
2020-01-28 13:47:18 +01:00
return FRAMETIME ;
2020-02-17 11:01:05 +01:00
}
2020-02-18 20:52:12 +01:00
/*
/ Modulates the brightness similar to a heartbeat
*/
2020-02-17 11:01:05 +01:00
uint16_t WS2812FX : : mode_heartbeat ( void ) {
uint8_t bpm = 40 + ( SEGMENT . speed > > 4 ) ;
uint32_t msPerBeat = ( 60000 / bpm ) ;
uint32_t secondBeat = ( msPerBeat / 3 ) ;
2020-02-18 18:52:47 +01:00
uint32_t bri_lower = SEGENV . aux1 ;
bri_lower = bri_lower * 2042 / ( 2048 + SEGMENT . intensity ) ;
SEGENV . aux1 = bri_lower ;
2020-02-17 11:01:05 +01:00
2020-02-18 18:52:47 +01:00
unsigned long beatTimer = millis ( ) - SEGENV . step ;
if ( ( beatTimer > secondBeat ) & & ! SEGENV . aux0 ) { // time for the second beat?
SEGENV . aux1 = UINT16_MAX ; //full bri
SEGENV . aux0 = 1 ;
2020-02-17 11:01:05 +01:00
}
if ( beatTimer > msPerBeat ) { // time to reset the beat timer?
2020-02-18 18:52:47 +01:00
SEGENV . aux1 = UINT16_MAX ; //full bri
SEGENV . aux0 = 0 ;
SEGENV . step = millis ( ) ;
}
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
setPixelColor ( i , color_blend ( color_from_palette ( i , true , PALETTE_SOLID_WRAP , 0 ) , SEGCOLOR ( 1 ) , 255 - ( SEGENV . aux1 > > 8 ) ) ) ;
2020-02-17 11:01:05 +01:00
}
return FRAMETIME ;
2020-02-29 18:24:51 +01:00
}
2020-03-25 11:17:45 +01:00
// "Pacifica"
// Gentle, blue-green ocean waves.
// December 2019, Mark Kriegsman and Mary Corey March.
// For Dan.
//
//
// In this animation, there are four "layers" of waves of light.
//
// Each layer moves independently, and each is scaled separately.
//
// All four wave layers are added together on top of each other, and then
// another filter is applied that adds "whitecaps" of brightness where the
// waves line up with each other more. Finally, another pass is taken
// over the led array to 'deepen' (dim) the blues and greens.
//
// The speed and scale and motion each layer varies slowly within independent
// hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions
// with a lot of oddly specific numeric ranges.
//
// These three custom blue-green color palettes were inspired by the colors found in
// the waters off the southern coast of California, https://goo.gl/maps/QQgd97jjHesHZVxQ7
//
// Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino
//
uint16_t WS2812FX : : mode_pacifica ( )
{
2020-08-25 17:23:17 +02:00
uint32_t nowOld = now ;
2020-03-25 11:17:45 +01:00
CRGBPalette16 pacifica_palette_1 =
{ 0x000507 , 0x000409 , 0x00030B , 0x00030D , 0x000210 , 0x000212 , 0x000114 , 0x000117 ,
0x000019 , 0x00001C , 0x000026 , 0x000031 , 0x00003B , 0x000046 , 0x14554B , 0x28AA50 } ;
CRGBPalette16 pacifica_palette_2 =
{ 0x000507 , 0x000409 , 0x00030B , 0x00030D , 0x000210 , 0x000212 , 0x000114 , 0x000117 ,
0x000019 , 0x00001C , 0x000026 , 0x000031 , 0x00003B , 0x000046 , 0x0C5F52 , 0x19BE5F } ;
CRGBPalette16 pacifica_palette_3 =
{ 0x000208 , 0x00030E , 0x000514 , 0x00061A , 0x000820 , 0x000927 , 0x000B2D , 0x000C33 ,
0x000E39 , 0x001040 , 0x001450 , 0x001860 , 0x001C70 , 0x002080 , 0x1040BF , 0x2060FF } ;
2020-04-22 00:51:00 +02:00
if ( SEGMENT . palette ) {
pacifica_palette_1 = currentPalette ;
pacifica_palette_2 = currentPalette ;
pacifica_palette_3 = currentPalette ;
}
2020-03-25 11:17:45 +01:00
// Increment the four "color index start" counters, one for each wave layer.
// Each is incremented at a different speed, and the speeds vary over time.
uint16_t sCIStart1 = SEGENV . aux0 , sCIStart2 = SEGENV . aux1 , sCIStart3 = SEGENV . step , sCIStart4 = SEGENV . step > > 16 ;
//static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4;
2020-08-25 17:23:17 +02:00
//uint32_t deltams = 26 + (SEGMENT.speed >> 3);
uint32_t deltams = ( FRAMETIME > > 2 ) + ( ( FRAMETIME * SEGMENT . speed ) > > 7 ) ;
uint64_t deltat = ( now > > 2 ) + ( ( now * SEGMENT . speed ) > > 7 ) ;
now = deltat ;
2020-03-25 11:17:45 +01:00
uint16_t speedfactor1 = beatsin16 ( 3 , 179 , 269 ) ;
uint16_t speedfactor2 = beatsin16 ( 4 , 179 , 269 ) ;
uint32_t deltams1 = ( deltams * speedfactor1 ) / 256 ;
uint32_t deltams2 = ( deltams * speedfactor2 ) / 256 ;
uint32_t deltams21 = ( deltams1 + deltams2 ) / 2 ;
sCIStart1 + = ( deltams1 * beatsin88 ( 1011 , 10 , 13 ) ) ;
sCIStart2 - = ( deltams21 * beatsin88 ( 777 , 8 , 11 ) ) ;
sCIStart3 - = ( deltams1 * beatsin88 ( 501 , 5 , 7 ) ) ;
sCIStart4 - = ( deltams2 * beatsin88 ( 257 , 4 , 6 ) ) ;
SEGENV . aux0 = sCIStart1 ; SEGENV . aux1 = sCIStart2 ;
SEGENV . step = sCIStart4 ; SEGENV . step = ( SEGENV . step < < 16 ) + sCIStart3 ;
// Clear out the LED array to a dim background blue-green
//fill(132618);
uint8_t basethreshold = beatsin8 ( 9 , 55 , 65 ) ;
uint8_t wave = beat8 ( 7 ) ;
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
CRGB c = CRGB ( 2 , 6 , 10 ) ;
// Render each of four layers, with different scales and speeds, that vary over time
c + = pacifica_one_layer ( i , pacifica_palette_1 , sCIStart1 , beatsin16 ( 3 , 11 * 256 , 14 * 256 ) , beatsin8 ( 10 , 70 , 130 ) , 0 - beat16 ( 301 ) ) ;
c + = pacifica_one_layer ( i , pacifica_palette_2 , sCIStart2 , beatsin16 ( 4 , 6 * 256 , 9 * 256 ) , beatsin8 ( 17 , 40 , 80 ) , beat16 ( 401 ) ) ;
c + = pacifica_one_layer ( i , pacifica_palette_3 , sCIStart3 , 6 * 256 , beatsin8 ( 9 , 10 , 38 ) , 0 - beat16 ( 503 ) ) ;
c + = pacifica_one_layer ( i , pacifica_palette_3 , sCIStart4 , 5 * 256 , beatsin8 ( 8 , 10 , 28 ) , beat16 ( 601 ) ) ;
// Add extra 'white' to areas where the four layers of light have lined up brightly
uint8_t threshold = scale8 ( sin8 ( wave ) , 20 ) + basethreshold ;
wave + = 7 ;
uint8_t l = c . getAverageLight ( ) ;
if ( l > threshold ) {
uint8_t overage = l - threshold ;
uint8_t overage2 = qadd8 ( overage , overage ) ;
c + = CRGB ( overage , overage2 , qadd8 ( overage2 , overage2 ) ) ;
}
//deepen the blues and greens
c . blue = scale8 ( c . blue , 145 ) ;
c . green = scale8 ( c . green , 200 ) ;
c | = CRGB ( 2 , 5 , 7 ) ;
setPixelColor ( i , c . red , c . green , c . blue ) ;
}
2020-08-25 17:23:17 +02:00
now = nowOld ;
2020-03-25 11:17:45 +01:00
return FRAMETIME ;
}
// Add one layer of waves into the led array
CRGB WS2812FX : : pacifica_one_layer ( uint16_t i , CRGBPalette16 & p , uint16_t cistart , uint16_t wavescale , uint8_t bri , uint16_t ioff )
{
uint16_t ci = cistart ;
uint16_t waveangle = ioff ;
uint16_t wavescale_half = ( wavescale > > 1 ) + 20 ;
waveangle + = ( ( 120 + SEGMENT . intensity ) * i ) ; //original 250 * i
uint16_t s16 = sin16 ( waveangle ) + 32768 ;
uint16_t cs = scale16 ( s16 , wavescale_half ) + wavescale_half ;
ci + = ( cs * i ) ;
uint16_t sindex16 = sin16 ( ci ) + 32768 ;
uint8_t sindex8 = scale16 ( sindex16 , 240 ) ;
return ColorFromPalette ( p , sindex8 , bri , LINEARBLEND ) ;
}
2020-05-06 15:28:16 +02:00
//Solid colour background with glitter
uint16_t WS2812FX : : mode_solid_glitter ( )
{
fill ( SEGCOLOR ( 0 ) ) ;
if ( SEGMENT . intensity > random8 ( ) )
{
setPixelColor ( random16 ( SEGLEN ) , ULTRAWHITE ) ;
}
return FRAMETIME ;
}
2020-06-06 00:57:34 +02:00
/*
* Mode simulates a gradual sunrise
*/
uint16_t WS2812FX : : mode_sunrise ( ) {
//speed 0 - static sun
2020-06-22 12:30:31 +02:00
//speed 1 - 60: sunrise time in minutes
//speed 60 - 120 : sunset time in minutes - 60;
2020-06-06 00:57:34 +02:00
//speed above: "breathing" rise and set
if ( SEGENV . call = = 0 | | SEGMENT . speed ! = SEGENV . aux0 ) {
SEGENV . step = millis ( ) ; //save starting time, millis() because now can change from sync
SEGENV . aux0 = SEGMENT . speed ;
}
fill ( 0 ) ;
uint16_t stage = 0xFFFF ;
uint32_t s10SinceStart = ( millis ( ) - SEGENV . step ) / 100 ; //tenths of seconds
if ( SEGMENT . speed > 120 ) { //quick sunrise and sunset
uint16_t counter = ( now > > 1 ) * ( ( ( SEGMENT . speed - 120 ) > > 1 ) + 1 ) ;
stage = triwave16 ( counter ) ;
} else if ( SEGMENT . speed ) { //sunrise
uint8_t durMins = SEGMENT . speed ;
if ( durMins > 60 ) durMins - = 60 ;
uint32_t s10Target = durMins * 600 ;
if ( s10SinceStart > s10Target ) s10SinceStart = s10Target ;
stage = map ( s10SinceStart , 0 , s10Target , 0 , 0xFFFF ) ;
if ( SEGMENT . speed > 60 ) stage = 0xFFFF - stage ; //sunset
}
for ( uint16_t i = 0 ; i < = SEGLEN / 2 ; i + + )
{
//default palette is Fire
uint32_t c = color_from_palette ( 0 , false , true , 255 ) ; //background
uint16_t wave = triwave16 ( ( i * stage ) / SEGLEN ) ;
wave = ( wave > > 8 ) + ( ( wave * SEGMENT . intensity ) > > 15 ) ;
if ( wave > 240 ) { //clipped, full white sun
c = color_from_palette ( 240 , false , true , 255 ) ;
} else { //transition
c = color_from_palette ( wave , false , true , 255 ) ;
}
setPixelColor ( i , c ) ;
setPixelColor ( SEGLEN - i - 1 , c ) ;
}
return FRAMETIME ;
}
/*
* Effects by Andrew Tuline
*/
uint16_t WS2812FX : : phased_base ( uint8_t moder ) { // We're making sine waves here. By Andrew Tuline.
uint8_t allfreq = 16 ; // Base frequency.
//float* phasePtr = reinterpret_cast<float*>(SEGENV.step); // Phase change value gets calculated.
static float phase = 0 ; //phasePtr[0];
uint8_t cutOff = ( 255 - SEGMENT . intensity ) ; // You can change the number of pixels. AKA INTENSITY (was 192).
uint8_t modVal = 5 ; //SEGMENT.fft1/8+1; // You can change the modulus. AKA FFT1 (was 5).
uint8_t index = now / 64 ; // Set color rotation speed
phase + = SEGMENT . speed / 32.0 ; // You can change the speed of the wave. AKA SPEED (was .4)
//phasePtr[0] = phase;
for ( int i = 0 ; i < SEGLEN ; i + + ) {
if ( moder = = 1 ) modVal = ( inoise8 ( i * 10 + i * 10 ) / 16 ) ; // Let's randomize our mod length with some Perlin noise.
uint16_t val = ( i + 1 ) * allfreq ; // This sets the frequency of the waves. The +1 makes sure that leds[0] is used.
if ( modVal = = 0 ) modVal = 1 ;
val + = phase * ( i % modVal + 1 ) / 2 ; // This sets the varying phase change of the waves. By Andrew Tuline.
uint8_t b = cubicwave8 ( val ) ; // Now we make an 8 bit sinewave.
b = ( b > cutOff ) ? ( b - cutOff ) : 0 ; // A ternary operator to cutoff the light.
setPixelColor ( i , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( index , false , false , 0 ) , b ) ) ;
index + = 256 / SEGLEN ;
2020-11-23 00:06:57 +01:00
if ( SEGLEN > 256 ) index + + ; // Correction for segments longer than 256 LEDs
2020-06-06 00:57:34 +02:00
}
return FRAMETIME ;
}
uint16_t WS2812FX : : mode_phased ( void ) {
return phased_base ( 0 ) ;
}
uint16_t WS2812FX : : mode_phased_noise ( void ) {
return phased_base ( 1 ) ;
}
uint16_t WS2812FX : : mode_twinkleup ( void ) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline.
random16_set_seed ( 535 ) ; // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through.
for ( int i = 0 ; i < SEGLEN ; i + + ) {
uint8_t ranstart = random8 ( ) ; // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work.
uint8_t pixBri = sin8 ( ranstart + 16 * now / ( 256 - SEGMENT . speed ) ) ;
if ( random8 ( ) > SEGMENT . intensity ) pixBri = 0 ;
2021-07-04 18:52:05 +02:00
setPixelColor ( i , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( random8 ( ) + now / 100 , false , PALETTE_SOLID_WRAP , 0 ) , pixBri ) ) ;
2020-06-06 00:57:34 +02:00
}
return FRAMETIME ;
}
// Peaceful noise that's slow and with gradually changing palettes. Does not support WLED palettes or default colours or controls.
uint16_t WS2812FX : : mode_noisepal ( void ) { // Slow noise palette by Andrew Tuline.
uint16_t scale = 15 + ( SEGMENT . intensity > > 2 ) ; //default was 30
//#define scale 30
2021-08-22 22:16:25 +02:00
uint16_t dataSize = sizeof ( CRGBPalette16 ) * 2 ; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes)
2020-06-06 00:57:34 +02:00
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
CRGBPalette16 * palettes = reinterpret_cast < CRGBPalette16 * > ( SEGENV . data ) ;
uint16_t changePaletteMs = 4000 + SEGMENT . speed * 10 ; //between 4 - 6.5sec
if ( millis ( ) - SEGENV . step > changePaletteMs )
{
SEGENV . step = millis ( ) ;
uint8_t baseI = random8 ( ) ;
palettes [ 1 ] = CRGBPalette16 ( CHSV ( baseI + random8 ( 64 ) , 255 , random8 ( 128 , 255 ) ) , CHSV ( baseI + 128 , 255 , random8 ( 128 , 255 ) ) , CHSV ( baseI + random8 ( 92 ) , 192 , random8 ( 128 , 255 ) ) , CHSV ( baseI + random8 ( 92 ) , 255 , random8 ( 128 , 255 ) ) ) ;
}
CRGB color ;
//EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms)
nblendPaletteTowardPalette ( palettes [ 0 ] , palettes [ 1 ] , 48 ) ; // Blend towards the target palette over 48 iterations.
if ( SEGMENT . palette > 0 ) palettes [ 0 ] = currentPalette ;
for ( int i = 0 ; i < SEGLEN ; i + + ) {
uint8_t index = inoise8 ( i * scale , SEGENV . aux0 + i * scale ) ; // Get a value from the noise function. I'm using both x and y axis.
color = ColorFromPalette ( palettes [ 0 ] , index , 255 , LINEARBLEND ) ; // Use the my own palette.
setPixelColor ( i , color . red , color . green , color . blue ) ;
}
SEGENV . aux0 + = beatsin8 ( 10 , 1 , 4 ) ; // Moving along the distance. Vary it a bit with a sine wave.
return FRAMETIME ;
}
// Sine waves that have controllable phase change speed, frequency and cutoff. By Andrew Tuline.
// SEGMENT.speed ->Speed, SEGMENT.intensity -> Frequency (SEGMENT.fft1 -> Color change, SEGMENT.fft2 -> PWM cutoff)
//
uint16_t WS2812FX : : mode_sinewave ( void ) { // Adjustable sinewave. By Andrew Tuline
//#define qsuba(x, b) ((x>b)?x-b:0) // Analog Unsigned subtraction macro. if result <0, then => 0
uint16_t colorIndex = now / 32 ; //(256 - SEGMENT.fft1); // Amount of colour change.
SEGENV . step + = SEGMENT . speed / 16 ; // Speed of animation.
uint16_t freq = SEGMENT . intensity / 4 ; //SEGMENT.fft2/8; // Frequency of the signal.
for ( int i = 0 ; i < SEGLEN ; i + + ) { // For each of the LED's in the strand, set a brightness based on a wave as follows:
int pixBri = cubicwave8 ( ( i * freq ) + SEGENV . step ) ; //qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub)..
//setPixCol(i, i*colorIndex/255, pixBri);
setPixelColor ( i , color_blend ( SEGCOLOR ( 1 ) , color_from_palette ( i * colorIndex / 255 , false , PALETTE_SOLID_WRAP , 0 ) , pixBri ) ) ;
}
return FRAMETIME ;
}
/*
* Best of both worlds from Palette and Spot effects . By Aircoookie
*/
uint16_t WS2812FX : : mode_flow ( void )
{
uint16_t counter = 0 ;
if ( SEGMENT . speed ! = 0 )
{
counter = now * ( ( SEGMENT . speed > > 2 ) + 1 ) ;
counter = counter > > 8 ;
}
uint16_t maxZones = SEGLEN / 6 ; //only looks good if each zone has at least 6 LEDs
uint16_t zones = ( SEGMENT . intensity * maxZones ) > > 8 ;
if ( zones & 0x01 ) zones + + ; //zones must be even
if ( zones < 2 ) zones = 2 ;
uint16_t zoneLen = SEGLEN / zones ;
uint16_t offset = ( SEGLEN - zones * zoneLen ) > > 1 ;
fill ( color_from_palette ( - counter , false , true , 255 ) ) ;
for ( uint16_t z = 0 ; z < zones ; z + + )
{
uint16_t pos = offset + z * zoneLen ;
for ( uint16_t i = 0 ; i < zoneLen ; i + + )
{
uint8_t colorIndex = ( i * 255 / zoneLen ) - counter ;
uint16_t led = ( z & 0x01 ) ? i : ( zoneLen - 1 ) - i ;
if ( IS_REVERSE ) led = ( zoneLen - 1 ) - led ;
setPixelColor ( pos + led , color_from_palette ( colorIndex , false , true , 255 ) ) ;
}
}
return FRAMETIME ;
}
2020-06-22 12:30:31 +02:00
/*
* Dots waving around in a sine / pendulum motion .
* Little pixel birds flying in a circle . By Aircoookie
*/
uint16_t WS2812FX : : mode_chunchun ( void )
{
fill ( SEGCOLOR ( 1 ) ) ;
uint16_t counter = now * ( 6 + ( SEGMENT . speed > > 4 ) ) ;
2021-03-01 19:50:14 +01:00
uint16_t numBirds = 2 + ( SEGLEN > > 3 ) ; // 2 + 1/8 of a segment
uint16_t span = ( SEGMENT . intensity < < 8 ) / numBirds ;
2020-06-22 12:30:31 +02:00
for ( uint16_t i = 0 ; i < numBirds ; i + + )
{
2021-03-01 19:50:14 +01:00
counter - = span ;
2021-02-24 20:23:32 +01:00
uint16_t megumin = sin16 ( counter ) + 0x8000 ;
2020-06-22 12:30:31 +02:00
uint32_t bird = ( megumin * SEGLEN ) > > 16 ;
2021-03-01 19:50:14 +01:00
uint32_t c = color_from_palette ( ( i * 255 ) / numBirds , false , false , 0 ) ; // no palette wrapping
2020-06-22 12:30:31 +02:00
setPixelColor ( bird , c ) ;
}
return FRAMETIME ;
2020-08-22 20:54:59 +02:00
}
2021-08-22 22:16:25 +02:00
//13 bytes
2020-08-22 20:54:59 +02:00
typedef struct Spotlight {
float speed ;
uint8_t colorIdx ;
int16_t position ;
unsigned long lastUpdateTime ;
uint8_t width ;
uint8_t type ;
} spotlight ;
2020-08-30 10:57:42 +02:00
# define SPOT_TYPE_SOLID 0
# define SPOT_TYPE_GRADIENT 1
# define SPOT_TYPE_2X_GRADIENT 2
# define SPOT_TYPE_2X_DOT 3
# define SPOT_TYPE_3X_DOT 4
# define SPOT_TYPE_4X_DOT 5
# define SPOT_TYPES_COUNT 6
2021-08-22 22:16:25 +02:00
# ifdef ESP8266
# define SPOT_MAX_COUNT 17 //Number of simultaneous waves
# else
# define SPOT_MAX_COUNT 49 //Number of simultaneous waves
# endif
2020-08-22 20:54:59 +02:00
/*
* Spotlights moving back and forth that cast dancing shadows .
* Shine this through tree branches / leaves or other close - up objects that cast
* interesting shadows onto a ceiling or tarp .
*
* By Steve Pomeroy @ xxv
*/
uint16_t WS2812FX : : mode_dancing_shadows ( void )
{
2021-09-12 13:31:47 +02:00
uint8_t numSpotlights = map ( SEGMENT . intensity , 0 , 255 , 2 , SPOT_MAX_COUNT ) ; // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
2020-08-22 20:54:59 +02:00
bool initialize = SEGENV . aux0 ! = numSpotlights ;
SEGENV . aux0 = numSpotlights ;
uint16_t dataSize = sizeof ( spotlight ) * numSpotlights ;
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
Spotlight * spotlights = reinterpret_cast < Spotlight * > ( SEGENV . data ) ;
fill ( BLACK ) ;
unsigned long time = millis ( ) ;
bool respawn = false ;
for ( uint8_t i = 0 ; i < numSpotlights ; i + + ) {
if ( ! initialize ) {
// advance the position of the spotlight
int16_t delta = ( float ) ( time - spotlights [ i ] . lastUpdateTime ) *
( spotlights [ i ] . speed * ( ( 1.0 + SEGMENT . speed ) / 100.0 ) ) ;
if ( abs ( delta ) > = 1 ) {
spotlights [ i ] . position + = delta ;
spotlights [ i ] . lastUpdateTime = time ;
}
respawn = ( spotlights [ i ] . speed > 0.0 & & spotlights [ i ] . position > ( SEGLEN + 2 ) )
| | ( spotlights [ i ] . speed < 0.0 & & spotlights [ i ] . position < - ( spotlights [ i ] . width + 2 ) ) ;
}
if ( initialize | | respawn ) {
spotlights [ i ] . colorIdx = random8 ( ) ;
spotlights [ i ] . width = random8 ( 1 , 10 ) ;
spotlights [ i ] . speed = 1.0 / random8 ( 4 , 50 ) ;
if ( initialize ) {
spotlights [ i ] . position = random16 ( SEGLEN ) ;
spotlights [ i ] . speed * = random8 ( 2 ) ? 1.0 : - 1.0 ;
} else {
if ( random8 ( 2 ) ) {
spotlights [ i ] . position = SEGLEN + spotlights [ i ] . width ;
spotlights [ i ] . speed * = - 1.0 ;
} else {
spotlights [ i ] . position = - spotlights [ i ] . width ;
}
}
spotlights [ i ] . lastUpdateTime = time ;
spotlights [ i ] . type = random8 ( SPOT_TYPES_COUNT ) ;
}
uint32_t color = color_from_palette ( spotlights [ i ] . colorIdx , false , false , 0 ) ;
int start = spotlights [ i ] . position ;
if ( spotlights [ i ] . width < = 1 ) {
if ( start > = 0 & & start < SEGLEN ) {
blendPixelColor ( start , color , 128 ) ;
}
} else {
switch ( spotlights [ i ] . type ) {
case SPOT_TYPE_SOLID :
for ( uint8_t j = 0 ; j < spotlights [ i ] . width ; j + + ) {
if ( ( start + j ) > = 0 & & ( start + j ) < SEGLEN ) {
blendPixelColor ( start + j , color , 128 ) ;
}
}
break ;
case SPOT_TYPE_GRADIENT :
for ( uint8_t j = 0 ; j < spotlights [ i ] . width ; j + + ) {
if ( ( start + j ) > = 0 & & ( start + j ) < SEGLEN ) {
blendPixelColor ( start + j , color ,
cubicwave8 ( map ( j , 0 , spotlights [ i ] . width - 1 , 0 , 255 ) ) ) ;
}
}
break ;
case SPOT_TYPE_2X_GRADIENT :
for ( uint8_t j = 0 ; j < spotlights [ i ] . width ; j + + ) {
if ( ( start + j ) > = 0 & & ( start + j ) < SEGLEN ) {
blendPixelColor ( start + j , color ,
cubicwave8 ( 2 * map ( j , 0 , spotlights [ i ] . width - 1 , 0 , 255 ) ) ) ;
}
}
break ;
case SPOT_TYPE_2X_DOT :
for ( uint8_t j = 0 ; j < spotlights [ i ] . width ; j + = 2 ) {
if ( ( start + j ) > = 0 & & ( start + j ) < SEGLEN ) {
blendPixelColor ( start + j , color , 128 ) ;
}
}
break ;
case SPOT_TYPE_3X_DOT :
for ( uint8_t j = 0 ; j < spotlights [ i ] . width ; j + = 3 ) {
if ( ( start + j ) > = 0 & & ( start + j ) < SEGLEN ) {
blendPixelColor ( start + j , color , 128 ) ;
}
}
break ;
case SPOT_TYPE_4X_DOT :
for ( uint8_t j = 0 ; j < spotlights [ i ] . width ; j + = 4 ) {
if ( ( start + j ) > = 0 & & ( start + j ) < SEGLEN ) {
blendPixelColor ( start + j , color , 128 ) ;
}
}
break ;
}
}
}
return FRAMETIME ;
}
2020-09-27 01:58:21 +02:00
/*
Imitates a washing machine , rotating same waves forward , then pause , then backward .
By Stefan Seegel
*/
uint16_t WS2812FX : : mode_washing_machine ( void ) {
2020-09-27 14:42:14 +02:00
float speed = tristate_square8 ( now > > 7 , 90 , 15 ) ;
float quot = 32.0f - ( ( float ) SEGMENT . speed / 16.0f ) ;
speed / = quot ;
SEGENV . step + = ( speed * 128.0f ) ;
2020-09-27 01:58:21 +02:00
for ( int i = 0 ; i < SEGLEN ; i + + ) {
2020-09-27 14:42:14 +02:00
uint8_t col = sin8 ( ( ( SEGMENT . intensity / 25 + 1 ) * 255 * i / SEGLEN ) + ( SEGENV . step > > 7 ) ) ;
2020-09-27 01:58:21 +02:00
setPixelColor ( i , color_from_palette ( col , false , PALETTE_SOLID_WRAP , 3 ) ) ;
}
return FRAMETIME ;
}
2020-12-15 13:35:50 +01:00
/*
Blends random colors across palette
Modified , originally by Mark Kriegsman https : //gist.github.com/kriegsman/1f7ccbbfa492a73c015e
*/
uint16_t WS2812FX : : mode_blends ( void ) {
2021-09-12 13:31:47 +02:00
uint16_t dataSize = sizeof ( uint32_t ) * SEGLEN ; // max segment length of 56 pixels on 16 segment ESP8266
2020-12-15 13:35:50 +01:00
if ( ! SEGENV . allocateData ( dataSize ) ) return mode_static ( ) ; //allocation failed
uint32_t * pixels = reinterpret_cast < uint32_t * > ( SEGENV . data ) ;
uint8_t blendSpeed = map ( SEGMENT . intensity , 0 , UINT8_MAX , 10 , 128 ) ;
uint8_t shift = ( now * ( ( SEGMENT . speed > > 3 ) + 1 ) ) > > 8 ;
for ( int i = 0 ; i < SEGLEN ; i + + ) {
pixels [ i ] = color_blend ( pixels [ i ] , color_from_palette ( shift + quadwave8 ( ( i + 1 ) * 16 ) , false , PALETTE_SOLID_WRAP , 255 ) , blendSpeed ) ;
setPixelColor ( i , pixels [ i ] ) ;
shift + = 3 ;
}
return FRAMETIME ;
2020-12-22 13:15:57 +01:00
}
2021-08-22 22:16:25 +02:00
/*
TV Simulator
Modified and adapted to WLED by Def3nder , based on " Fake TV Light for Engineers " by Phillip Burgess https : //learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
*/
//43 bytes
2020-12-22 13:15:57 +01:00
typedef struct TvSim {
uint32_t totalTime = 0 ;
uint32_t fadeTime = 0 ;
uint32_t startTime = 0 ;
uint32_t elapsed = 0 ;
uint32_t pixelNum = 0 ;
2021-04-16 10:22:22 +02:00
uint16_t sliderValues = 0 ;
uint32_t sceeneStart = 0 ;
uint32_t sceeneDuration = 0 ;
uint16_t sceeneColorHue = 0 ;
uint8_t sceeneColorSat = 0 ;
uint8_t sceeneColorBri = 0 ;
uint8_t actualColorR = 0 ;
uint8_t actualColorG = 0 ;
uint8_t actualColorB = 0 ;
2020-12-22 13:15:57 +01:00
uint16_t pr = 0 ; // Prev R, G, B
uint16_t pg = 0 ;
uint16_t pb = 0 ;
} tvSim ;
uint16_t WS2812FX : : mode_tv_simulator ( void ) {
2021-04-16 10:22:22 +02:00
uint16_t nr , ng , nb , r , g , b , i , hue ;
2021-04-22 00:47:50 +02:00
uint8_t sat , bri , j ;
2020-12-22 13:15:57 +01:00
if ( ! SEGENV . allocateData ( sizeof ( tvSim ) ) ) return mode_static ( ) ; //allocation failed
TvSim * tvSimulator = reinterpret_cast < TvSim * > ( SEGENV . data ) ;
2021-04-16 10:22:22 +02:00
uint8_t colorSpeed = map ( SEGMENT . speed , 0 , UINT8_MAX , 1 , 20 ) ;
uint8_t colorIntensity = map ( SEGMENT . intensity , 0 , UINT8_MAX , 10 , 30 ) ;
2020-12-22 13:15:57 +01:00
2021-04-16 10:22:22 +02:00
i = SEGMENT . speed < < 8 | SEGMENT . intensity ;
if ( i ! = tvSimulator - > sliderValues ) {
tvSimulator - > sliderValues = i ;
SEGENV . aux1 = 0 ;
}
2020-12-22 13:15:57 +01:00
2021-04-16 10:22:22 +02:00
// create a new sceene
if ( ( ( millis ( ) - tvSimulator - > sceeneStart ) > = tvSimulator - > sceeneDuration ) | | SEGENV . aux1 = = 0 ) {
tvSimulator - > sceeneStart = millis ( ) ; // remember the start of the new sceene
tvSimulator - > sceeneDuration = random16 ( 60 * 250 * colorSpeed , 60 * 750 * colorSpeed ) ; // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider)
tvSimulator - > sceeneColorHue = random16 ( 0 , 768 ) ; // random start color-tone for the sceene
tvSimulator - > sceeneColorSat = random8 ( 100 , 130 + colorIntensity ) ; // random start color-saturation for the sceene
tvSimulator - > sceeneColorBri = random8 ( 200 , 240 ) ; // random start color-brightness for the sceene
SEGENV . aux1 = 1 ;
SEGENV . aux0 = 0 ;
}
// slightly change the color-tone in this sceene
if ( SEGENV . aux0 = = 0 ) {
// hue change in both directions
j = random8 ( 4 * colorIntensity ) ;
hue = ( random8 ( ) < 128 ) ? ( ( j < tvSimulator - > sceeneColorHue ) ? tvSimulator - > sceeneColorHue - j : 767 - tvSimulator - > sceeneColorHue - j ) : // negative
( ( j + tvSimulator - > sceeneColorHue ) < 767 ? tvSimulator - > sceeneColorHue + j : tvSimulator - > sceeneColorHue + j - 767 ) ; // positive
// saturation
j = random8 ( 2 * colorIntensity ) ;
sat = ( tvSimulator - > sceeneColorSat - j ) < 0 ? 0 : tvSimulator - > sceeneColorSat - j ;
// brightness
j = random8 ( 100 ) ;
bri = ( tvSimulator - > sceeneColorBri - j ) < 0 ? 0 : tvSimulator - > sceeneColorBri - j ;
// calculate R,G,B from HSV
// Source: https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/
{ // just to create a local scope for the variables
uint8_t temp [ 5 ] , n = ( hue > > 8 ) % 3 ;
uint8_t x = ( ( ( ( hue & 255 ) * sat ) > > 8 ) * bri ) > > 8 ;
uint8_t s = ( ( 256 - sat ) * bri ) > > 8 ;
temp [ 0 ] = temp [ 3 ] = s ;
temp [ 1 ] = temp [ 4 ] = x + s ;
temp [ 2 ] = bri - x ;
tvSimulator - > actualColorR = temp [ n + 2 ] ;
tvSimulator - > actualColorG = temp [ n + 1 ] ;
tvSimulator - > actualColorB = temp [ n ] ;
}
}
// Apply gamma correction, further expand to 16/16/16
nr = ( uint8_t ) gamma8 ( tvSimulator - > actualColorR ) * 257 ; // New R/G/B
ng = ( uint8_t ) gamma8 ( tvSimulator - > actualColorG ) * 257 ;
nb = ( uint8_t ) gamma8 ( tvSimulator - > actualColorB ) * 257 ;
2020-12-22 13:15:57 +01:00
if ( SEGENV . aux0 = = 0 ) { // initialize next iteration
SEGENV . aux0 = 1 ;
// randomize total duration and fade duration for the actual color
2021-04-16 10:22:22 +02:00
tvSimulator - > totalTime = random16 ( 250 , 2500 ) ; // Semi-random pixel-to-pixel time
tvSimulator - > fadeTime = random16 ( 0 , tvSimulator - > totalTime ) ; // Pixel-to-pixel transition time
if ( random8 ( 10 ) < 3 ) tvSimulator - > fadeTime = 0 ; // Force scene cut 30% of time
2020-12-22 13:15:57 +01:00
tvSimulator - > startTime = millis ( ) ;
} // end of initialization
// how much time is elapsed ?
tvSimulator - > elapsed = millis ( ) - tvSimulator - > startTime ;
// fade from prev volor to next color
if ( tvSimulator - > elapsed < tvSimulator - > fadeTime ) {
r = map ( tvSimulator - > elapsed , 0 , tvSimulator - > fadeTime , tvSimulator - > pr , nr ) ;
g = map ( tvSimulator - > elapsed , 0 , tvSimulator - > fadeTime , tvSimulator - > pg , ng ) ;
b = map ( tvSimulator - > elapsed , 0 , tvSimulator - > fadeTime , tvSimulator - > pb , nb ) ;
} else { // Avoid divide-by-zero in map()
r = nr ;
g = ng ;
b = nb ;
}
// set strip color
for ( i = 0 ; i < SEGLEN ; i + + ) {
setPixelColor ( i , r > > 8 , g > > 8 , b > > 8 ) ; // Quantize to 8-bit
}
// if total duration has passed, remember last color and restart the loop
if ( tvSimulator - > elapsed > = tvSimulator - > totalTime ) {
tvSimulator - > pr = nr ; // Prev RGB = new RGB
tvSimulator - > pg = ng ;
tvSimulator - > pb = nb ;
SEGENV . aux0 = 0 ;
}
return FRAMETIME ;
}
2021-01-04 11:11:36 +01:00
/*
Aurora effect
*/
//CONFIG
2021-08-22 22:16:25 +02:00
# ifdef ESP8266
# define W_MAX_COUNT 9 //Number of simultaneous waves
# else
# define W_MAX_COUNT 20 //Number of simultaneous waves
# endif
2021-01-04 11:11:36 +01:00
# define W_MAX_SPEED 6 //Higher number, higher speed
# define W_WIDTH_FACTOR 6 //Higher number, smaller waves
2021-08-22 22:16:25 +02:00
//24 bytes
2021-01-04 11:11:36 +01:00
class AuroraWave {
private :
uint16_t ttl ;
CRGB basecolor ;
float basealpha ;
uint16_t age ;
uint16_t width ;
float center ;
bool goingleft ;
float speed_factor ;
bool alive = true ;
public :
void init ( uint32_t segment_length , CRGB color ) {
ttl = random ( 500 , 1501 ) ;
basecolor = color ;
basealpha = random ( 60 , 101 ) / ( float ) 100 ;
age = 0 ;
width = random ( segment_length / 20 , segment_length / W_WIDTH_FACTOR ) ; //half of width to make math easier
if ( ! width ) width = 1 ;
center = random ( 101 ) / ( float ) 100 * segment_length ;
goingleft = random ( 0 , 2 ) = = 0 ;
speed_factor = ( random ( 10 , 31 ) / ( float ) 100 * W_MAX_SPEED / 255 ) ;
alive = true ;
}
CRGB getColorForLED ( int ledIndex ) {
if ( ledIndex < center - width | | ledIndex > center + width ) return 0 ; //Position out of range of this wave
CRGB rgb ;
//Offset of this led from center of wave
//The further away from the center, the dimmer the LED
float offset = ledIndex - center ;
if ( offset < 0 ) offset = - offset ;
float offsetFactor = offset / width ;
//The age of the wave determines it brightness.
//At half its maximum age it will be the brightest.
float ageFactor = 0.1 ;
if ( ( float ) age / ttl < 0.5 ) {
ageFactor = ( float ) age / ( ttl / 2 ) ;
} else {
ageFactor = ( float ) ( ttl - age ) / ( ( float ) ttl * 0.5 ) ;
}
//Calculate color based on above factors and basealpha value
float factor = ( 1 - offsetFactor ) * ageFactor * basealpha ;
rgb . r = basecolor . r * factor ;
rgb . g = basecolor . g * factor ;
rgb . b = basecolor . b * factor ;
return rgb ;
} ;
//Change position and age of wave
//Determine if its sill "alive"
void update ( uint32_t segment_length , uint32_t speed ) {
if ( goingleft ) {
center - = speed_factor * speed ;
} else {
center + = speed_factor * speed ;
}
age + + ;
if ( age > ttl ) {
alive = false ;
} else {
if ( goingleft ) {
if ( center + width < 0 ) {
alive = false ;
}
} else {
if ( center - width > segment_length ) {
alive = false ;
}
}
}
} ;
bool stillAlive ( ) {
return alive ;
} ;
} ;
uint16_t WS2812FX : : mode_aurora ( void ) {
//aux1 = Wavecount
//aux2 = Intensity in last loop
AuroraWave * waves ;
if ( SEGENV . aux0 ! = SEGMENT . intensity | | SEGENV . call = = 0 ) {
//Intensity slider changed or first call
2021-08-22 22:16:25 +02:00
SEGENV . aux1 = map ( SEGMENT . intensity , 0 , 255 , 2 , W_MAX_COUNT ) ;
2021-01-04 11:11:36 +01:00
SEGENV . aux0 = SEGMENT . intensity ;
2021-09-12 13:31:47 +02:00
if ( ! SEGENV . allocateData ( sizeof ( AuroraWave ) * SEGENV . aux1 ) ) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
2021-01-04 11:11:36 +01:00
return mode_static ( ) ; //allocation failed
}
waves = reinterpret_cast < AuroraWave * > ( SEGENV . data ) ;
for ( int i = 0 ; i < SEGENV . aux1 ; i + + ) {
waves [ i ] . init ( SEGLEN , col_to_crgb ( color_from_palette ( random8 ( ) , false , false , random ( 0 , 3 ) ) ) ) ;
}
} else {
waves = reinterpret_cast < AuroraWave * > ( SEGENV . data ) ;
}
for ( int i = 0 ; i < SEGENV . aux1 ; i + + ) {
//Update values of wave
waves [ i ] . update ( SEGLEN , SEGMENT . speed ) ;
if ( ! ( waves [ i ] . stillAlive ( ) ) ) {
//If a wave dies, reinitialize it starts over.
waves [ i ] . init ( SEGLEN , col_to_crgb ( color_from_palette ( random8 ( ) , false , false , random ( 0 , 3 ) ) ) ) ;
}
}
2021-07-02 00:24:14 +02:00
uint8_t backlight = 1 ; //dimmer backlight if less active colors
if ( SEGCOLOR ( 0 ) ) backlight + + ;
if ( SEGCOLOR ( 1 ) ) backlight + + ;
if ( SEGCOLOR ( 2 ) ) backlight + + ;
2021-01-04 11:11:36 +01:00
//Loop through LEDs to determine color
for ( int i = 0 ; i < SEGLEN ; i + + ) {
2021-07-02 00:24:14 +02:00
CRGB mixedRgb = CRGB ( backlight , backlight , backlight ) ;
2021-01-04 11:11:36 +01:00
//For each LED we must check each wave if it is "active" at this position.
//If there are multiple waves active on a LED we multiply their values.
for ( int j = 0 ; j < SEGENV . aux1 ; j + + ) {
CRGB rgb = waves [ j ] . getColorForLED ( i ) ;
if ( rgb ! = CRGB ( 0 ) ) {
mixedRgb + = rgb ;
}
}
2021-07-02 00:24:14 +02:00
setPixelColor ( i , mixedRgb [ 0 ] , mixedRgb [ 1 ] , mixedRgb [ 2 ] ) ;
2021-01-04 11:11:36 +01:00
}
return FRAMETIME ;
}