Add two new Fairy FX modes

This commit is contained in:
cschwinne 2021-12-07 11:03:41 +01:00
parent 66bad2b6f8
commit 8643263227
5 changed files with 162 additions and 30 deletions

View File

@ -2,6 +2,12 @@
### Builds after release 0.12.0 ### Builds after release 0.12.0
#### Build 2112070
- Added new effect "Fairy", replacing "Police All"
- Added new effect "Fairytwinkle", replacing "Two Areas"
- Static single JSON buffer (performance and stability improvement) (PR #2336)
#### Build 2112030 #### Build 2112030
- Fixed ESP32 crash on Colortwinkles brightness change - Fixed ESP32 crash on Colortwinkles brightness change

View File

@ -1216,12 +1216,13 @@ uint16_t WS2812FX::mode_loading(void) {
//American Police Light with all LEDs Red and Blue //American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width) uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2)
{ {
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay); uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
uint16_t offset = it % SEGLEN; uint16_t offset = it % SEGLEN;
uint16_t width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip
if (!width) width = 1; if (!width) width = 1;
for (uint16_t i = 0; i < width; i++) { for (uint16_t i = 0; i < width; i++) {
uint16_t indexR = (offset + i) % SEGLEN; uint16_t indexR = (offset + i) % SEGLEN;
@ -1233,26 +1234,11 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width)
} }
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all()
{
return police_base(RED, BLUE, (SEGLEN>>1));
}
//Police Lights Red and Blue //Police Lights Red and Blue
uint16_t WS2812FX::mode_police() uint16_t WS2812FX::mode_police()
{ {
fill(SEGCOLOR(1)); fill(SEGCOLOR(1));
return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip return police_base(RED, BLUE);
}
//Police All with custom colors
uint16_t WS2812FX::mode_two_areas()
{
fill(SEGCOLOR(2));
return police_base(SEGCOLOR(0), SEGCOLOR(1), ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
} }
@ -1262,7 +1248,142 @@ uint16_t WS2812FX::mode_two_dots()
fill(SEGCOLOR(2)); fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
return police_base(SEGCOLOR(0), color2, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip return police_base(SEGCOLOR(0), color2);
}
/*
* Fairy, inspired by https://www.youtube.com/watch?v=zeOw5MZWq24
*/
//4 bytes
typedef struct Flasher {
uint16_t stateStart;
uint8_t stateDur;
bool stateOn;
} flasher;
#define FLASHERS_PER_ZONE 6
#define MAX_SHIMMER 92
uint16_t WS2812FX::mode_fairy() {
//set every pixel to a 'random' color from palette (using seed so it doesn't change between frames)
uint16_t PRNG16 = 5100 + _segment_index;
for (uint16_t i = 0; i < SEGLEN; i++) {
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0));
}
//amount of flasher pixels depending on intensity (0: none, 255: every LED)
if (SEGMENT.intensity == 0) return FRAMETIME;
uint8_t flasherDistance = ((255 - SEGMENT.intensity) / 28) +1; //1-10
uint16_t numFlashers = (SEGLEN / flasherDistance) +1;
uint16_t dataSize = sizeof(flasher) * numFlashers;
if (!SEGENV.allocateData(dataSize)) return FRAMETIME; //allocation failed
Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
uint16_t now16 = now & 0xFFFF;
//Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers
uint16_t zones = numFlashers/FLASHERS_PER_ZONE;
if (!zones) zones = 1;
uint8_t flashersInZone = numFlashers/zones;
uint8_t flasherBri[FLASHERS_PER_ZONE*2 -1];
for (uint16_t z = 0; z < zones; z++) {
uint16_t flasherBriSum = 0;
uint16_t firstFlasher = z*flashersInZone;
if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1));
for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
uint16_t stateTime = now16 - flashers[f].stateStart;
//random on/off time reached, switch state
if (stateTime > flashers[f].stateDur * 10) {
flashers[f].stateOn = !flashers[f].stateOn;
if (flashers[f].stateOn) {
flashers[f].stateDur = 12 + random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
} else {
flashers[f].stateDur = 20 + random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
}
//flashers[f].stateDur = 51 + random8(2 + ((255 - SEGMENT.speed) >> 1));
flashers[f].stateStart = now16;
if (stateTime < 255) {
flashers[f].stateStart -= 255 -stateTime; //start early to get correct bri
flashers[f].stateDur += 26 - stateTime/10;
stateTime = 255 - stateTime;
} else {
stateTime = 0;
}
}
if (stateTime > 255) stateTime = 255; //for flasher brightness calculation, fades in first 255 ms of state
//flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-gamma8((510 - stateTime) >> 1) : gamma8((510 - stateTime) >> 1);
flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? stateTime : 255 - (stateTime >> 0);
flasherBriSum += flasherBri[f - firstFlasher];
}
//dim factor, to create "shimmer" as other pixels get less voltage if a lot of flashers are on
uint8_t avgFlasherBri = flasherBriSum / flashersInZone;
uint8_t globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers
for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255;
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
uint16_t flasherPos = f*flasherDistance;
setPixelColor(flasherPos, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), bri));
for (uint16_t i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) {
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri));
}
}
}
return FRAMETIME;
}
/*
* Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on getPixelColor
* Warning: Uses 4 bytes of segment data per pixel
*/
uint16_t WS2812FX::mode_fairytwinkle() {
uint16_t dataSize = sizeof(flasher) * SEGLEN;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
uint16_t now16 = now & 0xFFFF;
uint16_t PRNG16 = 5100 + _segment_index;
uint16_t riseFallTime = 400 + (255-SEGMENT.speed)*3;
uint16_t maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1);
for (uint16_t f = 0; f < SEGLEN; f++) {
uint16_t stateTime = now16 - flashers[f].stateStart;
//random on/off time reached, switch state
if (stateTime > flashers[f].stateDur * 100) {
flashers[f].stateOn = !flashers[f].stateOn;
bool init = !flashers[f].stateDur;
if (flashers[f].stateOn) {
flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1;
} else {
flashers[f].stateDur = riseFallTime/100 + random8(3 + ((255 - SEGMENT.speed) >> 6)) +1;
}
flashers[f].stateStart = now16;
stateTime = 0;
if (init) {
flashers[f].stateStart -= riseFallTime; //start lit
flashers[f].stateDur = riseFallTime/100 + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker
stateTime = riseFallTime;
}
}
if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change
if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state
uint8_t fadeprog = 255 - ((stateTime * 255) / riseFallTime);
uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog);
uint16_t lastR = PRNG16;
uint16_t diff = 0;
while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
diff = (PRNG16 > lastR) ? PRNG16 - lastR : lastR - PRNG16;
}
setPixelColor(f, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri));
}
return FRAMETIME;
} }

View File

@ -161,14 +161,14 @@
#define FX_MODE_COMET 41 #define FX_MODE_COMET 41
#define FX_MODE_FIREWORKS 42 #define FX_MODE_FIREWORKS 42
#define FX_MODE_RAIN 43 #define FX_MODE_RAIN 43
#define FX_MODE_TETRIX 44 #define FX_MODE_TETRIX 44 //was Merry Christmas prior to 0.12.0 (use "Chase 2" with Red/Green)
#define FX_MODE_FIRE_FLICKER 45 #define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46 #define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47 #define FX_MODE_LOADING 47
#define FX_MODE_POLICE 48 // candidate for removal (after below three) #define FX_MODE_POLICE 48 // candidate for removal (after below three)
#define FX_MODE_POLICE_ALL 49 // candidate for removal #define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity)
#define FX_MODE_TWO_DOTS 50 #define FX_MODE_TWO_DOTS 50
#define FX_MODE_TWO_AREAS 51 // candidate for removal #define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
#define FX_MODE_RUNNING_DUAL 52 #define FX_MODE_RUNNING_DUAL 52
#define FX_MODE_HALLOWEEN 53 // candidate for removal #define FX_MODE_HALLOWEEN 53 // candidate for removal
#define FX_MODE_TRICOLOR_CHASE 54 #define FX_MODE_TRICOLOR_CHASE 54
@ -550,9 +550,9 @@ class WS2812FX {
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient; _mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading; _mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
_mode[FX_MODE_POLICE] = &WS2812FX::mode_police; _mode[FX_MODE_POLICE] = &WS2812FX::mode_police;
_mode[FX_MODE_POLICE_ALL] = &WS2812FX::mode_police_all; _mode[FX_MODE_FAIRY] = &WS2812FX::mode_fairy;
_mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots; _mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots;
_mode[FX_MODE_TWO_AREAS] = &WS2812FX::mode_two_areas; _mode[FX_MODE_FAIRYTWINKLE] = &WS2812FX::mode_fairytwinkle;
_mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual; _mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual;
_mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween;
_mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase;
@ -773,9 +773,9 @@ class WS2812FX {
mode_gradient(void), mode_gradient(void),
mode_loading(void), mode_loading(void),
mode_police(void), mode_police(void),
mode_police_all(void), mode_fairy(void),
mode_two_dots(void), mode_two_dots(void),
mode_two_areas(void), mode_fairytwinkle(void),
mode_running_dual(void), mode_running_dual(void),
mode_bicolor_chase(void), mode_bicolor_chase(void),
mode_tricolor_chase(void), mode_tricolor_chase(void),
@ -878,7 +878,7 @@ class WS2812FX {
chase(uint32_t, uint32_t, uint32_t, bool), chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool), gradient_base(bool),
ripple_base(bool), ripple_base(bool),
police_base(uint32_t, uint32_t, uint16_t), police_base(uint32_t, uint32_t),
running(uint32_t, uint32_t, bool theatre=false), running(uint32_t, uint32_t, bool theatre=false),
tricolor_chase(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool), twinklefox_base(bool),
@ -927,8 +927,8 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd", "Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random", "Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random",
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Chase 2","Aurora","Stream", "Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Chase 2","Aurora","Stream",
"Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Police All", "Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Fairy",
"Two Dots","Two Areas","Running Dual","Halloween","Chase 3","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "Two Dots","Fairytwinkle","Running Dual","Halloween","Chase 3","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple",
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",

View File

@ -318,8 +318,12 @@ class BusPwm : public Bus {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5; cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
} }
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
uint8_t ww, cw; uint8_t ww, cw;
#ifdef WLED_USE_IC_CCT
ww = w;
cw = cct;
#else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255; if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend); else ww = ((255-cct) * 255) / (255 - _cctBlend);
@ -328,6 +332,7 @@ class BusPwm : public Bus {
ww = (w * ww) / 255; //brightness scaling ww = (w * ww) / 255; //brightness scaling
cw = (w * cw) / 255; cw = (w * cw) / 255;
#endif
switch (_type) { switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2112030 #define VERSION 2112070
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG