2019-02-09 16:37:20 +01:00
/*
WS2812FX_fcn . cpp contains all utility functions
Harm Aldick - 2016
www . aldick . org
LICENSE
The MIT License ( MIT )
Copyright ( c ) 2016 Harm Aldick
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 .
Modified heavily for WLED
*/
2021-02-24 20:23:32 +01:00
# include "wled.h"
2019-10-07 23:38:21 +02:00
# include "FX.h"
2019-02-09 16:37:20 +01:00
# include "palettes.h"
2021-02-13 01:02:14 +01:00
/*
Custom per - LED mapping has moved !
2020-03-21 00:57:54 +01:00
2021-02-13 01:02:14 +01:00
Create a file " ledmap.json " using the edit page .
2020-03-21 00:57:54 +01:00
2021-02-13 01:02:14 +01:00
this is just an example ( 30 LEDs ) . It will first set all even , then all uneven LEDs .
{ " map " : [
0 , 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 , 22 , 24 , 26 , 28 ,
1 , 3 , 5 , 7 , 9 , 11 , 13 , 15 , 17 , 19 , 21 , 23 , 25 , 27 , 29 ] }
2020-03-21 00:57:54 +01:00
2021-02-13 01:02:14 +01:00
another example . Switches direction every 5 LEDs .
{ " map " : [
0 , 1 , 2 , 3 , 4 , 9 , 8 , 7 , 6 , 5 , 10 , 11 , 12 , 13 , 14 ,
2021-02-24 20:23:32 +01:00
19 , 18 , 17 , 16 , 15 , 20 , 21 , 22 , 23 , 24 , 29 , 28 , 27 , 26 , 25 ] }
2021-02-13 01:02:14 +01:00
*/
2020-12-13 19:02:12 +01:00
2021-04-15 10:55:22 +02:00
//factory defaults LED setup
//#define PIXEL_COUNTS 30, 30, 30, 30
//#define DATA_PINS 16, 1, 3, 4
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
# ifndef PIXEL_COUNTS
2021-12-02 13:09:53 +01:00
# define PIXEL_COUNTS DEFAULT_LED_COUNT
2021-04-15 10:55:22 +02:00
# endif
# ifndef DATA_PINS
# define DATA_PINS LEDPIN
# endif
# ifndef DEFAULT_LED_TYPE
# define DEFAULT_LED_TYPE TYPE_WS2812_RGB
# endif
2021-06-24 02:29:14 +02:00
# if MAX_NUM_SEGMENTS < WLED_MAX_BUSSES
# error "Max segments must be at least max number of busses!"
# endif
2021-01-18 20:51:32 +01:00
//do not call this method from system context (network callback)
2021-02-24 20:23:32 +01:00
void WS2812FX : : finalizeInit ( void )
2019-02-09 16:37:20 +01:00
{
2022-02-09 08:43:35 +01:00
//reset segment runtimes
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + ) {
_segment_runtimes [ i ] . markForReset ( ) ;
_segment_runtimes [ i ] . resetIfRequired ( ) ;
}
2022-02-04 13:28:00 +01:00
_hasWhiteChannel = _isOffRefreshRequired = false ;
2019-11-29 18:53:01 +01:00
2021-03-12 23:56:29 +01:00
//if busses failed to load, add default (fresh install, FS issue, ...)
2021-01-30 20:51:36 +01:00
if ( busses . getNumBusses ( ) = = 0 ) {
2021-04-15 22:19:58 +02:00
const uint8_t defDataPins [ ] = { DATA_PINS } ;
const uint16_t defCounts [ ] = { PIXEL_COUNTS } ;
2021-04-16 20:07:54 +02:00
const uint8_t defNumBusses = ( ( sizeof defDataPins ) / ( sizeof defDataPins [ 0 ] ) ) ; // min 1
const uint8_t defNumCounts = ( ( sizeof defCounts ) / ( sizeof defCounts [ 0 ] ) ) ; // min 1
2021-04-15 10:55:22 +02:00
uint16_t prevLen = 0 ;
2021-04-16 20:07:54 +02:00
for ( uint8_t i = 0 ; i < defNumBusses & & i < WLED_MAX_BUSSES ; i + + ) {
2021-04-15 10:55:22 +02:00
uint8_t defPin [ ] = { defDataPins [ i ] } ;
uint16_t start = prevLen ;
2021-10-11 02:19:33 +02:00
uint16_t count = defCounts [ ( i < defNumCounts ) ? i : defNumCounts - 1 ] ;
2021-04-15 10:55:22 +02:00
prevLen + = count ;
2022-03-09 13:39:51 +01:00
BusConfig defCfg = BusConfig ( DEFAULT_LED_TYPE , defPin , start , count , COL_ORDER_GRB , false , 0 , RGBW_MODE_MANUAL_ONLY ) ;
2021-04-15 10:55:22 +02:00
busses . add ( defCfg ) ;
}
2021-01-30 20:51:36 +01:00
}
2021-01-18 20:51:32 +01:00
2021-02-24 20:23:32 +01:00
_length = 0 ;
for ( uint8_t i = 0 ; i < busses . getNumBusses ( ) ; i + + ) {
Bus * bus = busses . getBus ( i ) ;
if ( bus = = nullptr ) continue ;
2021-10-11 02:19:33 +02:00
if ( bus - > getStart ( ) + bus - > getLength ( ) > MAX_LEDS ) break ;
2021-05-22 00:13:49 +02:00
//RGBW mode is enabled if at least one of the strips is RGBW
2022-02-04 13:28:00 +01:00
_hasWhiteChannel | = bus - > isRgbw ( ) ;
2021-05-22 00:13:49 +02:00
//refresh is required to remain off if at least one of the strips requires the refresh.
2022-02-04 13:28:00 +01:00
_isOffRefreshRequired | = bus - > isOffRefreshRequired ( ) ;
2021-10-11 02:19:33 +02:00
uint16_t busEnd = bus - > getStart ( ) + bus - > getLength ( ) ;
if ( busEnd > _length ) _length = busEnd ;
2021-06-24 02:29:14 +02:00
# ifdef ESP8266
2021-10-11 02:19:33 +02:00
if ( ( ! IS_DIGITAL ( bus - > getType ( ) ) | | IS_2PIN ( bus - > getType ( ) ) ) ) continue ;
2021-01-19 17:22:37 +01:00
uint8_t pins [ 5 ] ;
2021-10-11 02:19:33 +02:00
if ( ! bus - > getPins ( pins ) ) continue ;
BusDigital * bd = static_cast < BusDigital * > ( bus ) ;
2021-01-19 17:22:37 +01:00
if ( pins [ 0 ] = = 3 ) bd - > reinit ( ) ;
2021-06-24 02:29:14 +02:00
# endif
2021-02-24 20:23:32 +01:00
}
2021-09-12 01:00:54 +02:00
2021-10-11 02:19:33 +02:00
//segments are created in makeAutoSegments();
2020-01-02 22:10:59 +01:00
2019-02-09 16:37:20 +01:00
setBrightness ( _brightness ) ;
}
void WS2812FX : : service ( ) {
2019-11-26 20:41:15 +01:00
uint32_t nowUp = millis ( ) ; // Be aware, millis() rolls over every 49 days
now = nowUp + timebase ;
if ( nowUp - _lastShow < MIN_SHOW_DELAY ) return ;
2019-02-11 23:49:04 +01:00
bool doShow = false ;
2020-05-10 23:58:50 +02:00
2019-06-20 14:40:12 +02:00
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
2019-02-11 23:49:04 +01:00
{
_segment_index = i ;
2020-12-10 02:55:14 +01:00
// reset the segment runtime data if needed, called before isActive to ensure deleted
// segment's buffers are cleared
SEGENV . resetIfRequired ( ) ;
2021-01-09 00:35:48 +01:00
if ( ! SEGMENT . isActive ( ) ) continue ;
if ( nowUp > SEGENV . next_time | | _triggered | | ( doShow & & SEGMENT . mode = = 0 ) ) //last is temporary
2019-02-09 16:37:20 +01:00
{
2021-01-09 00:35:48 +01:00
if ( SEGMENT . grouping = = 0 ) SEGMENT . grouping = 1 ; //sanity check
doShow = true ;
uint16_t delay = FRAMETIME ;
if ( ! SEGMENT . getOption ( SEG_OPTION_FREEZE ) ) { //only run effect function if not frozen
2022-01-31 20:35:11 +01:00
SEGLEN = SEGMENT . virtualLength ( ) ;
2021-01-09 00:35:48 +01:00
_bri_t = SEGMENT . opacity ; _colors_t [ 0 ] = SEGMENT . colors [ 0 ] ; _colors_t [ 1 ] = SEGMENT . colors [ 1 ] ; _colors_t [ 2 ] = SEGMENT . colors [ 2 ] ;
2021-11-27 01:33:48 +01:00
uint8_t _cct_t = SEGMENT . cct ;
2021-01-09 00:35:48 +01:00
if ( ! IS_SEGMENT_ON ) _bri_t = 0 ;
for ( uint8_t t = 0 ; t < MAX_NUM_TRANSITIONS ; t + + ) {
if ( ( transitions [ t ] . segment & 0x3F ) ! = i ) continue ;
uint8_t slot = transitions [ t ] . segment > > 6 ;
if ( slot = = 0 ) _bri_t = transitions [ t ] . currentBri ( ) ;
2021-11-27 01:33:48 +01:00
if ( slot = = 1 ) _cct_t = transitions [ t ] . currentBri ( false , 1 ) ;
2021-01-09 00:35:48 +01:00
_colors_t [ slot ] = transitions [ t ] . currentColor ( SEGMENT . colors [ slot ] ) ;
2020-08-29 22:26:39 +02:00
}
2021-11-27 01:33:48 +01:00
if ( ! cctFromRgb | | correctWB ) busses . setSegmentCCT ( _cct_t , correctWB ) ;
2022-03-05 01:05:26 +01:00
for ( uint8_t c = 0 ; c < NUM_COLORS ; c + + ) {
_colors_t [ c ] = gamma32 ( _colors_t [ c ] ) ;
}
2021-01-09 00:35:48 +01:00
handle_palette ( ) ;
2022-03-08 02:16:33 +01:00
2021-01-09 00:35:48 +01:00
delay = ( this - > * _mode [ SEGMENT . mode ] ) ( ) ; //effect function
if ( SEGMENT . mode ! = FX_MODE_HALLOWEEN_EYES ) SEGENV . call + + ;
2019-06-20 14:40:12 +02:00
}
2021-01-09 00:35:48 +01:00
SEGENV . next_time = nowUp + delay ;
2019-02-09 16:37:20 +01:00
}
}
2022-01-31 20:35:11 +01:00
SEGLEN = 0 ;
2021-11-24 11:02:25 +01:00
busses . setSegmentCCT ( - 1 ) ;
2019-02-11 23:49:04 +01:00
if ( doShow ) {
2019-03-05 10:59:15 +01:00
yield ( ) ;
2019-02-11 23:49:04 +01:00
show ( ) ;
}
_triggered = false ;
2019-02-09 16:37:20 +01:00
}
2021-12-25 01:30:27 +01:00
void IRAM_ATTR WS2812FX : : setPixelColor ( uint16_t n , uint32_t c ) {
2021-10-26 20:35:45 +02:00
setPixelColor ( n , R ( c ) , G ( c ) , B ( c ) , W ( c ) ) ;
2019-02-09 16:37:20 +01:00
}
2020-08-20 00:04:02 +02:00
//used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring
2021-12-25 01:30:27 +01:00
uint16_t IRAM_ATTR WS2812FX : : realPixelIndex ( uint16_t i ) {
2020-01-14 10:57:23 +01:00
int16_t iGroup = i * SEGMENT . groupLength ( ) ;
2019-12-05 07:59:05 +01:00
2020-01-14 10:57:23 +01:00
/* reverse just an individual segment */
int16_t realIndex = iGroup ;
2020-08-20 00:04:02 +02:00
if ( IS_REVERSE ) {
if ( IS_MIRROR ) {
2021-02-24 20:23:32 +01:00
realIndex = ( SEGMENT . length ( ) - 1 ) / 2 - iGroup ; //only need to index half the pixels
2020-08-20 00:04:02 +02:00
} else {
2021-02-24 20:23:32 +01:00
realIndex = ( SEGMENT . length ( ) - 1 ) - iGroup ;
2020-08-20 00:04:02 +02:00
}
}
2019-12-05 07:59:05 +01:00
2020-01-14 10:57:23 +01:00
realIndex + = SEGMENT . start ;
return realIndex ;
2019-12-05 07:59:05 +01:00
}
2021-12-25 01:30:27 +01:00
void IRAM_ATTR WS2812FX : : setPixelColor ( uint16_t i , byte r , byte g , byte b , byte w )
2019-02-09 16:37:20 +01:00
{
2020-03-22 17:45:09 +01:00
if ( SEGLEN ) { //from segment
uint16_t realIndex = realPixelIndex ( i ) ;
2021-06-30 00:45:36 +02:00
uint16_t len = SEGMENT . length ( ) ;
2020-03-22 17:45:09 +01:00
2021-10-23 15:41:35 +02:00
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if ( _bri_t < 255 ) {
r = scale8 ( r , _bri_t ) ;
g = scale8 ( g , _bri_t ) ;
b = scale8 ( b , _bri_t ) ;
w = scale8 ( w , _bri_t ) ;
}
2021-10-26 20:35:45 +02:00
uint32_t col = RGBW32 ( r , g , b , w ) ;
2021-10-23 15:41:35 +02:00
/* Set all the pixels in the group */
2020-03-22 17:45:09 +01:00
for ( uint16_t j = 0 ; j < SEGMENT . grouping ; j + + ) {
2021-04-06 22:30:23 +02:00
uint16_t indexSet = realIndex + ( IS_REVERSE ? - j : j ) ;
2021-03-20 18:43:05 +01:00
if ( indexSet > = SEGMENT . start & & indexSet < SEGMENT . stop ) {
2020-08-20 00:04:02 +02:00
if ( IS_MIRROR ) { //set the corresponding mirrored pixel
2021-03-20 18:43:05 +01:00
uint16_t indexMir = SEGMENT . stop - indexSet + SEGMENT . start - 1 ;
2021-06-30 00:45:36 +02:00
/* offset/phase */
indexMir + = SEGMENT . offset ;
if ( indexMir > = SEGMENT . stop ) indexMir - = len ;
2021-04-06 22:30:23 +02:00
if ( indexMir < customMappingSize ) indexMir = customMappingTable [ indexMir ] ;
2021-12-02 13:09:53 +01:00
busses . setPixelColor ( indexMir , col ) ;
2020-08-20 00:04:02 +02:00
}
2021-06-30 00:45:36 +02:00
/* offset/phase */
2021-07-01 13:24:48 +02:00
indexSet + = SEGMENT . offset ;
if ( indexSet > = SEGMENT . stop ) indexSet - = len ;
2021-06-30 00:45:36 +02:00
2021-04-06 07:48:12 +02:00
if ( indexSet < customMappingSize ) indexSet = customMappingTable [ indexSet ] ;
2021-12-02 13:09:53 +01:00
busses . setPixelColor ( indexSet , col ) ;
2020-08-07 00:50:19 +02:00
}
2020-01-14 10:57:23 +01:00
}
2020-03-22 17:45:09 +01:00
} else { //live data, etc.
if ( i < customMappingSize ) i = customMappingTable [ i ] ;
2021-10-26 20:35:45 +02:00
busses . setPixelColor ( i , RGBW32 ( r , g , b , w ) ) ;
2019-02-09 16:37:20 +01:00
}
}
//DISCLAIMER
//The following function attemps to calculate the current LED power usage,
//and will limit the brightness to stay below a set amperage threshold.
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
//Stay safe with high amperage and have a reasonable safety margin!
//I am NOT to be held liable for burned down garages!
2019-11-12 19:33:34 +01:00
//fine tune power estimation constants for your setup
2019-02-09 16:37:20 +01:00
# define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
2021-10-11 02:19:53 +02:00
void WS2812FX : : estimateCurrentAndLimitBri ( ) {
2019-02-09 16:37:20 +01:00
//power limit calculation
//each LED can draw up 195075 "power units" (approx. 53mA)
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step
//so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU)
2020-01-13 18:09:20 +01:00
bool useWackyWS2815PowerModel = false ;
byte actualMilliampsPerLed = milliampsPerLed ;
if ( milliampsPerLed = = 255 ) {
useWackyWS2815PowerModel = true ;
actualMilliampsPerLed = 12 ; // from testing an actual strip
}
2021-10-11 02:19:53 +02:00
if ( ablMilliampsMax < 150 | | actualMilliampsPerLed = = 0 ) { //0 mA per LED and too low numbers turn off calculation
currentMilliamps = 0 ;
busses . setBrightness ( _brightness ) ;
return ;
}
2019-02-09 16:37:20 +01:00
2021-10-11 02:19:53 +02:00
uint16_t pLen = getLengthPhysical ( ) ;
uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed ;
uint32_t powerBudget = ( ablMilliampsMax - MA_FOR_ESP ) * puPerMilliamp ; //100mA for ESP power
if ( powerBudget > puPerMilliamp * pLen ) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget - = puPerMilliamp * pLen ;
} else {
powerBudget = 0 ;
}
2019-02-09 16:37:20 +01:00
2021-10-11 02:19:53 +02:00
uint32_t powerSum = 0 ;
for ( uint8_t b = 0 ; b < busses . getNumBusses ( ) ; b + + ) {
Bus * bus = busses . getBus ( b ) ;
if ( bus - > getType ( ) > = TYPE_NET_DDP_RGB ) continue ; //exclude non-physical network busses
uint16_t len = bus - > getLength ( ) ;
uint32_t busPowerSum = 0 ;
for ( uint16_t i = 0 ; i < len ; i + + ) { //sum up the usage of each LED
uint32_t c = bus - > getPixelColor ( i ) ;
2021-10-26 20:35:45 +02:00
byte r = R ( c ) , g = G ( c ) , b = B ( c ) , w = W ( c ) ;
2020-01-13 18:09:20 +01:00
2021-10-11 02:19:53 +02:00
if ( useWackyWS2815PowerModel ) { //ignore white component on WS2815 power calculation
busPowerSum + = ( MAX ( MAX ( r , g ) , b ) ) * 3 ;
} else {
busPowerSum + = ( r + g + b + w ) ;
2020-01-13 18:09:20 +01:00
}
2019-02-09 16:37:20 +01:00
}
2021-10-11 02:19:53 +02:00
if ( bus - > isRgbw ( ) ) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum * = 3 ;
busPowerSum = busPowerSum > > 2 ; //same as /= 4
2019-02-09 16:37:20 +01:00
}
2021-10-11 02:19:53 +02:00
powerSum + = busPowerSum ;
}
2019-02-09 16:37:20 +01:00
2021-10-11 02:19:53 +02:00
uint32_t powerSum0 = powerSum ;
powerSum * = _brightness ;
if ( powerSum > powerBudget ) //scale brightness down to stay in current limit
{
float scale = ( float ) powerBudget / ( float ) powerSum ;
uint16_t scaleI = scale * 255 ;
uint8_t scaleB = ( scaleI > 255 ) ? 255 : scaleI ;
uint8_t newBri = scale8 ( _brightness , scaleB ) ;
busses . setBrightness ( newBri ) ; //to keep brightness uniform, sets virtual busses too
currentMilliamps = ( powerSum0 * newBri ) / puPerMilliamp ;
2019-02-09 16:37:20 +01:00
} else {
2021-10-11 02:19:53 +02:00
currentMilliamps = powerSum / puPerMilliamp ;
2021-01-21 01:21:16 +01:00
busses . setBrightness ( _brightness ) ;
2019-02-09 16:37:20 +01:00
}
2021-10-11 02:19:53 +02:00
currentMilliamps + = MA_FOR_ESP ; //add power of ESP back to estimate
currentMilliamps + = pLen ; //add standby power back to estimate
}
void WS2812FX : : show ( void ) {
// avoid race condition, caputre _callback value
show_callback callback = _callback ;
if ( callback ) callback ( ) ;
estimateCurrentAndLimitBri ( ) ;
2019-02-09 16:37:20 +01:00
2020-12-10 05:29:53 +01:00
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
2021-01-21 01:21:16 +01:00
busses . show ( ) ;
2021-02-05 01:33:26 +01:00
unsigned long now = millis ( ) ;
unsigned long diff = now - _lastShow ;
uint16_t fpsCurr = 200 ;
if ( diff > 0 ) fpsCurr = 1000 / diff ;
_cumulativeFps = ( 3 * _cumulativeFps + fpsCurr ) > > 2 ;
_lastShow = now ;
2019-02-09 16:37:20 +01:00
}
2020-12-10 05:29:53 +01:00
/**
* Returns a true value if any of the strips are still being updated .
* On some hardware ( ESP32 ) , strip updates are done asynchronously .
*/
bool WS2812FX : : isUpdating ( ) {
2021-01-21 01:21:16 +01:00
return ! busses . canAllShow ( ) ;
2020-12-10 05:29:53 +01:00
}
2021-02-05 01:33:26 +01:00
/**
* Returns the refresh rate of the LED strip . Useful for finding out whether a given setup is fast enough .
* Only updates on show ( ) or is set to 0 fps if last show is more than 2 secs ago , so accurary varies
*/
uint16_t WS2812FX : : getFps ( ) {
if ( millis ( ) - _lastShow > 2000 ) return 0 ;
return _cumulativeFps + 1 ;
}
2021-12-25 01:30:27 +01:00
uint8_t WS2812FX : : getTargetFps ( ) {
return _targetFps ;
}
void WS2812FX : : setTargetFps ( uint8_t fps ) {
2021-12-30 01:48:27 +01:00
if ( fps > 0 & & fps < = 120 ) _targetFps = fps ;
2021-12-25 01:30:27 +01:00
_frametime = 1000 / _targetFps ;
}
2020-12-10 05:29:53 +01:00
/**
* Forces the next frame to be computed on all active segments .
*/
2019-02-09 16:37:20 +01:00
void WS2812FX : : trigger ( ) {
_triggered = true ;
}
2019-06-20 14:40:12 +02:00
void WS2812FX : : setMode ( uint8_t segid , uint8_t m ) {
if ( segid > = MAX_NUM_SEGMENTS ) return ;
if ( m > = MODE_COUNT ) m = MODE_COUNT - 1 ;
if ( _segments [ segid ] . mode ! = m )
{
2022-02-09 08:43:35 +01:00
_segment_runtimes [ segid ] . markForReset ( ) ;
2019-06-20 14:40:12 +02:00
_segments [ segid ] . mode = m ;
}
2019-02-09 16:37:20 +01:00
}
2019-02-22 22:53:33 +01:00
uint8_t WS2812FX : : getModeCount ( )
{
return MODE_COUNT ;
}
uint8_t WS2812FX : : getPaletteCount ( )
{
2020-02-25 02:19:12 +01:00
return 13 + GRADIENT_PALETTE_COUNT ;
2019-02-22 22:53:33 +01:00
}
2022-01-15 00:06:06 +01:00
/*
2019-06-20 14:40:12 +02:00
void WS2812FX : : setColor ( uint8_t slot , uint8_t r , uint8_t g , uint8_t b , uint8_t w ) {
2021-10-26 20:35:45 +02:00
setColor ( slot , RGBW32 ( r , g , b , w ) ) ;
2019-02-09 16:37:20 +01:00
}
2022-01-15 00:06:06 +01:00
*/
2022-02-20 22:24:11 +01:00
//applies to all active and selected segments
2019-06-20 14:40:12 +02:00
void WS2812FX : : setColor ( uint8_t slot , uint32_t c ) {
if ( slot > = NUM_COLORS ) return ;
2020-03-14 11:28:42 +01:00
2022-02-20 22:24:11 +01:00
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
if ( _segments [ i ] . isActive ( ) & & _segments [ i ] . isSelected ( ) ) {
_segments [ i ] . setColor ( slot , c , i ) ;
2019-12-02 12:41:35 +01:00
}
2020-03-14 11:28:42 +01:00
}
2019-02-09 16:37:20 +01:00
}
2022-02-23 19:20:07 +01:00
void WS2812FX : : setCCT ( uint16_t k ) {
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
if ( _segments [ i ] . isActive ( ) & & _segments [ i ] . isSelected ( ) ) {
_segments [ i ] . setCCT ( k , i ) ;
}
}
}
2019-02-09 16:37:20 +01:00
void WS2812FX : : setBrightness ( uint8_t b ) {
2020-12-14 23:32:57 +01:00
if ( gammaCorrectBri ) b = gamma8 ( b ) ;
2019-02-09 16:37:20 +01:00
if ( _brightness = = b ) return ;
2020-12-14 23:32:57 +01:00
_brightness = b ;
if ( _brightness = = 0 ) { //unfreeze all segments on power off
2020-08-29 22:26:39 +02:00
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
_segments [ i ] . setOption ( SEG_OPTION_FREEZE , false ) ;
}
}
2021-12-03 20:36:37 +01:00
unsigned long t = millis ( ) ;
if ( _segment_runtimes [ 0 ] . next_time > t + 22 & & t - _lastShow > MIN_SHOW_DELAY ) show ( ) ; //apply brightness change immediately if no refresh soon
2019-02-09 16:37:20 +01:00
}
uint8_t WS2812FX : : getBrightness ( void ) {
return _brightness ;
}
2019-03-06 01:20:38 +01:00
uint8_t WS2812FX : : getMaxSegments ( void ) {
return MAX_NUM_SEGMENTS ;
2019-02-09 16:37:20 +01:00
}
2022-02-23 19:20:07 +01:00
uint8_t WS2812FX : : getFirstSelectedSegId ( void )
{
2019-06-20 14:40:12 +02:00
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
2022-02-23 19:20:07 +01:00
if ( _segments [ i ] . isActive ( ) & & _segments [ i ] . isSelected ( ) ) return i ;
2019-06-20 14:40:12 +02:00
}
2022-02-23 19:20:07 +01:00
// if none selected, use the main segment
return getMainSegmentId ( ) ;
}
void WS2812FX : : setMainSegmentId ( uint8_t n ) {
if ( n > = MAX_NUM_SEGMENTS ) return ;
//use supplied n if active, or first active
2022-02-20 22:24:11 +01:00
if ( _segments [ n ] . isActive ( ) ) {
_mainSegment = n ; return ;
}
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
2019-06-20 14:40:12 +02:00
{
2022-02-20 22:24:11 +01:00
if ( _segments [ i ] . isActive ( ) ) {
_mainSegment = i ; return ;
}
2019-06-20 14:40:12 +02:00
}
2022-02-20 22:24:11 +01:00
_mainSegment = 0 ;
return ;
}
2019-06-20 14:40:12 +02:00
2019-11-30 19:17:25 +01:00
uint8_t WS2812FX : : getMainSegmentId ( void ) {
2022-02-20 22:24:11 +01:00
return _mainSegment ;
}
uint8_t WS2812FX : : getLastActiveSegmentId ( void ) {
for ( uint8_t i = MAX_NUM_SEGMENTS - 1 ; i > 0 ; i - - ) {
2020-06-22 12:30:31 +02:00
if ( _segments [ i ] . isActive ( ) ) return i ;
}
return 0 ;
2019-06-20 14:40:12 +02:00
}
2021-09-20 21:22:50 +02:00
uint8_t WS2812FX : : getActiveSegmentsNum ( void ) {
uint8_t c = 0 ;
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
if ( _segments [ i ] . isActive ( ) ) c + + ;
}
return c ;
}
2019-02-09 16:37:20 +01:00
uint32_t WS2812FX : : getPixelColor ( uint16_t i )
{
2020-03-21 00:57:54 +01:00
i = realPixelIndex ( i ) ;
2021-06-30 00:45:36 +02:00
if ( SEGLEN ) {
/* offset/phase */
i + = SEGMENT . offset ;
if ( i > = SEGMENT . stop ) i - = SEGMENT . length ( ) ;
}
2020-03-21 00:57:54 +01:00
if ( i < customMappingSize ) i = customMappingTable [ i ] ;
2021-02-24 20:23:32 +01:00
if ( i > = _length ) return 0 ;
2020-01-14 10:57:23 +01:00
2021-01-21 01:21:16 +01:00
return busses . getPixelColor ( i ) ;
2019-02-09 16:37:20 +01:00
}
2019-03-06 01:20:38 +01:00
WS2812FX : : Segment & WS2812FX : : getSegment ( uint8_t id ) {
2022-02-25 10:24:00 +01:00
if ( id > = MAX_NUM_SEGMENTS ) return _segments [ getMainSegmentId ( ) ] ;
2019-03-06 01:20:38 +01:00
return _segments [ id ] ;
2019-02-09 16:37:20 +01:00
}
2022-02-23 19:20:07 +01:00
WS2812FX : : Segment & WS2812FX : : getFirstSelectedSeg ( void ) {
return _segments [ getFirstSelectedSegId ( ) ] ;
}
2022-02-20 22:24:11 +01:00
WS2812FX : : Segment & WS2812FX : : getMainSegment ( void ) {
return _segments [ getMainSegmentId ( ) ] ;
2019-02-09 16:37:20 +01:00
}
WS2812FX : : Segment * WS2812FX : : getSegments ( void ) {
return _segments ;
}
2019-11-18 20:43:27 +01:00
uint32_t WS2812FX : : getLastShow ( void ) {
return _lastShow ;
}
2021-10-11 02:19:53 +02:00
uint16_t WS2812FX : : getLengthTotal ( void ) {
return _length ;
}
uint16_t WS2812FX : : getLengthPhysical ( void ) {
uint16_t len = 0 ;
for ( uint8_t b = 0 ; b < busses . getNumBusses ( ) ; b + + ) {
Bus * bus = busses . getBus ( b ) ;
if ( bus - > getType ( ) > = TYPE_NET_DDP_RGB ) continue ; //exclude non-physical network busses
len + = bus - > getLength ( ) ;
}
return len ;
}
2022-02-10 16:09:16 +01:00
uint8_t WS2812FX : : Segment : : differs ( Segment & b ) {
uint8_t d = 0 ;
if ( start ! = b . start ) d | = SEG_DIFFERS_BOUNDS ;
if ( stop ! = b . stop ) d | = SEG_DIFFERS_BOUNDS ;
if ( offset ! = b . offset ) d | = SEG_DIFFERS_GSO ;
if ( grouping ! = b . grouping ) d | = SEG_DIFFERS_GSO ;
if ( spacing ! = b . spacing ) d | = SEG_DIFFERS_GSO ;
if ( opacity ! = b . opacity ) d | = SEG_DIFFERS_BRI ;
if ( mode ! = b . mode ) d | = SEG_DIFFERS_FX ;
if ( speed ! = b . speed ) d | = SEG_DIFFERS_FX ;
if ( intensity ! = b . intensity ) d | = SEG_DIFFERS_FX ;
if ( palette ! = b . palette ) d | = SEG_DIFFERS_FX ;
2022-02-20 22:24:11 +01:00
if ( ( options & 0b00101110 ) ! = ( b . options & 0b00101110 ) ) d | = SEG_DIFFERS_OPT ;
if ( ( options & 0x01 ) ! = ( b . options & 0x01 ) ) d | = SEG_DIFFERS_SEL ;
2022-02-10 16:09:16 +01:00
for ( uint8_t i = 0 ; i < NUM_COLORS ; i + + )
{
if ( colors [ i ] ! = b . colors [ i ] ) d | = SEG_DIFFERS_COL ;
}
return d ;
}
2022-03-05 01:05:26 +01:00
void WS2812FX : : Segment : : refreshLightCapabilities ( ) {
if ( ! isActive ( ) ) {
_capabilities = 0 ; return ;
}
2022-02-10 13:24:51 +01:00
uint8_t capabilities = 0 ;
for ( uint8_t b = 0 ; b < busses . getNumBusses ( ) ; b + + ) {
Bus * bus = busses . getBus ( b ) ;
if ( bus = = nullptr | | bus - > getLength ( ) = = 0 ) break ;
2022-03-09 13:39:51 +01:00
if ( ! bus - > isOk ( ) ) continue ;
2022-02-10 13:24:51 +01:00
if ( bus - > getStart ( ) > = stop ) continue ;
if ( bus - > getStart ( ) + bus - > getLength ( ) < = start ) continue ;
uint8_t type = bus - > getType ( ) ;
2022-03-09 13:39:51 +01:00
if ( type ! = TYPE_ANALOG_1CH & & ( cctFromRgb | | type ! = TYPE_ANALOG_2CH ) ) capabilities | = 0x01 ; // segment supports RGB (full color)
if ( bus - > isRgbw ( ) ) capabilities | = 0x02 ; // segment supports white channel
2022-02-10 13:24:51 +01:00
if ( ! cctFromRgb ) {
switch ( type ) {
case TYPE_ANALOG_5CH :
case TYPE_ANALOG_2CH :
capabilities | = 0x04 ; //segment supports white CCT
}
}
if ( correctWB & & type ! = TYPE_ANALOG_1CH ) capabilities | = 0x04 ; //white balance correction (uses CCT slider)
2022-03-09 13:39:51 +01:00
bool whiteSlider = ( bus - > getAutoWhiteMode ( ) = = RGBW_MODE_DUAL | | bus - > getAutoWhiteMode ( ) = = RGBW_MODE_MANUAL_ONLY ) ;
if ( whiteSlider | | ( bus - > isRgbw ( ) & & ! ( capabilities & 0x01 ) ) ) capabilities | = 0x08 ; // segment allows white channel adjustments or has white and is not RGB -> show W slider
2022-02-10 13:24:51 +01:00
}
2022-03-05 01:05:26 +01:00
_capabilities = capabilities ;
2022-02-10 13:24:51 +01:00
}
2022-02-20 22:24:11 +01:00
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
//returns if there is an RGBW bus (supports RGB and White, not only white)
//not influenced by auto-white mode, also true if white slider does not affect output white channel
bool WS2812FX : : hasRGBWBus ( void ) {
for ( uint8_t b = 0 ; b < busses . getNumBusses ( ) ; b + + ) {
Bus * bus = busses . getBus ( b ) ;
if ( bus = = nullptr | | bus - > getLength ( ) = = 0 ) break ;
switch ( bus - > getType ( ) ) {
case TYPE_SK6812_RGBW :
case TYPE_TM1814 :
case TYPE_ANALOG_4CH :
return true ;
}
}
return false ;
}
2021-11-28 04:01:58 +01:00
bool WS2812FX : : hasCCTBus ( void ) {
if ( cctFromRgb & & ! correctWB ) return false ;
for ( uint8_t b = 0 ; b < busses . getNumBusses ( ) ; b + + ) {
Bus * bus = busses . getBus ( b ) ;
if ( bus = = nullptr | | bus - > getLength ( ) = = 0 ) break ;
switch ( bus - > getType ( ) ) {
case TYPE_ANALOG_5CH :
case TYPE_ANALOG_2CH :
return true ;
}
}
return false ;
}
2021-12-02 13:09:53 +01:00
void WS2812FX : : setSegment ( uint8_t n , uint16_t i1 , uint16_t i2 , uint8_t grouping , uint8_t spacing , uint16_t offset ) {
2019-03-06 01:20:38 +01:00
if ( n > = MAX_NUM_SEGMENTS ) return ;
Segment & seg = _segments [ n ] ;
2020-01-05 22:30:27 +01:00
2020-01-14 10:57:23 +01:00
//return if neither bounds nor grouping have changed
2022-03-05 01:05:26 +01:00
bool boundsUnchanged = ( seg . start = = i1 & & seg . stop = = i2 ) ;
if ( boundsUnchanged
2021-12-02 13:09:53 +01:00
& & ( ! grouping | | ( seg . grouping = = grouping & & seg . spacing = = spacing ) )
& & ( offset = = UINT16_MAX | | offset = = seg . offset ) ) return ;
2020-01-02 20:41:15 +01:00
2020-01-14 10:57:23 +01:00
if ( seg . stop ) setRange ( seg . start , seg . stop - 1 , 0 ) ; //turn old segment range off
if ( i2 < = i1 ) //disable segment
{
2021-09-18 13:15:19 +02:00
seg . stop = 0 ;
if ( seg . name ) {
delete [ ] seg . name ;
seg . name = nullptr ;
}
2022-02-23 19:20:07 +01:00
// if main segment is deleted, set first active as main segment
2022-02-20 22:24:11 +01:00
if ( n = = _mainSegment ) setMainSegmentId ( 0 ) ;
2020-01-27 00:45:30 +01:00
return ;
2019-12-05 07:59:05 +01:00
}
2020-01-14 10:57:23 +01:00
if ( i1 < _length ) seg . start = i1 ;
seg . stop = i2 ;
if ( i2 > _length ) seg . stop = _length ;
if ( grouping ) {
seg . grouping = grouping ;
seg . spacing = spacing ;
2019-06-20 14:40:12 +02:00
}
2021-12-02 13:09:53 +01:00
if ( offset < UINT16_MAX ) seg . offset = offset ;
2022-02-09 08:43:35 +01:00
_segment_runtimes [ n ] . markForReset ( ) ;
2022-03-05 01:05:26 +01:00
if ( ! boundsUnchanged ) seg . refreshLightCapabilities ( ) ;
2019-02-09 16:37:20 +01:00
}
2021-12-20 11:29:03 +01:00
void WS2812FX : : restartRuntime ( ) {
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + ) {
2022-02-09 08:43:35 +01:00
_segment_runtimes [ i ] . markForReset ( ) ;
2021-12-20 11:29:03 +01:00
}
}
2019-02-09 16:37:20 +01:00
void WS2812FX : : resetSegments ( ) {
2021-12-21 20:43:49 +01:00
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + ) if ( _segments [ i ] . name ) delete [ ] _segments [ i ] . name ;
2022-02-20 22:24:11 +01:00
_mainSegment = 0 ;
2019-02-09 16:37:20 +01:00
memset ( _segments , 0 , sizeof ( _segments ) ) ;
2019-12-31 11:11:05 +01:00
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
2019-02-09 16:37:20 +01:00
_segment_index = 0 ;
2019-03-06 01:20:38 +01:00
_segments [ 0 ] . mode = DEFAULT_MODE ;
_segments [ 0 ] . colors [ 0 ] = DEFAULT_COLOR ;
_segments [ 0 ] . start = 0 ;
_segments [ 0 ] . speed = DEFAULT_SPEED ;
2020-09-07 21:01:10 +02:00
_segments [ 0 ] . intensity = DEFAULT_INTENSITY ;
2020-01-14 10:57:23 +01:00
_segments [ 0 ] . stop = _length ;
_segments [ 0 ] . grouping = 1 ;
2020-04-24 23:35:39 +02:00
_segments [ 0 ] . setOption ( SEG_OPTION_SELECTED , 1 ) ;
_segments [ 0 ] . setOption ( SEG_OPTION_ON , 1 ) ;
2020-04-23 23:52:33 +02:00
_segments [ 0 ] . opacity = 255 ;
2021-11-26 20:18:38 +01:00
_segments [ 0 ] . cct = 127 ;
2020-04-23 23:52:33 +02:00
2019-12-13 01:23:07 +01:00
for ( uint16_t i = 1 ; i < MAX_NUM_SEGMENTS ; i + + )
2019-02-09 16:37:20 +01:00
{
2019-12-13 01:23:07 +01:00
_segments [ i ] . colors [ 0 ] = color_wheel ( i * 51 ) ;
2020-01-14 10:57:23 +01:00
_segments [ i ] . grouping = 1 ;
2020-04-24 23:35:39 +02:00
_segments [ i ] . setOption ( SEG_OPTION_ON , 1 ) ;
2020-04-23 23:52:33 +02:00
_segments [ i ] . opacity = 255 ;
2021-11-26 20:18:38 +01:00
_segments [ i ] . cct = 127 ;
2020-09-07 21:01:10 +02:00
_segments [ i ] . speed = DEFAULT_SPEED ;
_segments [ i ] . intensity = DEFAULT_INTENSITY ;
2022-02-09 08:43:35 +01:00
_segment_runtimes [ i ] . markForReset ( ) ;
2019-02-09 16:37:20 +01:00
}
2022-02-09 08:43:35 +01:00
_segment_runtimes [ 0 ] . markForReset ( ) ;
2019-02-09 16:37:20 +01:00
}
2022-02-10 16:09:16 +01:00
void WS2812FX : : makeAutoSegments ( bool forceReset ) {
2021-10-11 02:19:33 +02:00
if ( autoSegments ) { //make one segment per bus
2021-10-30 14:42:17 +02:00
uint16_t segStarts [ MAX_NUM_SEGMENTS ] = { 0 } ;
uint16_t segStops [ MAX_NUM_SEGMENTS ] = { 0 } ;
2021-10-11 02:19:33 +02:00
uint8_t s = 0 ;
for ( uint8_t i = 0 ; i < busses . getNumBusses ( ) ; i + + ) {
Bus * b = busses . getBus ( i ) ;
segStarts [ s ] = b - > getStart ( ) ;
segStops [ s ] = segStarts [ s ] + b - > getLength ( ) ;
//check for overlap with previous segments
for ( uint8_t j = 0 ; j < s ; j + + ) {
if ( segStops [ j ] > segStarts [ s ] & & segStarts [ j ] < segStops [ s ] ) {
//segments overlap, merge
segStarts [ j ] = min ( segStarts [ s ] , segStarts [ j ] ) ;
segStops [ j ] = max ( segStops [ s ] , segStops [ j ] ) ; segStops [ s ] = 0 ;
s - - ;
}
}
s + + ;
}
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + ) {
setSegment ( i , segStarts [ i ] , segStops [ i ] ) ;
2021-09-12 01:00:54 +02:00
}
} else {
2022-02-10 16:09:16 +01:00
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced
2021-10-11 02:19:33 +02:00
uint8_t mainSeg = getMainSegmentId ( ) ;
2022-02-20 22:24:11 +01:00
if ( forceReset ) {
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + ) {
setSegment ( i , 0 , 0 ) ;
}
}
2021-10-11 02:19:33 +02:00
2022-02-10 16:09:16 +01:00
if ( forceReset ) {
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + ) {
setSegment ( i , 0 , 0 ) ;
}
}
2021-10-11 02:19:33 +02:00
if ( getActiveSegmentsNum ( ) < 2 ) {
setSegment ( mainSeg , 0 , _length ) ;
}
}
fixInvalidSegments ( ) ;
}
void WS2812FX : : fixInvalidSegments ( ) {
//make sure no segment is longer than total (sanity check)
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
if ( _segments [ i ] . start > = _length ) setSegment ( i , 0 , 0 ) ;
if ( _segments [ i ] . stop > _length ) setSegment ( i , _segments [ i ] . start , _length ) ;
2022-03-05 01:05:26 +01:00
// this is always called as the last step after finalizeInit(), update covered bus types
getSegment ( i ) . refreshLightCapabilities ( ) ;
2021-10-11 02:19:33 +02:00
}
}
//true if all segments align with a bus, or if a segment covers the total length
bool WS2812FX : : checkSegmentAlignment ( ) {
for ( uint8_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
if ( _segments [ i ] . start > = _segments [ i ] . stop ) continue ; //inactive segment
bool aligned = false ;
for ( uint8_t b = 0 ; b < busses . getNumBusses ( ) ; b + + ) {
Bus * bus = busses . getBus ( b ) ;
if ( _segments [ i ] . start = = bus - > getStart ( ) & & _segments [ i ] . stop = = bus - > getStart ( ) + bus - > getLength ( ) ) aligned = true ;
}
if ( _segments [ i ] . start = = 0 & & _segments [ i ] . stop = = _length ) aligned = true ;
if ( ! aligned ) return false ;
2021-02-27 12:06:14 +01:00
}
2021-10-11 02:19:33 +02:00
return true ;
2021-02-27 12:06:14 +01:00
}
2020-08-29 22:26:39 +02:00
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
2022-01-31 20:35:11 +01:00
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
2021-12-03 20:36:37 +01:00
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
2022-01-31 20:35:11 +01:00
uint8_t WS2812FX : : setPixelSegment ( uint8_t n )
2020-08-29 22:26:39 +02:00
{
2022-01-31 20:35:11 +01:00
uint8_t prevSegId = _segment_index ;
2020-08-29 22:26:39 +02:00
if ( n < MAX_NUM_SEGMENTS ) {
_segment_index = n ;
2022-01-31 20:35:11 +01:00
SEGLEN = SEGMENT . virtualLength ( ) ;
2020-08-29 22:26:39 +02:00
}
2022-01-31 20:35:11 +01:00
return prevSegId ;
2020-08-29 22:26:39 +02:00
}
2019-02-09 16:37:20 +01:00
void WS2812FX : : setRange ( uint16_t i , uint16_t i2 , uint32_t col )
{
if ( i2 > = i )
{
2020-01-02 22:10:59 +01:00
for ( uint16_t x = i ; x < = i2 ; x + + ) setPixelColor ( x , col ) ;
2019-02-09 16:37:20 +01:00
} else
{
2020-01-02 22:10:59 +01:00
for ( uint16_t x = i2 ; x < = i ; x + + ) setPixelColor ( x , col ) ;
2019-02-09 16:37:20 +01:00
}
}
2020-01-02 22:10:59 +01:00
void WS2812FX : : setShowCallback ( show_callback cb )
2019-02-09 16:37:20 +01:00
{
2020-01-02 22:10:59 +01:00
_callback = cb ;
2019-02-09 16:37:20 +01:00
}
2021-01-09 00:35:48 +01:00
void WS2812FX : : setTransition ( uint16_t t )
{
_transitionDur = t ;
}
2019-02-09 16:37:20 +01:00
void WS2812FX : : setTransitionMode ( bool t )
{
2021-12-03 20:36:37 +01:00
unsigned long waitMax = millis ( ) + 20 ; //refresh after 20 ms if transition enabled
2020-06-22 12:30:31 +02:00
for ( uint16_t i = 0 ; i < MAX_NUM_SEGMENTS ; i + + )
{
2021-12-03 20:36:37 +01:00
_segments [ i ] . setOption ( SEG_OPTION_TRANSITIONAL , t ) ;
2020-06-22 12:30:31 +02:00
2021-12-03 20:36:37 +01:00
if ( t & & _segments [ i ] . mode = = FX_MODE_STATIC & & _segment_runtimes [ i ] . next_time > waitMax )
_segment_runtimes [ i ] . next_time = waitMax ;
2020-06-22 12:30:31 +02:00
}
2019-02-09 16:37:20 +01:00
}
/*
* color blend function
*/
2021-12-25 01:30:27 +01:00
uint32_t IRAM_ATTR WS2812FX : : color_blend ( uint32_t color1 , uint32_t color2 , uint16_t blend , bool b16 ) {
2019-02-09 16:37:20 +01:00
if ( blend = = 0 ) return color1 ;
2021-01-09 00:35:48 +01:00
uint16_t blendmax = b16 ? 0xFFFF : 0xFF ;
if ( blend = = blendmax ) return color2 ;
uint8_t shift = b16 ? 16 : 8 ;
2019-02-09 16:37:20 +01:00
2021-10-26 20:35:45 +02:00
uint32_t w1 = W ( color1 ) ;
uint32_t r1 = R ( color1 ) ;
uint32_t g1 = G ( color1 ) ;
uint32_t b1 = B ( color1 ) ;
2019-10-07 23:22:56 +02:00
2021-10-26 20:35:45 +02:00
uint32_t w2 = W ( color2 ) ;
uint32_t r2 = R ( color2 ) ;
uint32_t g2 = G ( color2 ) ;
uint32_t b2 = B ( color2 ) ;
2019-02-09 16:37:20 +01:00
2021-01-09 00:35:48 +01:00
uint32_t w3 = ( ( w2 * blend ) + ( w1 * ( blendmax - blend ) ) ) > > shift ;
uint32_t r3 = ( ( r2 * blend ) + ( r1 * ( blendmax - blend ) ) ) > > shift ;
uint32_t g3 = ( ( g2 * blend ) + ( g1 * ( blendmax - blend ) ) ) > > shift ;
uint32_t b3 = ( ( b2 * blend ) + ( b1 * ( blendmax - blend ) ) ) > > shift ;
2019-02-09 16:37:20 +01:00
2021-10-26 20:35:45 +02:00
return RGBW32 ( r3 , g3 , b3 , w3 ) ;
2019-02-09 16:37:20 +01:00
}
/*
* Fills segment with color
*/
void WS2812FX : : fill ( uint32_t c ) {
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-02-09 16:37:20 +01:00
setPixelColor ( i , c ) ;
}
}
2020-08-30 10:57:42 +02:00
/*
* Blends the specified color with the existing pixel color .
*/
void WS2812FX : : blendPixelColor ( uint16_t n , uint32_t color , uint8_t blend )
{
setPixelColor ( n , color_blend ( getPixelColor ( n ) , color , blend ) ) ;
}
2019-02-11 23:49:04 +01:00
/*
* fade out function , higher rate = quicker fade
*/
void WS2812FX : : fade_out ( uint8_t rate ) {
rate = ( 255 - rate ) > > 1 ;
float mappedRate = float ( rate ) + 1.1 ;
2019-05-22 00:23:09 +02:00
uint32_t color = SEGCOLOR ( 1 ) ; // target color
2021-10-26 20:35:45 +02:00
int w2 = W ( color ) ;
int r2 = R ( color ) ;
int g2 = G ( color ) ;
int b2 = B ( color ) ;
2019-02-11 23:49:04 +01:00
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + ) {
2019-02-11 23:49:04 +01:00
color = getPixelColor ( i ) ;
2021-10-26 20:35:45 +02:00
int w1 = W ( color ) ;
int r1 = R ( color ) ;
int g1 = G ( color ) ;
int b1 = B ( color ) ;
2019-02-11 23:49:04 +01:00
int wdelta = ( w2 - w1 ) / mappedRate ;
int rdelta = ( r2 - r1 ) / mappedRate ;
int gdelta = ( g2 - g1 ) / mappedRate ;
int bdelta = ( b2 - b1 ) / mappedRate ;
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
wdelta + = ( w2 = = w1 ) ? 0 : ( w2 > w1 ) ? 1 : - 1 ;
rdelta + = ( r2 = = r1 ) ? 0 : ( r2 > r1 ) ? 1 : - 1 ;
gdelta + = ( g2 = = g1 ) ? 0 : ( g2 > g1 ) ? 1 : - 1 ;
bdelta + = ( b2 = = b1 ) ? 0 : ( b2 > b1 ) ? 1 : - 1 ;
setPixelColor ( i , r1 + rdelta , g1 + gdelta , b1 + bdelta , w1 + wdelta ) ;
}
}
/*
* blurs segment content , source : FastLED colorutils . cpp
*/
void WS2812FX : : blur ( uint8_t blur_amount )
{
uint8_t keep = 255 - blur_amount ;
uint8_t seep = blur_amount > > 1 ;
CRGB carryover = CRGB : : Black ;
2020-01-14 10:57:23 +01:00
for ( uint16_t i = 0 ; i < SEGLEN ; i + + )
2019-02-11 23:49:04 +01:00
{
2019-08-30 15:39:34 +02:00
CRGB cur = col_to_crgb ( getPixelColor ( i ) ) ;
2019-02-11 23:49:04 +01:00
CRGB part = cur ;
part . nscale8 ( seep ) ;
cur . nscale8 ( keep ) ;
cur + = carryover ;
2020-01-14 10:57:23 +01:00
if ( i > 0 ) {
2019-02-11 23:49:04 +01:00
uint32_t c = getPixelColor ( i - 1 ) ;
2021-10-26 20:35:45 +02:00
uint8_t r = R ( c ) ;
uint8_t g = G ( c ) ;
uint8_t b = B ( c ) ;
2019-02-11 23:49:04 +01:00
setPixelColor ( i - 1 , qadd8 ( r , part . red ) , qadd8 ( g , part . green ) , qadd8 ( b , part . blue ) ) ;
}
setPixelColor ( i , cur . red , cur . green , cur . blue ) ;
carryover = part ;
}
}
2021-12-25 01:30:27 +01:00
uint16_t IRAM_ATTR WS2812FX : : triwave16 ( uint16_t in )
2019-12-04 12:15:12 +01:00
{
if ( in < 0x8000 ) return in * 2 ;
return 0xFFFF - ( in - 0x8000 ) * 2 ;
}
2021-12-25 01:30:27 +01:00
uint8_t IRAM_ATTR WS2812FX : : sin_gap ( uint16_t in ) {
2021-04-11 00:50:14 +02:00
if ( in & 0x100 ) return 0 ;
//if (in > 255) return 0;
return sin8 ( in + 192 ) ; //correct phase shift of sine so that it starts and stops at 0
}
2020-09-27 14:42:14 +02:00
/*
* Generates a tristate square wave w / attac & decay
* @ param x input value 0 - 255
* @ param pulsewidth 0 - 127
* @ param attdec attac & decay , max . pulsewidth / 2
* @ returns signed waveform value
*/
int8_t WS2812FX : : tristate_square8 ( uint8_t x , uint8_t pulsewidth , uint8_t attdec ) {
int8_t a = 127 ;
if ( x > 127 ) {
a = - 127 ;
x - = 127 ;
}
if ( x < attdec ) { //inc to max
return ( int16_t ) x * a / attdec ;
}
else if ( x < pulsewidth - attdec ) { //max
return a ;
}
else if ( x < pulsewidth ) { //dec to 0
return ( int16_t ) ( pulsewidth - x ) * a / attdec ;
}
return 0 ;
}
2019-02-09 16:37:20 +01:00
/*
* Put a value 0 to 255 in to get a color value .
* The colours are a transition r - > g - > b - > back to r
* Inspired by the Adafruit examples .
*/
uint32_t WS2812FX : : color_wheel ( uint8_t pos ) {
if ( SEGMENT . palette ) return color_from_palette ( pos , false , true , 0 ) ;
pos = 255 - pos ;
if ( pos < 85 ) {
return ( ( uint32_t ) ( 255 - pos * 3 ) < < 16 ) | ( ( uint32_t ) ( 0 ) < < 8 ) | ( pos * 3 ) ;
} else if ( pos < 170 ) {
pos - = 85 ;
return ( ( uint32_t ) ( 0 ) < < 16 ) | ( ( uint32_t ) ( pos * 3 ) < < 8 ) | ( 255 - pos * 3 ) ;
} else {
pos - = 170 ;
return ( ( uint32_t ) ( pos * 3 ) < < 16 ) | ( ( uint32_t ) ( 255 - pos * 3 ) < < 8 ) | ( 0 ) ;
}
}
/*
2020-03-30 12:56:51 +02:00
* Returns a new , random wheel index with a minimum distance of 42 from pos .
2019-02-09 16:37:20 +01:00
*/
uint8_t WS2812FX : : get_random_wheel_index ( uint8_t pos ) {
uint8_t r = 0 , x = 0 , y = 0 , d = 0 ;
while ( d < 42 ) {
r = random8 ( ) ;
x = abs ( pos - r ) ;
y = 255 - x ;
2020-03-26 10:18:19 +01:00
d = MIN ( x , y ) ;
2019-02-09 16:37:20 +01:00
}
return r ;
}
2021-12-25 01:30:27 +01:00
uint32_t IRAM_ATTR WS2812FX : : crgb_to_col ( CRGB fastled )
2019-08-30 15:39:34 +02:00
{
2021-10-26 20:35:45 +02:00
return RGBW32 ( fastled . red , fastled . green , fastled . blue , 0 ) ;
2019-08-30 15:39:34 +02:00
}
2021-12-25 01:30:27 +01:00
CRGB IRAM_ATTR WS2812FX : : col_to_crgb ( uint32_t color )
2019-02-09 16:37:20 +01:00
{
CRGB fastled_col ;
2021-10-26 20:35:45 +02:00
fastled_col . red = R ( color ) ;
fastled_col . green = G ( color ) ;
fastled_col . blue = B ( color ) ;
2019-02-09 16:37:20 +01:00
return fastled_col ;
}
2020-02-25 02:19:12 +01:00
void WS2812FX : : load_gradient_palette ( uint8_t index )
{
byte i = constrain ( index , 0 , GRADIENT_PALETTE_COUNT - 1 ) ;
byte tcp [ 72 ] ; //support gradient palettes with up to 18 entries
memcpy_P ( tcp , ( byte * ) pgm_read_dword ( & ( gGradientPalettes [ i ] ) ) , 72 ) ;
targetPalette . loadDynamicGradientPalette ( tcp ) ;
}
2019-02-09 16:37:20 +01:00
/*
* FastLED palette modes helper function . Limitation : Due to memory reasons , multiple active segments with FastLED will disable the Palette transitions
*/
void WS2812FX : : handle_palette ( void )
{
bool singleSegmentMode = ( _segment_index = = _segment_index_palette_last ) ;
_segment_index_palette_last = _segment_index ;
byte paletteIndex = SEGMENT . palette ;
2020-06-06 00:57:34 +02:00
if ( paletteIndex = = 0 ) //default palette. Differs depending on effect
{
switch ( SEGMENT . mode )
{
case FX_MODE_FIRE_2012 : paletteIndex = 35 ; break ; //heat palette
case FX_MODE_COLORWAVES : paletteIndex = 26 ; break ; //landscape 33
case FX_MODE_FILLNOISE8 : paletteIndex = 9 ; break ; //ocean colors
case FX_MODE_NOISE16_1 : paletteIndex = 20 ; break ; //Drywet
case FX_MODE_NOISE16_2 : paletteIndex = 43 ; break ; //Blue cyan yellow
case FX_MODE_NOISE16_3 : paletteIndex = 35 ; break ; //heat palette
case FX_MODE_NOISE16_4 : paletteIndex = 26 ; break ; //landscape 33
case FX_MODE_GLITTER : paletteIndex = 11 ; break ; //rainbow colors
case FX_MODE_SUNRISE : paletteIndex = 35 ; break ; //heat palette
case FX_MODE_FLOW : paletteIndex = 6 ; break ; //party
}
}
2019-12-06 01:44:45 +01:00
if ( SEGMENT . mode > = FX_MODE_METEOR & & paletteIndex = = 0 ) paletteIndex = 4 ;
2019-02-09 16:37:20 +01:00
switch ( paletteIndex )
{
2020-06-06 00:57:34 +02:00
case 0 : //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p ; break ;
2019-02-09 16:37:20 +01:00
case 1 : { //periodically replace palette with a random one. Doesn't work with multiple FastLED segments
if ( ! singleSegmentMode )
{
targetPalette = PartyColors_p ; break ; //fallback
}
if ( millis ( ) - _lastPaletteChange > 1000 + ( ( uint32_t ) ( 255 - SEGMENT . intensity ) ) * 100 )
{
targetPalette = CRGBPalette16 (
CHSV ( random8 ( ) , 255 , random8 ( 128 , 255 ) ) ,
CHSV ( random8 ( ) , 255 , random8 ( 128 , 255 ) ) ,
CHSV ( random8 ( ) , 192 , random8 ( 128 , 255 ) ) ,
CHSV ( random8 ( ) , 255 , random8 ( 128 , 255 ) ) ) ;
_lastPaletteChange = millis ( ) ;
} break ; }
case 2 : { //primary color only
2019-08-30 15:39:34 +02:00
CRGB prim = col_to_crgb ( SEGCOLOR ( 0 ) ) ;
2019-02-09 16:37:20 +01:00
targetPalette = CRGBPalette16 ( prim ) ; break ; }
2020-05-03 20:57:53 +02:00
case 3 : { //primary + secondary
2019-08-30 15:39:34 +02:00
CRGB prim = col_to_crgb ( SEGCOLOR ( 0 ) ) ;
CRGB sec = col_to_crgb ( SEGCOLOR ( 1 ) ) ;
2020-05-03 20:57:53 +02:00
targetPalette = CRGBPalette16 ( prim , prim , sec , sec ) ; break ; }
case 4 : { //primary + secondary + tertiary
2019-08-30 15:39:34 +02:00
CRGB prim = col_to_crgb ( SEGCOLOR ( 0 ) ) ;
CRGB sec = col_to_crgb ( SEGCOLOR ( 1 ) ) ;
2019-12-06 01:44:45 +01:00
CRGB ter = col_to_crgb ( SEGCOLOR ( 2 ) ) ;
targetPalette = CRGBPalette16 ( ter , sec , prim ) ; break ; }
2020-05-03 20:57:53 +02:00
case 5 : { //primary + secondary (+tert if not off), more distinct
CRGB prim = col_to_crgb ( SEGCOLOR ( 0 ) ) ;
CRGB sec = col_to_crgb ( SEGCOLOR ( 1 ) ) ;
if ( SEGCOLOR ( 2 ) ) {
CRGB ter = col_to_crgb ( SEGCOLOR ( 2 ) ) ;
targetPalette = CRGBPalette16 ( prim , prim , prim , prim , prim , sec , sec , sec , sec , sec , ter , ter , ter , ter , ter , prim ) ;
} else {
targetPalette = CRGBPalette16 ( prim , prim , prim , prim , prim , prim , prim , prim , sec , sec , sec , sec , sec , sec , sec , sec ) ;
}
break ; }
2019-02-09 16:37:20 +01:00
case 6 : //Party colors
targetPalette = PartyColors_p ; break ;
case 7 : //Cloud colors
targetPalette = CloudColors_p ; break ;
case 8 : //Lava colors
targetPalette = LavaColors_p ; break ;
case 9 : //Ocean colors
targetPalette = OceanColors_p ; break ;
case 10 : //Forest colors
targetPalette = ForestColors_p ; break ;
case 11 : //Rainbow colors
targetPalette = RainbowColors_p ; break ;
case 12 : //Rainbow stripe colors
targetPalette = RainbowStripeColors_p ; break ;
default : //progmem palettes
2020-06-06 00:57:34 +02:00
load_gradient_palette ( paletteIndex - 13 ) ;
2019-02-09 16:37:20 +01:00
}
2020-09-27 14:42:14 +02:00
if ( singleSegmentMode & & paletteFade & & SEGENV . call > 0 ) //only blend if just one segment uses FastLED mode
2019-02-09 16:37:20 +01:00
{
nblendPaletteTowardPalette ( currentPalette , targetPalette , 48 ) ;
} else
{
currentPalette = targetPalette ;
}
}
2020-07-19 16:24:26 +02:00
/*
* Gets a single color from the currently selected palette .
* @ param i Palette Index ( if mapping is true , the full palette will be SEGLEN long , if false , 255 ) . Will wrap around automatically .
* @ param mapping if true , LED position in segment is considered for color
* @ param wrap FastLED palettes will usally wrap back to the start smoothly . Set false to get a hard edge
* @ param mcol If the default palette 0 is selected , return the standard color 0 , 1 or 2 instead . If > 2 , Party palette is used instead
* @ param pbri Value to scale the brightness of the returned color by . Default is 255. ( no scaling )
* @ returns Single color from palette
*/
2021-12-25 01:30:27 +01:00
uint32_t IRAM_ATTR WS2812FX : : color_from_palette ( uint16_t i , bool mapping , bool wrap , uint8_t mcol , uint8_t pbri )
2019-02-09 16:37:20 +01:00
{
2022-03-05 01:05:26 +01:00
if ( ( SEGMENT . palette = = 0 & & mcol < 3 ) | | _no_rgb ) {
2020-12-13 18:36:18 +01:00
uint32_t color = SEGCOLOR ( mcol ) ;
2022-03-05 01:05:26 +01:00
if ( pbri = = 255 ) return color ;
return RGBW32 ( scale8_video ( R ( color ) , pbri ) , scale8_video ( G ( color ) , pbri ) , scale8_video ( B ( color ) , pbri ) , scale8_video ( W ( color ) , pbri ) ) ;
2020-12-13 18:36:18 +01:00
}
2019-02-09 16:37:20 +01:00
uint8_t paletteIndex = i ;
2021-06-15 23:36:12 +02:00
if ( mapping & & SEGLEN > 1 ) paletteIndex = ( i * 255 ) / ( SEGLEN - 1 ) ;
2019-02-09 16:37:20 +01:00
if ( ! wrap ) paletteIndex = scale8 ( paletteIndex , 240 ) ; //cut off blend at palette "end"
CRGB fastled_col ;
2022-03-05 01:05:26 +01:00
fastled_col = ColorFromPalette ( currentPalette , paletteIndex , pbri , ( paletteBlend = = 3 ) ? NOBLEND : LINEARBLEND ) ;
2020-12-13 18:36:18 +01:00
return crgb_to_col ( fastled_col ) ;
2019-02-09 16:37:20 +01:00
}
2019-05-22 00:23:09 +02:00
2020-02-22 17:20:34 +01:00
2021-11-03 14:52:22 +01:00
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
2021-04-04 13:54:34 +02:00
void WS2812FX : : deserializeMap ( uint8_t n ) {
2021-09-12 01:15:51 +02:00
char fileName [ 32 ] ;
strcpy_P ( fileName , PSTR ( " /ledmap " ) ) ;
if ( n ) sprintf ( fileName + 7 , " %d " , n ) ;
strcat ( fileName , " .json " ) ;
2021-04-04 13:54:34 +02:00
bool isFile = WLED_FS . exists ( fileName ) ;
if ( ! isFile ) {
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
if ( ! n & & customMappingTable ! = nullptr ) {
customMappingSize = 0 ;
delete [ ] customMappingTable ;
customMappingTable = nullptr ;
}
return ;
}
2021-02-13 01:02:14 +01:00
2021-12-04 01:05:01 +01:00
if ( ! requestJSONBufferLock ( 7 ) ) return ;
2021-11-03 14:52:22 +01:00
2021-04-04 13:54:34 +02:00
DEBUG_PRINT ( F ( " Reading LED map from " ) ) ;
DEBUG_PRINTLN ( fileName ) ;
2021-02-13 01:02:14 +01:00
2021-11-03 14:52:22 +01:00
if ( ! readObjectFromFile ( fileName , nullptr , & doc ) ) {
2021-11-12 23:33:10 +01:00
releaseJSONBufferLock ( ) ;
2021-11-03 14:52:22 +01:00
return ; //if file does not exist just exit
}
2021-02-13 01:02:14 +01:00
2021-04-04 13:54:34 +02:00
// erase old custom ledmap
2021-02-13 01:02:14 +01:00
if ( customMappingTable ! = nullptr ) {
2021-04-04 13:54:34 +02:00
customMappingSize = 0 ;
2021-02-13 01:02:14 +01:00
delete [ ] customMappingTable ;
customMappingTable = nullptr ;
}
JsonArray map = doc [ F ( " map " ) ] ;
if ( ! map . isNull ( ) & & map . size ( ) ) { // not an empty map
customMappingSize = map . size ( ) ;
customMappingTable = new uint16_t [ customMappingSize ] ;
for ( uint16_t i = 0 ; i < customMappingSize ; i + + ) {
customMappingTable [ i ] = ( uint16_t ) map [ i ] ;
}
}
2021-11-03 14:52:22 +01:00
2021-11-12 23:33:10 +01:00
releaseJSONBufferLock ( ) ;
2021-02-13 01:02:14 +01:00
}
2020-11-04 11:04:40 +01:00
//gamma 2.8 lookup table used for color correction
byte gammaT [ ] = {
2019-05-22 00:23:09 +02:00
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ,
2 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 4 , 4 , 4 , 4 , 4 , 5 , 5 , 5 ,
5 , 6 , 6 , 6 , 6 , 7 , 7 , 7 , 7 , 8 , 8 , 8 , 9 , 9 , 9 , 10 ,
10 , 10 , 11 , 11 , 11 , 12 , 12 , 13 , 13 , 13 , 14 , 14 , 15 , 15 , 16 , 16 ,
17 , 17 , 18 , 18 , 19 , 19 , 20 , 20 , 21 , 21 , 22 , 22 , 23 , 24 , 24 , 25 ,
25 , 26 , 27 , 27 , 28 , 29 , 29 , 30 , 31 , 32 , 32 , 33 , 34 , 35 , 35 , 36 ,
37 , 38 , 39 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 50 ,
51 , 52 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 , 64 , 66 , 67 , 68 ,
69 , 70 , 72 , 73 , 74 , 75 , 77 , 78 , 79 , 81 , 82 , 83 , 85 , 86 , 87 , 89 ,
90 , 92 , 93 , 95 , 96 , 98 , 99 , 101 , 102 , 104 , 105 , 107 , 109 , 110 , 112 , 114 ,
115 , 117 , 119 , 120 , 122 , 124 , 126 , 127 , 129 , 131 , 133 , 135 , 137 , 138 , 140 , 142 ,
144 , 146 , 148 , 150 , 152 , 154 , 156 , 158 , 160 , 162 , 164 , 167 , 169 , 171 , 173 , 175 ,
177 , 180 , 182 , 184 , 186 , 189 , 191 , 193 , 196 , 198 , 200 , 203 , 205 , 208 , 210 , 213 ,
215 , 218 , 220 , 223 , 225 , 228 , 231 , 233 , 236 , 239 , 241 , 244 , 247 , 249 , 252 , 255 } ;
2020-11-04 11:04:40 +01:00
uint8_t WS2812FX : : gamma8_cal ( uint8_t b , float gamma ) {
return ( int ) ( pow ( ( float ) b / 255.0 , gamma ) * 255 + 0.5 ) ;
}
void WS2812FX : : calcGammaTable ( float gamma )
{
for ( uint16_t i = 0 ; i < 256 ; i + + ) {
gammaT [ i ] = gamma8_cal ( i , gamma ) ;
}
}
2019-05-22 00:23:09 +02:00
uint8_t WS2812FX : : gamma8 ( uint8_t b )
{
return gammaT [ b ] ;
}
uint32_t WS2812FX : : gamma32 ( uint32_t color )
{
if ( ! gammaCorrectCol ) return color ;
2021-10-26 20:35:45 +02:00
uint8_t w = W ( color ) ;
uint8_t r = R ( color ) ;
uint8_t g = G ( color ) ;
uint8_t b = B ( color ) ;
2019-05-22 00:23:09 +02:00
w = gammaT [ w ] ;
r = gammaT [ r ] ;
g = gammaT [ g ] ;
b = gammaT [ b ] ;
2021-10-26 20:35:45 +02:00
return RGBW32 ( r , g , b , w ) ;
2019-05-22 00:23:09 +02:00
}
2019-12-31 11:11:05 +01:00
2021-11-24 11:02:25 +01:00
WS2812FX * WS2812FX : : instance = nullptr ;
2021-11-30 22:52:17 +01:00
//Bus static member definition, would belong in bus_manager.cpp
2021-11-27 16:49:42 +01:00
int16_t Bus : : _cct = - 1 ;
2021-11-28 01:21:17 +01:00
uint8_t Bus : : _cctBlend = 0 ;
2021-12-28 18:09:52 +01:00
// WLEDSR: extensions
// Technical notes
// ===============
// If an effect name is followed by an @, slider and color control is effective.
// See setSliderAndColorControl in index.js for implementation
// If not effective then:
// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown
// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown
// If effective (@)
// - a ; seperates slider controls (left) from color controls (middle) and palette control (right)
// - if left, middle or right is empty no controls are shown
// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value
// - a ! means that the default is used.
// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3
// - For colors: Fx color, Background color, Custom
// - For palette: prompt for color palette OR palette ID if numeric (will hide palette selection)
//
// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle.
// If a color is specified, the 1,2 or 3 is replaced by that specification.
// Note: Effects can override default pattern behaviour
// - FadeToBlack can override the background setting
// - Defining SEGCOL(<i>) can override a specific palette using these values (e.g. Color Gradient)
const char JSON_mode_names [ ] PROGMEM = R " =====([
" Solid " ,
" Blink@!,;!,!,;! " ,
" Breathe@!,;!,!;! " ,
" Wipe@!,!;!,!,;! " ,
2022-01-12 21:48:34 +01:00
" Wipe Random@!,;1,2,3;! " ,
" Random Colors@!,Fade time;1,2,3;! " ,
2021-12-28 18:09:52 +01:00
" Sweep@!,!;!,!,;! " ,
2022-01-12 21:35:10 +01:00
" Dynamic@!,!;1,2,3;! " ,
2022-01-12 21:48:34 +01:00
" Colorloop@!,Saturation;1,2,3;! " ,
2022-01-12 21:35:10 +01:00
" Rainbow@!,Size;1,2,3;! " ,
2021-12-28 18:09:52 +01:00
" Scan@!,# of dots;!,!,;! " ,
" Scan Dual@!,# of dots;!,!,;! " ,
2022-01-12 21:35:10 +01:00
" Fade@!,;!,!,;! " ,
2021-12-28 18:09:52 +01:00
" Theater@!,Gap size;!,!,;! " ,
2022-01-23 11:13:50 +01:00
" Theater Rainbow@!,Gap size;1,2,3;! " ,
2021-12-28 18:09:52 +01:00
" Running@!,Wave width;!,!,;! " ,
" Saw@!,Width;!,!,;! " ,
2022-01-12 21:35:10 +01:00
" Twinkle@!,;!,!,;! " ,
" Dissolve@Repeat speed,Dissolve speed;!,!,;! " ,
" Dissolve Rnd@Repeat speed,Dissolve speed;,!,;! " ,
2022-01-10 18:42:09 +01:00
" Sparkle@!,;!,!,;! " ,
" Sparkle Dark@!,!;Bg,Fx,;! " ,
" Sparkle+@!,!;Bg,Fx,;! " ,
2022-01-12 21:35:10 +01:00
" Strobe@!,;!,!,;! " ,
" Strobe Rainbow@!,;,!,;! " ,
" Strobe Mega@!,!;!,!,;! " ,
2022-01-10 18:42:09 +01:00
" Blink Rainbow@Frequency,Blink duration;!,!,;! " ,
" Android@!,Width;!,!,;! " ,
" Chase@!,Width;!,!,!;! " ,
" Chase Random@!,Width;!,,!;! " ,
" Chase Rainbow@!,Width;!,!,;0 " ,
" Chase Flash@!,;Bg,Fx,!;! " ,
" Chase Flash Rnd@!,;,Fx,;! " ,
2022-01-12 21:35:10 +01:00
" Rainbow Runner@!,Size;Bg,,;! " ,
2022-01-10 18:42:09 +01:00
" Colorful@!,Saturation;1,2,3;! " ,
2022-01-12 21:48:34 +01:00
" Traffic Light@!,;,!,;! " ,
2021-12-28 18:09:52 +01:00
" Sweep Random " ,
" Chase 2@!,Width;!,!,;! " ,
2022-01-10 19:00:44 +01:00
" Aurora@!=24,!;1,2,3;!=50 " ,
2021-12-28 18:09:52 +01:00
" Stream " ,
" Scanner " ,
" Lighthouse " ,
2022-01-12 15:45:32 +01:00
" Fireworks@Sharpness=96,Frequency=192;!,2,;!=11 " ,
" Rain@Fade rate=128,Frequency=128;!,2,;! " ,
" Tetrix@!=224,Width=0;!,!,;!=11 " ,
" Fire Flicker@!,!;!,,;! " ,
" Gradient@!,Spread=16;!,!,;! " ,
" Loading@!,Fade=16;!,!,;! " ,
2022-01-10 18:42:09 +01:00
" Police@!,Width;,Bg,;0 " ,
2021-12-28 18:09:52 +01:00
" Fairy " ,
" Two Dots@!,Dot size;1,2,Bg;! " ,
" Fairy Twinkle " ,
" Running Dual " ,
" Halloween " ,
" Chase 3@!,Size;1,2,3;0 " ,
" Tri Wipe@!,Width;1,2,3;0 " ,
" Tri Fade " ,
" Lightning " ,
" ICU " ,
" Multi Comet " ,
" Scanner Dual " ,
" Stream 2 " ,
" Oscillate " ,
2022-01-10 18:42:09 +01:00
" Pride 2015@!,;; " ,
2021-12-28 18:09:52 +01:00
" Juggle@!=16,Trail=240;!,!,;! " ,
2022-01-12 21:48:34 +01:00
" Palette@!,;1,2,3;! " ,
" Fire 2012@Spark rate=120,Decay=64;1,2,3;! " ,
2021-12-28 18:09:52 +01:00
" Colorwaves " ,
2022-01-12 21:48:34 +01:00
" Bpm@!=64,;1,2,3;! " ,
2021-12-28 18:09:52 +01:00
" Fill Noise " ,
" Noise 1 " ,
" Noise 2 " ,
" Noise 3 " ,
" Noise 4 " ,
2022-01-10 18:42:09 +01:00
" Colortwinkles@Fade speed,Spawn speed;1,2,3;! " ,
2022-01-12 21:48:34 +01:00
" Lake@!,;1,2,3;! " ,
2021-12-28 18:09:52 +01:00
" Meteor@!,Trail length;!,!,;! " ,
" Meteor Smooth@!,Trail length;!,!,;! " ,
" Railway " ,
" Ripple " ,
" Twinklefox " ,
" Twinklecat " ,
" Halloween Eyes " ,
2021-12-28 18:53:30 +01:00
" Solid Pattern@Fg size,Bg size;Fg,Bg,;!=0 " ,
" Solid Pattern Tri@,Size;1,2,3;!=0 " ,
2021-12-28 18:09:52 +01:00
" Spots@Spread,Width;!,!,;! " ,
" Spots Fade@Spread,Width;!,!,;! " ,
" Glitter " ,
" Candle@Flicker rate=96,Flicker intensity=224;!,!,;0 " ,
" Fireworks Starburst " ,
" Fireworks 1D@Gravity,Firing side;!,!,;! " ,
" Bouncing Balls@Gravity,# of balls;!,!,;! " ,
" Sinelon " ,
" Sinelon Dual " ,
" Sinelon Rainbow " ,
2022-01-23 11:13:50 +01:00
" Popcorn " ,
2021-12-28 18:09:52 +01:00
" Drip@Gravity,# of drips;!,!;! " ,
" Plasma@Phase,;1,2,3;! " ,
" Percent@,% of fill;!,!,;! " ,
" Ripple Rainbow " ,
2022-01-12 21:48:34 +01:00
" Heartbeat@!,!;!,!,;! " ,
2021-12-28 18:09:52 +01:00
" Pacifica " ,
" Candle Multi@Flicker rate=96,Flicker intensity=224;!,!,;0 " ,
" Solid Glitter@,!;!,,;0 " ,
" Sunrise@Time [min]=60,;;0 " ,
" Phased " ,
" Twinkleup@!,Intensity;!,!,;! " ,
" Noise Pal " ,
" Sine " ,
" Phased Noise " ,
" Flow " ,
" Chunchun@!,Gap size;!,!,;! " ,
2022-01-10 18:42:09 +01:00
" Dancing Shadows@!,# of shadows;!,,;! " ,
2021-12-28 18:09:52 +01:00
" Washing Machine " ,
2022-01-12 21:48:34 +01:00
" Candy Cane@!,Width;; " ,
2022-01-10 18:42:09 +01:00
" Blends@Shift speed,Blend speed;1,2,3,! " ,
2021-12-28 18:09:52 +01:00
" TV Simulator " ,
" Dynamic Smooth "
] ) = = = = = " ;
const char JSON_palette_names [ ] PROGMEM = R " =====([
" Default " , " * Random Cycle " , " * Color 1 " , " * Colors 1&2 " , " * Color Gradient " , " * Colors Only " , " Party " , " Cloud " , " Lava " , " Ocean " ,
" Forest " , " Rainbow " , " Rainbow Bands " , " Sunset " , " Rivendell " , " Breeze " , " Red & Blue " , " Yellowout " , " Analogous " , " Splash " ,
" Pastel " , " Sunset 2 " , " Beech " , " Vintage " , " Departure " , " Landscape " , " Beach " , " Sherbet " , " Hult " , " Hult 64 " ,
" Drywet " , " Jul " , " Grintage " , " Rewhi " , " Tertiary " , " Fire " , " Icefire " , " Cyane " , " Light Pink " , " Autumn " ,
" Magenta " , " Magred " , " Yelmag " , " Yelblu " , " Orange & Teal " , " Tiamat " , " April Night " , " Orangery " , " C9 " , " Sakura " ,
" Aurora " , " Atlantica " , " C9 2 " , " C9 New " , " Temperature " , " Aurora 2 " , " Retro Clown " , " Candy " , " Toxy Reaf " , " Fairy Reaf " ,
" Semi Blue " , " Pink Candy " , " Red Reaf " , " Aqua Flash " , " Yelblu Hot " , " Lite Light " , " Red Flash " , " Blink Red " , " Red Shift " , " Red Tide " ,
" Candy2 "
] ) = = = = = " ;