From 805fe1d47e3a99f357ba8120a03ea40c78614dca Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Tue, 24 Dec 2019 15:05:12 +0100 Subject: [PATCH 01/15] Revert "more effects to FRAMETIME" This reverts commit f3371c443e16599dff26610e311d627ac4ce7875. --- wled00/FX.cpp | 178 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 146 insertions(+), 32 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1791b710c..b1e348849 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -961,10 +961,6 @@ uint16_t WS2812FX::mode_halloween(void) { * Random colored pixels running. */ uint16_t WS2812FX::mode_running_random(void) { - uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.aux1 == it) return FRAMETIME; - for(uint16_t i=SEGLEN-1; i > 0; i--) { setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1)); } @@ -979,9 +975,7 @@ uint16_t WS2812FX::mode_running_random(void) { { SEGENV.step = 0; } - - SEGENV.aux1 = it; - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -1097,10 +1091,6 @@ uint16_t WS2812FX::mode_rain() * Fire flicker function */ uint16_t WS2812FX::mode_fire_flicker(void) { - uint32_t cycleTime = 40 + (255 - SEGMENT.speed); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) return FRAMETIME; - byte w = (SEGCOLOR(0) >> 24) & 0xFF; byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte g = (SEGCOLOR(0) >> 8) & 0xFF; @@ -1115,9 +1105,7 @@ uint16_t WS2812FX::mode_fire_flicker(void) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } } - - SEGENV.step = it; - return FRAMETIME; + return 20 + random((255 - SEGMENT.speed),(2 * (uint16_t)(255 - SEGMENT.speed))); } @@ -1297,7 +1285,6 @@ uint16_t WS2812FX::mode_icu(void) { setPixelColor(SEGMENT.start + dest, col); setPixelColor(SEGMENT.start + dest + SEGLEN/2, col); - if (SEGMENT.intensity > 127) return FRAMETIME; return SPEED_FORMULA_L; } @@ -1307,11 +1294,6 @@ uint16_t WS2812FX::mode_icu(void) { */ uint16_t WS2812FX::mode_tricolor_wipe(void) { - uint32_t cycleTime = 40 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) return FRAMETIME; - uint8_t incr = it-SEGENV.step; - if(SEGENV.step < SEGLEN) { uint32_t led_offset = SEGENV.step; setPixelColor(SEGMENT.start + led_offset, SEGCOLOR(0)); @@ -1325,7 +1307,7 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) } SEGENV.step = (SEGENV.step + 1) % (SEGLEN * 3); - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -1336,9 +1318,6 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) */ uint16_t WS2812FX::mode_tricolor_fade(void) { - uint16_t counter = now * ((SEGMENT.speed >> 3) +1); - SEGENV.step = (counter * 768) >> 16; - uint32_t color1 = 0, color2 = 0; byte stage = 0; @@ -1369,7 +1348,10 @@ uint16_t WS2812FX::mode_tricolor_fade(void) setPixelColor(i, color); } - return FRAMETIME; + SEGENV.step += 4; + if(SEGENV.step >= 768) SEGENV.step = 0; + + return 5 + ((uint32_t)(255 - SEGMENT.speed) / 10); } @@ -1379,10 +1361,6 @@ uint16_t WS2812FX::mode_tricolor_fade(void) */ uint16_t WS2812FX::mode_multi_comet(void) { - uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) return FRAMETIME; - fade_out(SEGMENT.intensity); static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; @@ -1404,9 +1382,7 @@ uint16_t WS2812FX::mode_multi_comet(void) } } } - - SEGENV.step = it; - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -2365,6 +2341,95 @@ uint16_t WS2812FX::mode_spots_fade() } +/* +* Bouncing Balls Effect +* Adapted from: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ +*/ +uint16_t WS2812FX::mode_BouncingBalls(void) { + // number of balls based on intensity setting, + // only has 4 for a few of the higher settings as there is no colour selection + // fourth ball is a random colour + int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); + + // ideally use speed for gravity effect on the bounce + float Gravity = -9.81; + + const int maxBallCount = 7; + static float Height[maxBallCount]; + static float ImpactVelocity[maxBallCount]; + static int Position[maxBallCount]; + static float TimeSinceLastBounce[maxBallCount]; + static long ClockTimeSinceLastBounce[maxBallCount]; + static int StartHeight = 1; // height in metres (strip length) + static float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) + static float ImpactVelocityStart=sqrt( -2 * Gravity * StartHeight); + + // Different from the examples, to allow for initialisation of the first bounce + if (Dampening[0] == 0) { + for (int i = 0 ; i < maxBallCount ; i++) { + ClockTimeSinceLastBounce[i] = millis(); + ImpactVelocity[i] = ImpactVelocityStart; + TimeSinceLastBounce[i] = 0; + Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); + } + } + + for (int i = 0 ; i < balls ; i++) { + TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i]; + Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000; + + if ( Height[i] < 0 ) { + Height[i] = 0; + ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i]; + ClockTimeSinceLastBounce[i] = millis(); + + if ( ImpactVelocity[i] < 0.01 ) { + ImpactVelocity[i] = ImpactVelocityStart; + } + } + Position[i] = round( Height[i] * (SEGLEN - 1) / StartHeight); + } + + fill(BLACK); + + for (int i = 0 ; i < balls ; i++) { + uint32_t color = SEGCOLOR(i % NUM_COLORS); + if (!color) { + color = color_wheel(random8()); + } + + setPixelColor(Position[i],color); + } + + return 20; +} + +/* +* Sinelon stolen from FASTLED examples +*/ +uint16_t WS2812FX::mode_sinelon(void) { + + fade_out(SEGMENT.intensity); + int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); + static int prevpos = 0; + + // setRange seems great to use, but doesn't work here for some reason + if( pos < prevpos ) { + for (uint16_t i = pos; i < prevpos; i++) + { + setPixelColor(i, color_from_palette(pos, false, false, 0)); + } + } else { + for (uint16_t i = prevpos; i < pos; i++) + { + setPixelColor(i, color_from_palette(pos, false, false, 0)); + } + } + prevpos = pos; + return FRAMETIME; +} + + //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 uint16_t WS2812FX::mode_glitter() { @@ -2379,6 +2444,55 @@ uint16_t WS2812FX::mode_glitter() } +/* +* POPCORN +*/ +typedef struct Kernel { + float position; + float velocity; + int32_t color; +} kernel; + +#define MAX_NUM_POPCORN 12 +#define GRAVITY 0.06 + +uint16_t WS2812FX::mode_popcorn(void) { + uint32_t popcornColor = SEGCOLOR(0); + uint32_t bgColor = SEGCOLOR(1); + if(popcornColor == bgColor) popcornColor = color_wheel(random8()); + + static kernel popcorn[MAX_NUM_POPCORN]; + static float coeff = 0.0f; + if(coeff == 0.0f) { // calculate the velocity coeff once (the secret sauce) + coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; + } + + fill(SEGCOLOR(1)); + + uint16_t ledIndex; + for(int8_t i=0; i < MAX_NUM_POPCORN; i++) { + bool isActive = popcorn[i].position >= 0.0f; + + if(isActive) { // if kernel is active, update its position + popcorn[i].position += popcorn[i].velocity; + popcorn[i].velocity -= (GRAVITY * SEGMENT.intensity/25); + ledIndex = SEGMENT.start + popcorn[i].position; + if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); + } else { // if kernel is inactive, randomly pop it + if(random8() < 2) { // POP!!! + popcorn[i].position = 0.0f; + popcorn[i].velocity = coeff * (random(66, 100) / 100.0f); + popcorn[i].color = popcornColor; + ledIndex = SEGMENT.start; + setPixelColor(ledIndex, popcorn[i].color); + } + } + } + + return SPEED_FORMULA_L; +} + + //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/ From 3083ccddcc68db29c07509d280b304403e1e579a Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Tue, 24 Dec 2019 20:41:03 +0100 Subject: [PATCH 02/15] rework of new effects, sync to FRAMETIME add options for sinelon --- wled00/FX.cpp | 63 ++++++++++++++++++++++++++++++++++++++++----------- wled00/FX.h | 15 ++++++++---- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index b1e348849..5461347e3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2346,9 +2346,8 @@ uint16_t WS2812FX::mode_spots_fade() * Adapted from: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_BouncingBalls(void) { - // number of balls based on intensity setting, - // only has 4 for a few of the higher settings as there is no colour selection - // fourth ball is a random colour + // number of balls based on intensity setting to max of 7 (cycles colors) + // non-chosen color is a random color int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); // ideally use speed for gravity effect on the bounce @@ -2375,7 +2374,7 @@ uint16_t WS2812FX::mode_BouncingBalls(void) { } for (int i = 0 ; i < balls ; i++) { - TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i]; + TimeSinceLastBounce[i] = (millis() - ClockTimeSinceLastBounce[i])/((255-SEGMENT.speed)*8/256 +1); Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000; if ( Height[i] < 0 ) { @@ -2398,37 +2397,75 @@ uint16_t WS2812FX::mode_BouncingBalls(void) { color = color_wheel(random8()); } - setPixelColor(Position[i],color); + setPixelColor(SEGMENT.start+Position[i],color); } - return 20; + return FRAMETIME; } /* * Sinelon stolen from FASTLED examples */ -uint16_t WS2812FX::mode_sinelon(void) { +uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { fade_out(SEGMENT.intensity); int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); static int prevpos = 0; + + uint32_t color1 = color_from_palette(pos, true, false, 0); + if (rainbow) { + color1 = color_wheel((pos % 8) * 32); + } - // setRange seems great to use, but doesn't work here for some reason if( pos < prevpos ) { for (uint16_t i = pos; i < prevpos; i++) { - setPixelColor(i, color_from_palette(pos, false, false, 0)); + setPixelColor(i, color1); } } else { for (uint16_t i = prevpos; i < pos; i++) { - setPixelColor(i, color_from_palette(pos, false, false, 0)); + setPixelColor(SEGMENT.start + i, color1); + } + } + + if (dual) { + uint32_t color2 = SEGCOLOR(2); + + if (color2 == 0) { + color2 = color_from_palette(pos, true, false, 0); + } + if (rainbow) { + color2 = color_wheel((pos % 8) * 32); + } + if( pos < prevpos ) { + for (uint16_t i = pos; i < prevpos; i++) + { + setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); + } + } else { + for (uint16_t i = prevpos; i < pos; i++) + { + setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); + } } } prevpos = pos; return FRAMETIME; } +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) { + return sinelon_base(true, true); +} + //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 uint16_t WS2812FX::mode_glitter() @@ -2470,12 +2507,12 @@ uint16_t WS2812FX::mode_popcorn(void) { fill(SEGCOLOR(1)); uint16_t ledIndex; - for(int8_t i=0; i < MAX_NUM_POPCORN; i++) { + for(int8_t i=0; i < SEGMENT.intensity*MAX_NUM_POPCORN/255; i++) { bool isActive = popcorn[i].position >= 0.0f; if(isActive) { // if kernel is active, update its position popcorn[i].position += popcorn[i].velocity; - popcorn[i].velocity -= (GRAVITY * SEGMENT.intensity/25); + popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1)); ledIndex = SEGMENT.start + popcorn[i].position; if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); } else { // if kernel is inactive, randomly pop it @@ -2489,7 +2526,7 @@ uint16_t WS2812FX::mode_popcorn(void) { } } - return SPEED_FORMULA_L; + return FRAMETIME; } diff --git a/wled00/FX.h b/wled00/FX.h index 92f3cf78d..29a667939 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -84,7 +84,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 92 +#define MODE_COUNT 94 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -177,7 +177,9 @@ #define FX_MODE_CANDLE 88 #define FX_MODE_BOUNCINGBALLS 89 #define FX_MODE_SINELON 90 -#define FX_MODE_POPCORN 91 +#define FX_MODE_SINELON_DUAL 91 +#define FX_MODE_SINELON_RAINBOW 92 +#define FX_MODE_POPCORN 93 class WS2812FX { @@ -324,6 +326,8 @@ class WS2812FX { _mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade; _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_BouncingBalls; _mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon; + _mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual; + _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; @@ -510,6 +514,8 @@ class WS2812FX { mode_spots_fade(void), mode_BouncingBalls(void), mode_sinelon(void), + mode_sinelon_dual(void), + mode_sinelon_rainbow(void), mode_popcorn(void), mode_glitter(void), mode_candle(void); @@ -553,6 +559,7 @@ class WS2812FX { theater_chase(uint32_t, uint32_t, bool), running_base(bool), larson_scanner(bool), + sinelon_base(bool,bool), dissolve(uint32_t), chase(uint32_t, uint32_t, uint32_t, bool), gradient_base(bool), @@ -587,8 +594,8 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "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", -"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle", -"Bouncing Balls", "Sinelon","Popcorn" +"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Bouncing Balls", +"Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn" ])====="; From 50082043ef4c3870446df7ce0ead46c6532201c1 Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Fri, 27 Dec 2019 20:58:06 +0100 Subject: [PATCH 03/15] add exploding fireworks --- wled00/FX.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 12 +++-- 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c862a0854..207ce1ff8 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2348,3 +2348,125 @@ uint16_t WS2812FX::mode_candle() return FRAMETIME; } + + +/* + * Exploding fireworks effect + * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ + */ +#define NUM_SPARKS 60 // max number (could be NUM_LEDS / 2); +uint16_t WS2812FX::mode_exploding_fireworks(void) +{ + if (SEGENV.aux1==0) { // manage states using params + flare(); + } else { + explode(); + } + + return FRAMETIME; +} + +static float sparkPos[NUM_SPARKS]; +static float sparkVel[NUM_SPARKS]; +static float sparkCol[NUM_SPARKS]; +static float flarePos; +static float flareVel; +static float brightness; + +void WS2812FX::flare() { + float gravity = -0.02 - (SEGMENT.speed/10000.0); // m/s/s + + if (SEGENV.aux0 == 0) { //use aux0 for initialization flag + flarePos = 0; + flareVel = float(random16(150, 190)) / (75+SEGMENT.intensity/4); // trial and error to get reasonable range + brightness = 1; +Serial.printf("%3.1f %3.3f %3d",flareVel,gravity,SEGMENT.intensity); +Serial.println(""); + // initialize launch sparks + for (int i = 0; i < 5; i++) { + sparkPos[i] = 0; + sparkVel[i] = (float(random8(100,255)) / 255.0) * (flareVel/ 3.0); // random around 20% of flare velocity + sparkCol[i] = sparkVel[i] * 1000.0; + sparkCol[i] = constrain(sparkCol[i], 0, 255); + } + SEGENV.aux0=1; + } + + // launch + if (flareVel >= -.3) { + // sparks + for (int i = 0; i < 5; i++) { + sparkPos[i] += sparkVel[i]; + sparkPos[i] = constrain(sparkPos[i], 0, SEGLEN-1); + sparkVel[i] += gravity; + sparkCol[i] += -.8; + sparkCol[i] = constrain(sparkCol[i], 0, 255); + + CRGB color = HeatColor(sparkCol[i]); + setPixelColor(SEGMENT.stop - int(sparkPos[i]),color.red,color.green,color.blue); + } + // flare + fade_out(255); + setPixelColor(SEGMENT.stop - int(flarePos),brightness*255,brightness*255,brightness*255); + + flarePos += flareVel; + flarePos = constrain(flarePos, 0, SEGLEN-1); + flareVel += gravity; + brightness *= .975; + } else { + SEGENV.aux0=0; // allow next state to init + SEGENV.aux1=1; // ready to explode + } +} + +/* + * Explode! + * + * Explosion happens where the flare ended. + * Size is proportional to the height. + */ +void WS2812FX::explode() { + int nSparks = flarePos / 2; // works out to look about right + nSparks = constrain(nSparks,0,NUM_SPARKS); + static float dying_gravity; + float gravity = -0.02 - (SEGMENT.speed/10000.0); + float c1=120; + float c2=50; + + // initialize sparks + if (SEGENV.aux0==0) { + for (int i = 0; i < nSparks; i++) { + sparkPos[i] = flarePos; + sparkVel[i] = (float(random16(0, 20000)) / 10000.0) - 1.0; // from -1 to 1 + sparkCol[i] = abs(sparkVel[i] * 500.0); // set colors before scaling velocity to keep them bright + sparkCol[i] = constrain(sparkCol[i], 0, 255); + sparkVel[i] *= flarePos / SEGLEN; // proportional to height + } + sparkCol[0] = 255; // this will be our known spark + dying_gravity = gravity; + SEGENV.aux0=1; + } + + if (sparkCol[0] > c2/10 ) {//&& sparkPos[0] > 0) { // as long as our known spark is lit, work with all the sparks + fade_out(255); + for (int i = 0; i < nSparks; i++) { + sparkPos[i] += sparkVel[i]; + sparkPos[i] = constrain(sparkPos[i], 0, SEGLEN-1); + sparkVel[i] += dying_gravity; + sparkCol[i] *= .97; + sparkCol[i] = constrain(sparkCol[i], 0, 255); // red cross dissolve + + if(sparkCol[i] > c1) { // fade white to yellow + setPixelColor(SEGMENT.stop - sparkPos[i],255, 255, (255 * (sparkCol[i] - c1)) / (255 - c1)); + } else if (sparkCol[i] < c2) { // fade from red to black + setPixelColor(SEGMENT.stop - sparkPos[i],(255 * sparkCol[i]) / c2, 0, 0); + } else { // fade from yellow to red + setPixelColor(SEGMENT.stop - sparkPos[i],255, (255 * (sparkCol[i] - c2)) / (c1 - c2), 0); + } + } + dying_gravity *= .995; // as sparks burn out they fall slower + } else { + SEGENV.aux0=0; + SEGENV.aux1=0; + } +} \ No newline at end of file diff --git a/wled00/FX.h b/wled00/FX.h index 9ba70357d..7fa1a8487 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -84,7 +84,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 89 +#define MODE_COUNT 90 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -175,6 +175,7 @@ #define FX_MODE_SPOTS_FADE 86 #define FX_MODE_GLITTER 87 #define FX_MODE_CANDLE 88 +#define FX_MODE_EXPLODING_FIREWORKS 89 class WS2812FX { @@ -321,6 +322,8 @@ class WS2812FX { _mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade; _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; + _mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; + _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -359,6 +362,8 @@ class WS2812FX { resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), + flare(void), + explode(void), show(void); bool @@ -503,7 +508,8 @@ class WS2812FX { mode_spots(void), mode_spots_fade(void), mode_glitter(void), - mode_candle(void); + mode_candle(void), + mode_exploding_fireworks(void); private: @@ -577,7 +583,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "Dual Scanner","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Smooth Meteor","Railway","Ripple", -"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle" +"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks 1D" ])====="; From ce5839ce27d5e7a6cd5deaeeb8e0dd5ad888fc60 Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Fri, 27 Dec 2019 22:20:34 +0100 Subject: [PATCH 04/15] remove debug message --- wled00/FX.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 207ce1ff8..af0974b03 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2380,8 +2380,7 @@ void WS2812FX::flare() { flarePos = 0; flareVel = float(random16(150, 190)) / (75+SEGMENT.intensity/4); // trial and error to get reasonable range brightness = 1; -Serial.printf("%3.1f %3.3f %3d",flareVel,gravity,SEGMENT.intensity); -Serial.println(""); + // initialize launch sparks for (int i = 0; i < 5; i++) { sparkPos[i] = 0; From 67758b49806e281596ab4e31a067f4214771e5f3 Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Mon, 30 Dec 2019 00:20:05 +0100 Subject: [PATCH 05/15] rework sinelon, bouncing, popcorn to remove some unnecessary statics --- wled00/FX.cpp | 62 +++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5461347e3..79837df0d 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2343,34 +2343,28 @@ uint16_t WS2812FX::mode_spots_fade() /* * Bouncing Balls Effect -* Adapted from: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_BouncingBalls(void) { // number of balls based on intensity setting to max of 7 (cycles colors) // non-chosen color is a random color int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); - // ideally use speed for gravity effect on the bounce - float Gravity = -9.81; + const int maxBallCount = 7; + + float Gravity = -9.81; // standard value of gravity + int Position[maxBallCount]; // current position of the ball normalized to segment size + float TimeSinceLastBounce[maxBallCount] = {0}; // time difference for physics calculations + int StartHeight = 1; // height in metres (strip length) + float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) + float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight); - const int maxBallCount = 7; + static float ImpactVelocity[maxBallCount] = {ImpactVelocityStart}; + static long ClockTimeSinceLastBounce[maxBallCount] = {millis()}; static float Height[maxBallCount]; - static float ImpactVelocity[maxBallCount]; - static int Position[maxBallCount]; - static float TimeSinceLastBounce[maxBallCount]; - static long ClockTimeSinceLastBounce[maxBallCount]; - static int StartHeight = 1; // height in metres (strip length) - static float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) - static float ImpactVelocityStart=sqrt( -2 * Gravity * StartHeight); - - // Different from the examples, to allow for initialisation of the first bounce - if (Dampening[0] == 0) { - for (int i = 0 ; i < maxBallCount ; i++) { - ClockTimeSinceLastBounce[i] = millis(); - ImpactVelocity[i] = ImpactVelocityStart; - TimeSinceLastBounce[i] = 0; - Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); - } + + //set up variable damping for better effect using multiple balls + for (int i = 0 ; i < maxBallCount ; i++) { + Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); } for (int i = 0 ; i < balls ; i++) { @@ -2410,7 +2404,7 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { fade_out(SEGMENT.intensity); int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); - static int prevpos = 0; + int prevpos = SEGENV.aux0; uint32_t color1 = color_from_palette(pos, true, false, 0); if (rainbow) { @@ -2418,13 +2412,11 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { } if( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) - { + for (uint16_t i = pos; i < prevpos; i++) { setPixelColor(i, color1); } } else { - for (uint16_t i = prevpos; i < pos; i++) - { + for (uint16_t i = prevpos; i < pos; i++) { setPixelColor(SEGMENT.start + i, color1); } } @@ -2438,19 +2430,18 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { if (rainbow) { color2 = color_wheel((pos % 8) * 32); } - if( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) - { + if ( pos < prevpos ) { + for (uint16_t i = pos; i < prevpos; i++) { setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); } } else { - for (uint16_t i = prevpos; i < pos; i++) - { + for (uint16_t i = prevpos; i < pos; i++) { setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); } } } - prevpos = pos; + + SEGENV.aux0 = pos; return FRAMETIME; } @@ -2497,12 +2488,9 @@ uint16_t WS2812FX::mode_popcorn(void) { uint32_t popcornColor = SEGCOLOR(0); uint32_t bgColor = SEGCOLOR(1); if(popcornColor == bgColor) popcornColor = color_wheel(random8()); - + static kernel popcorn[MAX_NUM_POPCORN]; - static float coeff = 0.0f; - if(coeff == 0.0f) { // calculate the velocity coeff once (the secret sauce) - coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; - } + float coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; fill(SEGCOLOR(1)); @@ -2513,6 +2501,7 @@ uint16_t WS2812FX::mode_popcorn(void) { if(isActive) { // if kernel is active, update its position popcorn[i].position += popcorn[i].velocity; popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1)); + ledIndex = SEGMENT.start + popcorn[i].position; if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); } else { // if kernel is inactive, randomly pop it @@ -2520,6 +2509,7 @@ uint16_t WS2812FX::mode_popcorn(void) { popcorn[i].position = 0.0f; popcorn[i].velocity = coeff * (random(66, 100) / 100.0f); popcorn[i].color = popcornColor; + ledIndex = SEGMENT.start; setPixelColor(ledIndex, popcorn[i].color); } From eb251050a526b00cde387fdbe9696fbd8f747a7f Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Mon, 30 Dec 2019 00:22:28 +0100 Subject: [PATCH 06/15] fix exploding firworks controls to match the slider direction to the effect --- wled00/FX.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index af0974b03..7e920352f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2378,7 +2378,7 @@ void WS2812FX::flare() { if (SEGENV.aux0 == 0) { //use aux0 for initialization flag flarePos = 0; - flareVel = float(random16(150, 190)) / (75+SEGMENT.intensity/4); // trial and error to get reasonable range + flareVel = float(random16(150, 190)) / (75+(255-SEGMENT.intensity)/4); // trial and error to get reasonable range brightness = 1; // initialize launch sparks @@ -2428,10 +2428,10 @@ void WS2812FX::explode() { int nSparks = flarePos / 2; // works out to look about right nSparks = constrain(nSparks,0,NUM_SPARKS); static float dying_gravity; - float gravity = -0.02 - (SEGMENT.speed/10000.0); + float gravity = -0.02 - ((255-SEGMENT.speed)/10000.0); float c1=120; float c2=50; - + // initialize sparks if (SEGENV.aux0==0) { for (int i = 0; i < nSparks; i++) { From 8013f8d5b35cff4947ef36f48c91b173037fdb88 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 2 Jan 2020 02:12:10 +0100 Subject: [PATCH 07/15] Update year --- wled00/FX.cpp | 11 +++++++++-- wled00/FX.h | 4 +--- wled00/html_settings.h | 4 ++-- wled00/wled00.ino | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dac4aade4..291703b67 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2446,6 +2446,7 @@ uint16_t WS2812FX::mode_candle() */ #define STARBURST_MAX_FRAG 12 +//each needs 64 byte typedef struct particle { CRGB color; uint32_t birth =0; @@ -2456,10 +2457,16 @@ typedef struct particle { } star; uint16_t WS2812FX::mode_starburst(void) { + uint8_t numStars = 1 + (SEGLEN >> 3); + if (numStars > 15) numStars = 15; + uint16_t dataSize = sizeof(star) * numStars; + + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t it = millis(); - const uint8_t numStars = 15; - static star stars[numStars]; + star* stars = reinterpret_cast(SEGENV.data); + float maxSpeed = 375.0f; // Max velocity float particleIgnition = 250.0f; // How long to "flash" float particleFadeTime = 1500.0f; // Fade out time diff --git a/wled00/FX.h b/wled00/FX.h index bb33741d0..5bf896cb3 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -248,9 +248,7 @@ class WS2812FX { return true; } void deallocateData(){ - if (data) { - delete[] data; - } + delete[] data; data = nullptr; WS2812FX::_usedSegmentData -= _dataLen; _dataLen = 0; diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 678aa2812..a15e62ebb 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -368,10 +368,10 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
Enable ArduinoOTA:

About

-WLED version 0.9.0-b1

+WLED version 0.9.0-b2

Contributors, dependencies and special thanks
A huge thank you to everyone who helped me create WLED!

-(c) 2016-2019 Christian Schwinne
+(c) 2016-2020 Christian Schwinne
Licensed under the MIT license

Server message: Response error!
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index c77eae7ac..7860c20b8 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1912312 +#define VERSION 2001011 char versionString[] = "0.9.0-b2"; From 3d359229cfb0de997b8315e34dfb09ccbdda5cb6 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 2 Jan 2020 20:41:15 +0100 Subject: [PATCH 08/15] Reworked effects to use data instead of locked --- wled00/FX.cpp | 164 ++++++++++++++++++++++++++++------------------ wled00/FX.h | 3 - wled00/FX_fcn.cpp | 30 +-------- wled00/wled00.ino | 2 +- 4 files changed, 105 insertions(+), 94 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 291703b67..ca4b3d434 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -236,22 +236,24 @@ uint16_t WS2812FX::mode_random_color(void) { // * to new random colors. */ uint16_t WS2812FX::mode_dynamic(void) { + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + if(SEGENV.call == 0) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) _locked[i] = random8(); + for (uint16_t i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); } uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15; uint32_t it = now / cycleTime; if (it != SEGENV.step && SEGMENT.speed != 0) //new color { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { - if (random8() <= SEGMENT.intensity) _locked[i] = random8(); + for (uint16_t i = 0; i < SEGLEN; i++) { + if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); } SEGENV.step = it; } - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { - setPixelColor(i, color_wheel(_locked[i])); + for (uint16_t i = 0; i < SEGLEN; i++) { + setPixelColor(SEGMENT.start + i, color_wheel(SEGENV.data[i])); } return FRAMETIME; } @@ -1467,17 +1469,24 @@ typedef struct Oscillator { */ uint16_t WS2812FX::mode_oscillate(void) { - static oscillator oscillators[NUM_COLORS] = { - {SEGLEN/4, SEGLEN/8, 1, 1}, - {SEGLEN/4*3, SEGLEN/8, 1, 2}, - {SEGLEN/4*2, SEGLEN/8, -1, 1} - - }; + uint8_t numOscillators = 3; + uint16_t dataSize = sizeof(oscillator) * numOscillators; + + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + Oscillator* oscillators = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) + { + oscillators[0] = {SEGLEN/4, SEGLEN/8, 1, 1}; + oscillators[1] = {SEGLEN/4*3, SEGLEN/8, 1, 2}; + oscillators[2] = {SEGLEN/4*2, SEGLEN/8, -1, 1}; + } uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); uint32_t it = now / cycleTime; - for(int8_t i=0; i < sizeof(oscillators)/sizeof(oscillators[0]); i++) { + for(uint8_t i=0; i < numOscillators; i++) { // 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; oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); @@ -1494,9 +1503,9 @@ uint16_t WS2812FX::mode_oscillate(void) } } - for(int16_t i=0; i < SEGLEN; i++) { + for(uint16_t i=0; i < SEGLEN; i++) { uint32_t color = BLACK; - for(int8_t j=0; j < sizeof(oscillators)/sizeof(oscillators[0]); j++) { + for(uint8_t j=0; j < numOscillators; j++) { if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { color = (color == BLACK) ? SEGMENT.colors[j] : color_blend(color, SEGMENT.colors[j], 128); } @@ -1666,30 +1675,34 @@ uint16_t WS2812FX::mode_fire_2012() { uint32_t it = now >> 5; //div 32 + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + + byte* heat = SEGENV.data; + if (it != SEGENV.step) { // Step 1. Cool down every cell a little - for( int i = SEGMENT.start; i < SEGMENT.stop; i++) { - _locked[i] = qsub8(_locked[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2)); + for (uint16_t i = 0; i < SEGLEN; i++) { + SEGENV.data[i] = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2)); } // Step 2. Heat from each cell drifts 'up' and diffuses a little - for( int k= SEGMENT.stop -1; k >= SEGMENT.start + 2; k--) { - _locked[k] = (_locked[k - 1] + _locked[k - 2] + _locked[k - 2] ) / 3; + for (uint16_t k= SEGLEN -1; k > 1; k--) { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; } // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if( random8() <= SEGMENT.intensity ) { - int y = SEGMENT.start + random8(7); - if (y < SEGMENT.stop) _locked[y] = qadd8(_locked[y], random8(160,255) ); + if (random8() <= SEGMENT.intensity) { + uint8_t y = random8(7); + if (y < SEGLEN) heat[y] = qadd8(heat[y], random8(160,255)); } SEGENV.step = it; } // Step 4. Map from heat cells to LED colors - for( int j = SEGMENT.start; j < SEGMENT.stop; j++) { - CRGB color = ColorFromPalette( currentPalette, min(_locked[j],240), 255, LINEARBLEND); - setPixelColor(j, color.red, color.green, color.blue); + for (uint16_t j = 0; j < SEGLEN; j++) { + CRGB color = ColorFromPalette(currentPalette, min(heat[j],240), 255, LINEARBLEND); + setPixelColor(SEGMENT.start + j, color.red, color.green, color.blue); } return FRAMETIME; } @@ -1870,18 +1883,25 @@ uint16_t WS2812FX::mode_noise16_4() //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e uint16_t WS2812FX::mode_colortwinkle() { + uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + CRGB fastled_col, prev; fract8 fadeUpAmount = 8 + (SEGMENT.speed/4), fadeDownAmount = 5 + (SEGMENT.speed/7); - for( uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { fastled_col = col_to_crgb(getPixelColor(i)); prev = fastled_col; - if(_locked[i]) { + uint16_t index = (i - SEGMENT.start) >> 3; + uint8_t bitNum = (i - SEGMENT.start) & 0x07; + bool fadeUp = bitRead(SEGENV.data[index], bitNum); + + if (fadeUp) { CRGB incrementalColor = fastled_col; incrementalColor.nscale8_video( fadeUpAmount); fastled_col += incrementalColor; - if( fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { - _locked[i] = false; + if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { + bitWrite(SEGENV.data[index], bitNum, false); } setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); @@ -1898,13 +1918,15 @@ uint16_t WS2812FX::mode_colortwinkle() for (uint16_t j = 0; j <= SEGLEN / 50; j++) { - if ( random8() <= SEGMENT.intensity ) { + if (random8() <= SEGMENT.intensity) { for (uint8_t times = 0; times < 5; times++) //attempt to spawn a new pixel 5 times { int i = SEGMENT.start + random16(SEGLEN); if(getPixelColor(i) == 0) { fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); - _locked[i] = true; + uint16_t index = (i - SEGMENT.start) >> 3; + uint8_t bitNum = (i - SEGMENT.start) & 0x07; + bitWrite(SEGENV.data[index], bitNum, true); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); break; //only spawn 1 new pixel per frame per 50 LEDs } @@ -1938,29 +1960,33 @@ uint16_t WS2812FX::mode_lake() { // 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() { + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + + byte* trail = SEGENV.data; + byte meteorSize= 1+ SEGLEN / 10; uint16_t counter = now * ((SEGMENT.speed >> 2) +8); uint16_t in = counter * SEGLEN >> 16; // fade all leds to colors[1] in LEDs one step - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { if (random8() <= 255 - SEGMENT.intensity) { byte meteorTrailDecay = 128 + random8(127); - _locked[i] = scale8(_locked[i], meteorTrailDecay); - setPixelColor(i, color_from_palette(_locked[i], false, true, 255)); + trail[i] = scale8(trail[i], meteorTrailDecay); + setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255)); } } // draw meteor for(int j = 0; j < meteorSize; j++) { uint16_t index = in + j; - if(in + j >= SEGMENT.stop) { - index = SEGMENT.start + (in + j - SEGMENT.stop); + if(index >= SEGLEN) { + index = (in + j - SEGLEN); } - _locked[index] = 240; - setPixelColor(index, color_from_palette(_locked[index], false, true, 255)); + trail[index] = 240; + setPixelColor(SEGMENT.start + index, color_from_palette(trail[index], false, true, 255)); } return FRAMETIME; @@ -1971,29 +1997,33 @@ uint16_t WS2812FX::mode_meteor() { // 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() { + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + + byte* trail = SEGENV.data; + byte meteorSize= 1+ SEGLEN / 10; - uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, SEGMENT.start, SEGMENT.stop -1); + uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1); // fade all leds to colors[1] in LEDs one step - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { - if (_locked[i] != 0 && random8() <= 255 - SEGMENT.intensity) + for (uint16_t i = 0; i < SEGLEN; i++) { + if (trail[i] != 0 && random8() <= 255 - SEGMENT.intensity) { int change = 3 - random8(12); //change each time between -8 and +3 - _locked[i] += change; - if (_locked[i] > 245) _locked[i] = 0; - if (_locked[i] > 240) _locked[i] = 240; - setPixelColor(i, color_from_palette(_locked[i], false, true, 255)); + trail[i] += change; + if (trail[i] > 245) trail[i] = 0; + if (trail[i] > 240) trail[i] = 240; + setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255)); } } // draw meteor for(int j = 0; j < meteorSize; j++) { uint16_t index = in + j; - if(in + j >= SEGMENT.stop) { - index = SEGMENT.start + (in + j - SEGMENT.stop); + if(in + j >= SEGLEN) { + index = (in + j - SEGLEN); } - setPixelColor(index, color_blend(getPixelColor(index), color_from_palette(240, false, true, 255), 48)); - _locked[index] = 240; + setPixelColor(SEGMENT.start + index, color_blend(getPixelColor(SEGMENT.start + index), color_from_palette(240, false, true, 255), 48)); + trail[index] = 240; } SEGENV.step += SEGMENT.speed +1; @@ -2035,23 +2065,35 @@ uint16_t WS2812FX::mode_railway() //Water ripple //propagation velocity from speed //drop rate from intensity + +//4 bytes +typedef struct Ripple { + uint8_t state; + uint8_t color; + uint16_t pos; +} ripple; + uint16_t WS2812FX::mode_ripple() { - uint16_t maxripples = SEGLEN / 4; - if (maxripples == 0) return mode_static(); + uint16_t maxRipples = 1 + (SEGLEN >> 2); + if (maxRipples > 100) maxRipples = 100; + uint16_t dataSize = sizeof(ripple) * maxRipples; + + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + Ripple* ripples = reinterpret_cast(SEGENV.data); fill(SEGCOLOR(1)); //draw wave - for (uint16_t rippleI = 0; rippleI < maxripples; rippleI++) + for (uint16_t i = 0; i < maxRipples; i++) { - uint16_t storeI = SEGMENT.start + 4*rippleI; - uint16_t ripplestate = _locked[storeI]; + uint16_t ripplestate = ripples[i].state; if (ripplestate) { uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation - uint16_t rippleorigin = (_locked[storeI+1] << 8) + _locked[storeI+2]; - uint32_t col = color_from_palette(_locked[storeI+3], false, false, 255); + uint16_t rippleorigin = ripples[i].pos; + uint32_t col = color_from_palette(ripples[i].color, false, false, 255); uint16_t propagation = ((ripplestate/rippledecay -1) * SEGMENT.speed); int16_t propI = propagation >> 8; uint8_t propF = propagation & 0xFF; @@ -2061,7 +2103,7 @@ uint16_t WS2812FX::mode_ripple() for (int16_t v = left; v < left +4; v++) { uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp); - if (v >= SEGMENT.start) + if (v < SEGMENT.stop && v >= SEGMENT.start) { setPixelColor(v, color_blend(getPixelColor(v), col, mag)); } @@ -2072,16 +2114,14 @@ uint16_t WS2812FX::mode_ripple() } } ripplestate += rippledecay; - _locked[storeI] = (ripplestate > 254) ? 0 : ripplestate; + ripples[i].state = (ripplestate > 254) ? 0 : ripplestate; } else //randomly create new wave { if (random16(IBN + 10000) <= SEGMENT.intensity) { - _locked[storeI] = 1; - uint16_t origin = SEGMENT.start + random16(SEGLEN); - _locked[storeI+1] = origin >> 8; - _locked[storeI+2] = origin & 0xFF; - _locked[storeI+3] = random8(); //color + ripples[i].state = 1; + ripples[i].pos = SEGMENT.start + random16(SEGLEN); + ripples[i].color = random8(); //color } } } diff --git a/wled00/FX.h b/wled00/FX.h index 5bf896cb3..8c51f1427 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -358,7 +358,6 @@ class WS2812FX { currentMilliamps = 0; timebase = 0; _locked = nullptr; - _modeUsesLock = false; bus = new NeoPixelWrapper(); resetSegments(); } @@ -552,10 +551,8 @@ class WS2812FX { void handle_palette(void); void fill(uint32_t); - bool modeUsesLock(uint8_t); bool - _modeUsesLock, _rgbwMode, _cronixieMode, _cronixieBacklightEnabled, diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7fa6eae89..25f40a88c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -96,14 +96,6 @@ void WS2812FX::service() { _triggered = false; } -bool WS2812FX::modeUsesLock(uint8_t m) -{ - if (m == FX_MODE_FIRE_2012 || m == FX_MODE_COLORTWINKLE || - m == FX_MODE_METEOR || m == FX_MODE_METEOR_SMOOTH || - m == FX_MODE_RIPPLE || m == FX_MODE_DYNAMIC ) return true; - return false; -} - void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { uint8_t w = (c >> 24); uint8_t r = (c >> 16); @@ -115,7 +107,7 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { i = i * (_disableNLeds+1); - if (_locked[i] && !_modeUsesLock) return; + if (_locked[i]) return; if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment byte tmpg = g; switch (colorOrder) //0 = Grb, default @@ -273,7 +265,6 @@ void WS2812FX::trigger() { void WS2812FX::setMode(uint8_t segid, uint8_t m) { if (segid >= MAX_NUM_SEGMENTS) return; - bool anyUsedLock = _modeUsesLock, anyUseLock = false; if (m >= MODE_COUNT) m = MODE_COUNT - 1; if (_segments[segid].mode != m) @@ -281,13 +272,6 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) { _segment_runtimes[segid].reset(); _segments[segid].mode = m; } - - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (modeUsesLock(_segments[i].mode)) anyUseLock = true; - } - if (anyUsedLock && !anyUseLock) unlockAll(); - _modeUsesLock = anyUseLock; } uint8_t WS2812FX::getModeCount() @@ -454,12 +438,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) { if (n >= MAX_NUM_SEGMENTS) return; Segment& seg = _segments[n]; if (seg.start == i1 && seg.stop == i2) return; - if (seg.isActive() && modeUsesLock(seg.mode)) - { - _modeUsesLock = false; - unlockRange(seg.start, seg.stop); - _modeUsesLock = true; - } + _segment_index = n; fill(0); //turn old segment range off if (i2 <= i1) //disable segment { @@ -491,7 +470,6 @@ void WS2812FX::resetSegments() { void WS2812FX::setIndividual(uint16_t i, uint32_t col) { - if (modeUsesLock(SEGMENT.mode)) return; if (i >= 0 && i < _length) { _locked[i] = false; @@ -513,13 +491,11 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) void WS2812FX::lock(uint16_t i) { - if (_modeUsesLock) return; if (i < _length) _locked[i] = true; } void WS2812FX::lockRange(uint16_t i, uint16_t i2) { - if (_modeUsesLock) return; for (uint16_t x = i; x < i2; x++) { if (x < _length) _locked[i] = true; @@ -528,13 +504,11 @@ void WS2812FX::lockRange(uint16_t i, uint16_t i2) void WS2812FX::unlock(uint16_t i) { - if (_modeUsesLock) return; if (i < _length) _locked[i] = false; } void WS2812FX::unlockRange(uint16_t i, uint16_t i2) { - if (_modeUsesLock) return; for (uint16_t x = i; x < i2; x++) { if (x < _length) _locked[x] = false; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 7860c20b8..3144b56cc 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001011 +#define VERSION 2001021 char versionString[] = "0.9.0-b2"; From 9bf534288ca860a86e856e27c84370e68a75419c Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 2 Jan 2020 22:10:59 +0100 Subject: [PATCH 09/15] Removed pixel locking --- wled00/FX.h | 14 +++++----- wled00/FX_fcn.cpp | 55 ++++++-------------------------------- wled00/wled00.ino | 3 ++- wled00/wled03_set.ino | 24 ----------------- wled00/wled05_init.ino | 1 + wled00/wled07_notify.ino | 2 -- wled00/wled11_ol.ino | 32 ++++++++++------------ wled00/wled13_cronixie.ino | 1 - 8 files changed, 31 insertions(+), 101 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 8c51f1427..c6d458d87 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -187,6 +187,9 @@ class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); + + // pre show callback + typedef void (*show_callback) (void); // segment parameters public: @@ -357,7 +360,6 @@ class WS2812FX { ablMilliampsMax = 850; currentMilliamps = 0; timebase = 0; - _locked = nullptr; bus = new NeoPixelWrapper(); resetSegments(); } @@ -374,13 +376,8 @@ class WS2812FX { driverModeCronixie(bool b), setCronixieDigits(byte* d), setCronixieBacklight(bool b), - setIndividual(uint16_t i, uint32_t col), setRange(uint16_t i, uint16_t i2, uint32_t col), - lock(uint16_t i), - lockRange(uint16_t i, uint16_t i2), - unlock(uint16_t i), - unlockRange(uint16_t i, uint16_t i2), - unlockAll(void), + setShowCallback(show_callback cb), setTransitionMode(bool t), trigger(void), setSegment(uint8_t n, uint16_t start, uint16_t stop), @@ -559,11 +556,12 @@ class WS2812FX { _skipFirstMode, _triggered; - byte* _locked; byte _cronixieDigits[6]; mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element + show_callback _callback = nullptr; + // mode helper functions uint16_t blink(uint32_t, uint32_t, bool strobe, bool), diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 25f40a88c..3081c2be5 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -32,7 +32,7 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uint8_t disableNLeds) { - if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && disableNLeds == _disableNLeds) return; + if (supportWhite == _rgbwMode && countPixels == _length && disableNLeds == _disableNLeds) return; RESET_RUNTIME; _rgbwMode = supportWhite; _skipFirstMode = skipFirst; @@ -59,13 +59,9 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uin bus->Begin((NeoPixelType)ty, _lengthRaw); - delete[] _locked; - _locked = new byte[_length]; - _segments[0].start = 0; _segments[0].stop = _usableCount; - - unlockAll(); + setBrightness(_brightness); } @@ -107,7 +103,6 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { i = i * (_disableNLeds+1); - if (_locked[i]) return; if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment byte tmpg = g; switch (colorOrder) //0 = Grb, default @@ -200,6 +195,8 @@ void WS2812FX::setCronixieDigits(byte d[]) //you can set it to 0 if the ESP is powered by USB and the LEDs by external void WS2812FX::show(void) { + if (_callback) _callback(); + //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 @@ -468,56 +465,20 @@ void WS2812FX::resetSegments() { _segment_runtimes[0].reset(); } -void WS2812FX::setIndividual(uint16_t i, uint32_t col) -{ - if (i >= 0 && i < _length) - { - _locked[i] = false; - setPixelColor(i, col); - _locked[i] = true; - } -} - void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) { if (i2 >= i) { - for (uint16_t x = i; x <= i2; x++) setIndividual(x,col); + for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col); } else { - for (uint16_t x = i2; x <= i; x++) setIndividual(x,col); + for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col); } } -void WS2812FX::lock(uint16_t i) +void WS2812FX::setShowCallback(show_callback cb) { - if (i < _length) _locked[i] = true; -} - -void WS2812FX::lockRange(uint16_t i, uint16_t i2) -{ - for (uint16_t x = i; x < i2; x++) - { - if (x < _length) _locked[i] = true; - } -} - -void WS2812FX::unlock(uint16_t i) -{ - if (i < _length) _locked[i] = false; -} - -void WS2812FX::unlockRange(uint16_t i, uint16_t i2) -{ - for (uint16_t x = i; x < i2; x++) - { - if (x < _length) _locked[x] = false; - } -} - -void WS2812FX::unlockAll() -{ - for (int i=0; i < _length; i++) _locked[i] = false; + _callback = cb; } void WS2812FX::setTransitionMode(bool t) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 3144b56cc..5fc1092f1 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001021 +#define VERSION 2001022 char versionString[] = "0.9.0-b2"; @@ -424,6 +424,7 @@ AsyncMqttClient* mqtt = NULL; void colorFromUint32(uint32_t,bool=false); void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte); void handleE131Packet(e131_packet_t*, IPAddress); +void handleOverlayDraw(); #define E131_MAX_UNIVERSE_COUNT 9 diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 9c753fec2..b20103ce8 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -200,7 +200,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (request->hasArg("OL")){ overlayDefault = request->arg("OL").toInt(); - if (overlayCurrent != overlayDefault) strip.unlockAll(); overlayCurrent = overlayDefault; } @@ -459,29 +458,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) pos = req.indexOf("OL="); if (pos > 0) { overlayCurrent = getNumVal(&req, pos); - strip.unlockAll(); - } - - //(un)lock pixel (ranges) - pos = req.indexOf("&L="); - if (pos > 0) { - uint16_t index = getNumVal(&req, pos); - pos = req.indexOf("L2="); - bool unlock = req.indexOf("UL") > 0; - if (pos > 0) { - uint16_t index2 = getNumVal(&req, pos); - if (unlock) { - strip.unlockRange(index, index2); - } else { - strip.lockRange(index, index2); - } - } else { - if (unlock) { - strip.unlock(index); - } else { - strip.lock(index); - } - } } //apply macro diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 684893f1b..a347eb4dc 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -96,6 +96,7 @@ void wledInit() void beginStrip() { // Initialize NeoPixel Strip and button + strip.setShowCallback(handleOverlayDraw); #ifdef BTNPIN pinMode(BTNPIN, INPUT_PULLUP); diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 22370072c..8bb739e05 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -79,7 +79,6 @@ void arlsLock(uint32_t timeoutMs) { strip.setPixelColor(i,0,0,0,0); } - strip.unlockAll(); realtimeActive = true; } realtimeTimeout = millis() + timeoutMs; @@ -127,7 +126,6 @@ void handleNotifications() //unlock strip when realtime UDP times out if (realtimeActive && millis() > realtimeTimeout) { - //strip.unlockAll(); strip.setBrightness(bri); realtimeActive = false; //strip.setMode(effectCurrent); diff --git a/wled00/wled11_ol.ino b/wled00/wled11_ol.ino index fc3a47d8b..cfe8a1f9f 100644 --- a/wled00/wled11_ol.ino +++ b/wled00/wled11_ol.ino @@ -1,6 +1,7 @@ /* * Used to draw clock overlays over the strip */ + void initCronixie() { if (overlayCurrent == 3 && !cronixieInit) @@ -24,14 +25,8 @@ void handleOverlays() initCronixie(); updateLocalTime(); checkTimers(); - switch (overlayCurrent) - { - case 0: break;//no overlay - case 1: _overlayAnalogClock(); break;//2 analog clock - case 2: break;//nixie 1-digit, removed - case 3: _overlayCronixie();//Diamex cronixie clock kit - } - if (!countdownMode || overlayCurrent < 3) checkCountdown(); //countdown macro activation must work + checkCountdown(); + if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit overlayRefreshedTime = millis(); } } @@ -40,7 +35,6 @@ void handleOverlays() void _overlayAnalogClock() { int overlaySize = overlayMax - overlayMin +1; - strip.unlockAll(); if (countdownMode) { _overlayAnalogCountdown(); return; @@ -73,23 +67,19 @@ void _overlayAnalogClock() { pix = analogClock12pixel + round((overlaySize / 12.0) *i); if (pix > overlayMax) pix -= overlaySize; - strip.setIndividual(pix, 0x00FFAA); + strip.setPixelColor(pix, 0x00FFAA); } } - if (!analogClockSecondsTrail) strip.setIndividual(secondPixel, 0xFF0000); - strip.setIndividual(minutePixel, 0x00FF00); - strip.setIndividual(hourPixel, 0x0000FF); + if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); + strip.setPixelColor(minutePixel, 0x00FF00); + strip.setPixelColor(hourPixel, 0x0000FF); overlayRefreshMs = 998; } void _overlayAnalogCountdown() { - strip.unlockAll(); - if (now() >= countdownTime) - { - checkCountdown(); - } else + if (now() < countdownTime) { long diff = countdownTime - now(); double pval = 60; @@ -127,3 +117,9 @@ void _overlayAnalogCountdown() } overlayRefreshMs = 998; } + + +void handleOverlayDraw() { + if (overlayCurrent != 1) return; //only analog clock + _overlayAnalogClock(); +} diff --git a/wled00/wled13_cronixie.ino b/wled00/wled13_cronixie.ino index 4c5a74961..de89aede2 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/wled13_cronixie.ino @@ -145,7 +145,6 @@ void setCronixie() void _overlayCronixie() { - if (countdownMode) checkCountdown(); #ifndef WLED_DISABLE_CRONIXIE byte h = hour(local); byte h0 = h; From cdef7a53a43c5c223539f85de010dc01a4e26407 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 3 Jan 2020 17:47:06 +0100 Subject: [PATCH 10/15] Fireworks 1D working --- wled00/FX.cpp | 224 +++++++++++++++++++++++++------------------------- wled00/FX.h | 2 - 2 files changed, 114 insertions(+), 112 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c93f83057..6051a155f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2605,118 +2605,122 @@ uint16_t WS2812FX::mode_starburst(void) { * Exploding fireworks effect * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ */ -#define NUM_SPARKS 60 // max number (could be NUM_LEDS / 2); + +//each needs 12 byte +typedef struct Spark { + float pos; + float vel; + float col; +} spark; + uint16_t WS2812FX::mode_exploding_fireworks(void) { - if (SEGENV.aux1==0) { // manage states using params - flare(); - } else { - explode(); - } + //allocate segment data + uint16_t numSparks = 2 + (SEGLEN >> 1); + if (numSparks > 80) numSparks = 80; + uint16_t dataSize = sizeof(spark) * numSparks; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + fill(BLACK); + + Spark* sparks = reinterpret_cast(SEGENV.data); + Spark* flare = sparks; //first spark is flare data + + float gravity = -0.02 - (SEGMENT.speed/10000.0); // m/s/s + + if (SEGENV.aux1 == 0) { //FLARE + if (SEGENV.aux0 == 0) { //init flare + flare->pos = 0; + flare->vel = float(random16(150, 190)) / (75+(255-SEGMENT.intensity)/4); // trial and error to get reasonable range + //flare->vel = 2; + flare->col = 1; //brightness + + // initialize launch sparks + for (int i = 1; i < min(6, numSparks); i++) { + sparks[i].pos = 0; + sparks[i].vel = (float(random8(100,255)) / 255.0) * (flare->vel/ 3.0); // random around 20% of flare velocity + sparks[i].col = sparks[i].vel * 1000.0; + sparks[i].col = constrain(sparks[i].col, 0, 255); + } + SEGENV.aux0 = 1; + } + + // launch + if (flare->vel > -.3) { + // sparks + for (int i = 1; i < min(6, numSparks); i++) { + sparks[i].pos += sparks[i].vel; + sparks[i].pos = constrain(sparks[i].pos, 0, SEGLEN-1); + sparks[i].vel += gravity; + sparks[i].col -= 0.8; + sparks[i].col = constrain(sparks[i].col, 0, 255); + + CRGB color = HeatColor(sparks[i].col); + setPixelColor(SEGMENT.start + int(sparks[i].pos),color.red,color.green,color.blue); + } + // flare + //fade_out(255); + setPixelColor(SEGMENT.start + int(flare->pos),flare->col*255,flare->col*255,flare->col*255); + + flare->pos += flare->vel; + flare->pos = constrain(flare->pos, 0, SEGLEN-1); + flare->vel += gravity; + flare->col *= .975; + } else { + SEGENV.aux0 = 0; // allow next state to init + SEGENV.aux1 = 1; // ready to explode + } + } else { + /* + * Explode! + * + * Explosion happens where the flare ended. + * Size is proportional to the height. + */ + int nSparks = flare->pos / 2; // works out to look about right + nSparks = constrain(nSparks, 0, numSparks); + static float dying_gravity; + //gravity = -0.02 - ((255-SEGMENT.speed)/10000.0); + + // initialize sparks + if (SEGENV.aux0==0) { + for (int i = 1; i < nSparks; i++) { + sparks[i].pos = flare->pos; + sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 1.0; // from -1 to 1 + sparks[i].col = abs(sparks[i].vel * 500.0); // set colors before scaling velocity to keep them bright + sparks[i].col = constrain(sparks[i].col, 0, 255); + sparks[i].vel *= flare->pos / SEGLEN; // proportional to height + } + sparks[1].col = 255; // this will be our known spark + dying_gravity = gravity; + SEGENV.aux0 = 1; + } + + float c1 = 120; + float c2 = 50; + + if (sparks[1].col > 5) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks + //fade_out(255); + for (int i = 1; i < nSparks; i++) { + sparks[i].pos += sparks[i].vel; + sparks[i].pos = constrain(sparks[i].pos, 0, SEGLEN-1); + sparks[i].vel += dying_gravity; + sparks[i].col *= .97; + sparks[i].col = constrain(sparks[i].col, 0, 255); // red cross dissolve + + if(sparks[i].pos > c1) { // fade white to yellow + setPixelColor(SEGMENT.start + sparks[i].pos,255, 255, (255 * (sparks[i].col - c1)) / (255 - c1)); + } else if (sparks[i].col < c2) { // fade from red to black + setPixelColor(SEGMENT.start + sparks[i].pos,(255 * sparks[i].col) / c2, 0, 0); + } else { // fade from yellow to red + setPixelColor(SEGMENT.start + sparks[i].pos,255, (255 * (sparks[i].col - c2)) / (c1 - c2), 0); + } + } + dying_gravity *= .995; // as sparks burn out they fall slower + } else { + SEGENV.aux0 = 0; //init again + SEGENV.aux1 = 0; //back to flare + } + } return FRAMETIME; } - -static float sparkPos[NUM_SPARKS]; -static float sparkVel[NUM_SPARKS]; -static float sparkCol[NUM_SPARKS]; -static float flarePos; -static float flareVel; -static float brightness; - -void WS2812FX::flare() { - float gravity = -0.02 - (SEGMENT.speed/10000.0); // m/s/s - - if (SEGENV.aux0 == 0) { //use aux0 for initialization flag - flarePos = 0; - flareVel = float(random16(150, 190)) / (75+(255-SEGMENT.intensity)/4); // trial and error to get reasonable range - brightness = 1; - - // initialize launch sparks - for (int i = 0; i < 5; i++) { - sparkPos[i] = 0; - sparkVel[i] = (float(random8(100,255)) / 255.0) * (flareVel/ 3.0); // random around 20% of flare velocity - sparkCol[i] = sparkVel[i] * 1000.0; - sparkCol[i] = constrain(sparkCol[i], 0, 255); - } - SEGENV.aux0=1; - } - - // launch - if (flareVel >= -.3) { - // sparks - for (int i = 0; i < 5; i++) { - sparkPos[i] += sparkVel[i]; - sparkPos[i] = constrain(sparkPos[i], 0, SEGLEN-1); - sparkVel[i] += gravity; - sparkCol[i] += -.8; - sparkCol[i] = constrain(sparkCol[i], 0, 255); - - CRGB color = HeatColor(sparkCol[i]); - setPixelColor(SEGMENT.stop - int(sparkPos[i]),color.red,color.green,color.blue); - } - // flare - fade_out(255); - setPixelColor(SEGMENT.stop - int(flarePos),brightness*255,brightness*255,brightness*255); - - flarePos += flareVel; - flarePos = constrain(flarePos, 0, SEGLEN-1); - flareVel += gravity; - brightness *= .975; - } else { - SEGENV.aux0=0; // allow next state to init - SEGENV.aux1=1; // ready to explode - } -} - -/* - * Explode! - * - * Explosion happens where the flare ended. - * Size is proportional to the height. - */ -void WS2812FX::explode() { - int nSparks = flarePos / 2; // works out to look about right - nSparks = constrain(nSparks,0,NUM_SPARKS); - static float dying_gravity; - float gravity = -0.02 - ((255-SEGMENT.speed)/10000.0); - float c1=120; - float c2=50; - - // initialize sparks - if (SEGENV.aux0==0) { - for (int i = 0; i < nSparks; i++) { - sparkPos[i] = flarePos; - sparkVel[i] = (float(random16(0, 20000)) / 10000.0) - 1.0; // from -1 to 1 - sparkCol[i] = abs(sparkVel[i] * 500.0); // set colors before scaling velocity to keep them bright - sparkCol[i] = constrain(sparkCol[i], 0, 255); - sparkVel[i] *= flarePos / SEGLEN; // proportional to height - } - sparkCol[0] = 255; // this will be our known spark - dying_gravity = gravity; - SEGENV.aux0=1; - } - - if (sparkCol[0] > c2/10 ) {//&& sparkPos[0] > 0) { // as long as our known spark is lit, work with all the sparks - fade_out(255); - for (int i = 0; i < nSparks; i++) { - sparkPos[i] += sparkVel[i]; - sparkPos[i] = constrain(sparkPos[i], 0, SEGLEN-1); - sparkVel[i] += dying_gravity; - sparkCol[i] *= .97; - sparkCol[i] = constrain(sparkCol[i], 0, 255); // red cross dissolve - - if(sparkCol[i] > c1) { // fade white to yellow - setPixelColor(SEGMENT.stop - sparkPos[i],255, 255, (255 * (sparkCol[i] - c1)) / (255 - c1)); - } else if (sparkCol[i] < c2) { // fade from red to black - setPixelColor(SEGMENT.stop - sparkPos[i],(255 * sparkCol[i]) / c2, 0, 0); - } else { // fade from yellow to red - setPixelColor(SEGMENT.stop - sparkPos[i],255, (255 * (sparkCol[i] - c2)) / (c1 - c2), 0); - } - } - dying_gravity *= .995; // as sparks burn out they fall slower - } else { - SEGENV.aux0=0; - SEGENV.aux1=0; - } -} diff --git a/wled00/FX.h b/wled00/FX.h index 824e0befc..0b2d21afa 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -386,8 +386,6 @@ class WS2812FX { resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), - flare(void), - explode(void), show(void); bool From 5c7d993dbea2c532f08eebf50d88d23edea29bb8 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 3 Jan 2020 22:56:56 +0100 Subject: [PATCH 11/15] Numerous improvements to 1D FW --- wled00/FX.cpp | 117 ++++++++++++++++++++++------------------------ wled00/wled00.ino | 2 +- 2 files changed, 58 insertions(+), 61 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 6051a155f..e1af83383 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2610,7 +2610,8 @@ uint16_t WS2812FX::mode_starburst(void) { typedef struct Spark { float pos; float vel; - float col; + uint16_t col; + uint8_t colIndex; } spark; uint16_t WS2812FX::mode_exploding_fireworks(void) @@ -2623,104 +2624,100 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) fill(BLACK); + bool actuallyReverse = SEGMENT.getOption(1); + //have fireworks start in either direction based on intensity + SEGMENT.setOption(1, SEGENV.step); + Spark* sparks = reinterpret_cast(SEGENV.data); Spark* flare = sparks; //first spark is flare data - float gravity = -0.02 - (SEGMENT.speed/10000.0); // m/s/s + float gravity = -0.0004 - (SEGMENT.speed/800000.0); // m/s/s + gravity *= SEGLEN; - if (SEGENV.aux1 == 0) { //FLARE + if (SEGENV.aux0 < 2) { //FLARE if (SEGENV.aux0 == 0) { //init flare flare->pos = 0; - flare->vel = float(random16(150, 190)) / (75+(255-SEGMENT.intensity)/4); // trial and error to get reasonable range - //flare->vel = 2; - flare->col = 1; //brightness - - // initialize launch sparks - for (int i = 1; i < min(6, numSparks); i++) { - sparks[i].pos = 0; - sparks[i].vel = (float(random8(100,255)) / 255.0) * (flare->vel/ 3.0); // random around 20% of flare velocity - sparks[i].col = sparks[i].vel * 1000.0; - sparks[i].col = constrain(sparks[i].col, 0, 255); - } + uint16_t peakHeight = 75 + random8(180); //0-255 + peakHeight = (peakHeight * (SEGLEN -1)) >> 8; + flare->vel = sqrt(-2.0 * gravity * peakHeight); + flare->col = 255; //brightness + SEGENV.aux0 = 1; } // launch - if (flare->vel > -.3) { - // sparks - for (int i = 1; i < min(6, numSparks); i++) { - sparks[i].pos += sparks[i].vel; - sparks[i].pos = constrain(sparks[i].pos, 0, SEGLEN-1); - sparks[i].vel += gravity; - sparks[i].col -= 0.8; - sparks[i].col = constrain(sparks[i].col, 0, 255); - - CRGB color = HeatColor(sparks[i].col); - setPixelColor(SEGMENT.start + int(sparks[i].pos),color.red,color.green,color.blue); - } + if (flare->vel > 12 * gravity) { // flare - //fade_out(255); - setPixelColor(SEGMENT.start + int(flare->pos),flare->col*255,flare->col*255,flare->col*255); + setPixelColor(SEGMENT.start + int(flare->pos),flare->col,flare->col,flare->col); flare->pos += flare->vel; flare->pos = constrain(flare->pos, 0, SEGLEN-1); flare->vel += gravity; - flare->col *= .975; + flare->col -= 2; } else { - SEGENV.aux0 = 0; // allow next state to init - SEGENV.aux1 = 1; // ready to explode + SEGENV.aux0 = 2; // ready to explode } - } else { + } else if (SEGENV.aux0 < 4) { /* * Explode! * * Explosion happens where the flare ended. * Size is proportional to the height. */ - int nSparks = flare->pos / 2; // works out to look about right + int nSparks = flare->pos; nSparks = constrain(nSparks, 0, numSparks); static float dying_gravity; - //gravity = -0.02 - ((255-SEGMENT.speed)/10000.0); // initialize sparks - if (SEGENV.aux0==0) { + if (SEGENV.aux0 == 2) { for (int i = 1; i < nSparks; i++) { sparks[i].pos = flare->pos; - sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 1.0; // from -1 to 1 - sparks[i].col = abs(sparks[i].vel * 500.0); // set colors before scaling velocity to keep them bright - sparks[i].col = constrain(sparks[i].col, 0, 255); - sparks[i].vel *= flare->pos / SEGLEN; // proportional to height + 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; } - sparks[1].col = 255; // this will be our known spark - dying_gravity = gravity; - SEGENV.aux0 = 1; + //sparks[1].col = 345; // this will be our known spark + dying_gravity = gravity/2; + SEGENV.aux0 = 3; } - - float c1 = 120; - float c2 = 50; - if (sparks[1].col > 5) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks - //fade_out(255); + if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks for (int i = 1; i < nSparks; i++) { sparks[i].pos += sparks[i].vel; - sparks[i].pos = constrain(sparks[i].pos, 0, SEGLEN-1); sparks[i].vel += dying_gravity; - sparks[i].col *= .97; - sparks[i].col = constrain(sparks[i].col, 0, 255); // red cross dissolve - - if(sparks[i].pos > c1) { // fade white to yellow - setPixelColor(SEGMENT.start + sparks[i].pos,255, 255, (255 * (sparks[i].col - c1)) / (255 - c1)); - } else if (sparks[i].col < c2) { // fade from red to black - setPixelColor(SEGMENT.start + sparks[i].pos,(255 * sparks[i].col) / c2, 0, 0); - } else { // fade from yellow to red - setPixelColor(SEGMENT.start + sparks[i].pos,255, (255 * (sparks[i].col - c2)) / (c1 - c2), 0); + 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); + } + setPixelColor(SEGMENT.start + int(sparks[i].pos), c.red, c.green, c.blue); } } - dying_gravity *= .995; // as sparks burn out they fall slower + dying_gravity *= .99; // as sparks burn out they fall slower } else { - SEGENV.aux0 = 0; //init again - SEGENV.aux1 = 0; //back to flare + SEGENV.aux0 = 6 + random8(10); //wait for this many frames + } + } else { + SEGENV.aux0--; + if (SEGENV.aux0 < 4) { + SEGENV.aux0 = 0; //back to flare + SEGENV.step = (SEGMENT.intensity > random8()); //decide firing side } } + + SEGMENT.setOption(1, actuallyReverse); + return FRAMETIME; } diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 5fc1092f1..52951f616 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001022 +#define VERSION 2001031 char versionString[] = "0.9.0-b2"; From e4ad0d3b59687c9cd879c4f0967b57d901d08d14 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 5 Jan 2020 21:26:14 +0100 Subject: [PATCH 12/15] Improved bouncing balls --- wled00/FX.cpp | 199 +++++++++++++++++++++++----------------------- wled00/FX.h | 4 +- wled00/wled00.ino | 2 +- 3 files changed, 101 insertions(+), 104 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 4549a0ed0..72d540a92 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2401,107 +2401,94 @@ uint16_t WS2812FX::mode_spots_fade() } +//each needs 12 bytes +//Spark type is used for popcorn and 1D fireworks +typedef struct Ball { + unsigned long lastBounceTime; + float impactVelocity; + float height; +} ball; + /* * Bouncing Balls Effect */ -uint16_t WS2812FX::mode_BouncingBalls(void) { +uint16_t WS2812FX::mode_bouncing_balls(void) { + //allocate segment data + uint16_t maxNumBalls = 16; + uint16_t dataSize = sizeof(ball) * maxNumBalls; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + Ball* balls = reinterpret_cast(SEGENV.data); + // number of balls based on intensity setting to max of 7 (cycles colors) // non-chosen color is a random color - int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); - - const int maxBallCount = 7; + uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); - float Gravity = -9.81; // standard value of gravity - int Position[maxBallCount]; // current position of the ball normalized to segment size - float TimeSinceLastBounce[maxBallCount] = {0}; // time difference for physics calculations - int StartHeight = 1; // height in metres (strip length) - float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) - float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight); + float gravity = -9.81; // standard value of gravity + float impactVelocityStart = sqrt( -2 * gravity); - static float ImpactVelocity[maxBallCount] = {ImpactVelocityStart}; - static long ClockTimeSinceLastBounce[maxBallCount] = {millis()}; - static float Height[maxBallCount]; - - //set up variable damping for better effect using multiple balls - for (int i = 0 ; i < maxBallCount ; i++) { - Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); + unsigned long time = millis(); + + if (SEGENV.call == 0) { + for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; } - for (int i = 0 ; i < balls ; i++) { - TimeSinceLastBounce[i] = (millis() - ClockTimeSinceLastBounce[i])/((255-SEGMENT.speed)*8/256 +1); - Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000; + 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 ( Height[i] < 0 ) { - Height[i] = 0; - ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i]; - ClockTimeSinceLastBounce[i] = millis(); + 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 ( ImpactVelocity[i] < 0.01 ) { - ImpactVelocity[i] = ImpactVelocityStart; + if (balls[i].impactVelocity < 0.015) { + balls[i].impactVelocity = impactVelocityStart; } } - Position[i] = round( Height[i] * (SEGLEN - 1) / StartHeight); - } - - fill(BLACK); - - for (int i = 0 ; i < balls ; i++) { - uint32_t color = SEGCOLOR(i % NUM_COLORS); - if (!color) { - color = color_wheel(random8()); - } - setPixelColor(SEGMENT.start+Position[i],color); + uint32_t color = SEGCOLOR(0); + if (SEGMENT.palette) { + color = color_wheel(i*(256/max(numBalls, 8))); + } else if (hasCol2) { + color = SEGCOLOR(i % NUM_COLORS); + } + + uint16_t pos = round(balls[i].height * (SEGLEN - 1)); + setPixelColor(SEGMENT.start + pos, color); } return FRAMETIME; } + /* * Sinelon stolen from FASTLED examples */ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { - fade_out(SEGMENT.intensity); int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); - int prevpos = SEGENV.aux0; uint32_t color1 = color_from_palette(pos, true, false, 0); if (rainbow) { - color1 = color_wheel((pos % 8) * 32); - } - - if( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) { - setPixelColor(i, color1); - } - } else { - for (uint16_t i = prevpos; i < pos; i++) { - setPixelColor(SEGMENT.start + i, color1); - } + color1 = color_wheel((pos & 0x07) * 32); } + setPixelColor(SEGMENT.start + pos, color1); if (dual) { uint32_t color2 = SEGCOLOR(2); - if (color2 == 0) { - color2 = color_from_palette(pos, true, false, 0); - } - if (rainbow) { - color2 = color_wheel((pos % 8) * 32); - } - if ( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) { - setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); - } - } else { - for (uint16_t i = prevpos; i < pos; i++) { - setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); - } - } + if (!color2) color2 = color_from_palette(pos, true, false, 0); + if (rainbow) color2 = color1; //rainbow + + setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2); } - SEGENV.aux0 = pos; return FRAMETIME; } @@ -2532,46 +2519,64 @@ uint16_t WS2812FX::mode_glitter() } + +//each needs 12 bytes +//Spark type is used for popcorn and 1D fireworks +typedef struct Spark { + float pos; + float vel; + uint16_t col; + uint8_t colIndex; +} spark; + /* * POPCORN +* modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h */ -typedef struct Kernel { - float position; - float velocity; - int32_t color; -} kernel; - -#define MAX_NUM_POPCORN 12 -#define GRAVITY 0.06 - uint16_t WS2812FX::mode_popcorn(void) { - uint32_t popcornColor = SEGCOLOR(0); - uint32_t bgColor = SEGCOLOR(1); - if(popcornColor == bgColor) popcornColor = color_wheel(random8()); + //allocate segment data + uint16_t maxNumPopcorn = 24; + uint16_t dataSize = sizeof(spark) * maxNumPopcorn; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + Spark* popcorn = reinterpret_cast(SEGENV.data); - static kernel popcorn[MAX_NUM_POPCORN]; - float coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; + float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s + gravity *= SEGLEN; - fill(SEGCOLOR(1)); + bool hasCol2 = SEGCOLOR(2); + fill(hasCol2 ? BLACK : SEGCOLOR(1)); - uint16_t ledIndex; - for(int8_t i=0; i < SEGMENT.intensity*MAX_NUM_POPCORN/255; i++) { - bool isActive = popcorn[i].position >= 0.0f; + uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; + if (numPopcorn == 0) numPopcorn = 1; + + for(uint8_t i = 0; i < numPopcorn; i++) { + bool isActive = popcorn[i].pos >= 0.0f; if(isActive) { // if kernel is active, update its position - popcorn[i].position += popcorn[i].velocity; - popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1)); + 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); - ledIndex = SEGMENT.start + popcorn[i].position; - if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); + uint16_t ledIndex = SEGMENT.start + popcorn[i].pos; + if(ledIndex >= SEGMENT.start && ledIndex < SEGMENT.stop) setPixelColor(ledIndex, col); } else { // if kernel is inactive, randomly pop it if(random8() < 2) { // POP!!! - popcorn[i].position = 0.0f; - popcorn[i].velocity = coeff * (random(66, 100) / 100.0f); - popcorn[i].color = popcornColor; + popcorn[i].pos = 0.01f; - ledIndex = SEGMENT.start; - setPixelColor(ledIndex, popcorn[i].color); + 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; + } } } } @@ -2756,14 +2761,6 @@ uint16_t WS2812FX::mode_starburst(void) { * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ */ -//each needs 12 byte -typedef struct Spark { - float pos; - float vel; - uint16_t col; - uint8_t colIndex; -} spark; - uint16_t WS2812FX::mode_exploding_fireworks(void) { //allocate segment data diff --git a/wled00/FX.h b/wled00/FX.h index fbad203f9..fdbbfb554 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -360,7 +360,7 @@ class WS2812FX { _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; _mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst; _mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; - _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_BouncingBalls; + _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls; _mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon; _mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual; _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; @@ -543,7 +543,7 @@ class WS2812FX { mode_candle(void), mode_starburst(void), mode_exploding_fireworks(void), - mode_BouncingBalls(void), + mode_bouncing_balls(void), mode_sinelon(void), mode_sinelon_dual(void), mode_sinelon_rainbow(void), diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 52951f616..c706f51cd 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001031 +#define VERSION 2001051 char versionString[] = "0.9.0-b2"; From ef4ec16860654e7d62e7bca89d2c9436118fb12e Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 6 Jan 2020 00:43:15 +0100 Subject: [PATCH 13/15] Add RSSI to info (#434) --- wled00/wled00.ino | 2 +- wled00/wled19_json.ino | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index c706f51cd..b9e36a83f 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001051 +#define VERSION 2001052 char versionString[] = "0.9.0-b2"; diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 802941a62..23de1bf14 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -257,6 +257,8 @@ void serializeInfo(JsonObject root) JsonObject wifi_info = root.createNestedObject("wifi"); wifi_info["bssid"] = WiFi.BSSIDstr(); + int qrssi = WiFi.RSSI(); + wifi_info["rssi"] = qrssi; wifi_info["signal"] = getSignalQuality(WiFi.RSSI()); wifi_info["channel"] = WiFi.channel(); From 318ffc821c673f0c466cc901c02209ef5a29aaa7 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 6 Jan 2020 01:06:02 +0100 Subject: [PATCH 14/15] Re-add accidentally reverted Frametime fixes --- wled00/FX.cpp | 29 ++++++++++++++++++++++------- wled00/wled19_json.ino | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 72d540a92..2c6bc116a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -962,6 +962,10 @@ uint16_t WS2812FX::mode_halloween(void) { * Random colored pixels running. */ uint16_t WS2812FX::mode_running_random(void) { + uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); + uint32_t it = now / cycleTime; + if (SEGENV.aux1 == it) return FRAMETIME; + for(uint16_t i=SEGLEN-1; i > 0; i--) { setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1)); } @@ -976,7 +980,9 @@ uint16_t WS2812FX::mode_running_random(void) { { SEGENV.step = 0; } - return SPEED_FORMULA_L; + + SEGENV.aux1 = it; + return FRAMETIME; } @@ -1092,6 +1098,10 @@ uint16_t WS2812FX::mode_rain() * Fire flicker function */ uint16_t WS2812FX::mode_fire_flicker(void) { + uint32_t cycleTime = 40 + (255 - SEGMENT.speed); + uint32_t it = now / cycleTime; + if (SEGENV.step == it) return FRAMETIME; + byte w = (SEGCOLOR(0) >> 24) & 0xFF; byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte g = (SEGCOLOR(0) >> 8) & 0xFF; @@ -1106,7 +1116,9 @@ uint16_t WS2812FX::mode_fire_flicker(void) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } } - return 20 + random((255 - SEGMENT.speed),(2 * (uint16_t)(255 - SEGMENT.speed))); + + SEGENV.step = it; + return FRAMETIME; } @@ -1370,10 +1382,7 @@ uint16_t WS2812FX::mode_tricolor_fade(void) setPixelColor(i, color); } - SEGENV.step += 4; - if(SEGENV.step >= 768) SEGENV.step = 0; - - return 5 + ((uint32_t)(255 - SEGMENT.speed) / 10); + return FRAMETIME; } @@ -1383,6 +1392,10 @@ uint16_t WS2812FX::mode_tricolor_fade(void) */ uint16_t WS2812FX::mode_multi_comet(void) { + uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); + uint32_t it = now / cycleTime; + if (SEGENV.step == it) return FRAMETIME; + fade_out(SEGMENT.intensity); static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; @@ -1404,7 +1417,9 @@ uint16_t WS2812FX::mode_multi_comet(void) } } } - return SPEED_FORMULA_L; + + SEGENV.step = it; + return FRAMETIME; } diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 23de1bf14..12339db43 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -259,7 +259,7 @@ void serializeInfo(JsonObject root) wifi_info["bssid"] = WiFi.BSSIDstr(); int qrssi = WiFi.RSSI(); wifi_info["rssi"] = qrssi; - wifi_info["signal"] = getSignalQuality(WiFi.RSSI()); + wifi_info["signal"] = getSignalQuality(qrssi); wifi_info["channel"] = WiFi.channel(); #ifdef ARDUINO_ARCH_ESP32 From 558811e4f9fd0a546e28275a6982d6768ae50d74 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 7 Jan 2020 01:02:52 +0100 Subject: [PATCH 15/15] Update to Espalexa v2.4.4 --- wled00/src/dependencies/espalexa/Espalexa.h | 14 +++--- .../dependencies/espalexa/EspalexaDevice.cpp | 50 ++++++++++--------- .../dependencies/espalexa/EspalexaDevice.h | 1 + wled00/wled00.ino | 2 +- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/wled00/src/dependencies/espalexa/Espalexa.h b/wled00/src/dependencies/espalexa/Espalexa.h index 0c817dcf8..49c9b6a9a 100644 --- a/wled00/src/dependencies/espalexa/Espalexa.h +++ b/wled00/src/dependencies/espalexa/Espalexa.h @@ -10,7 +10,7 @@ */ /* * @title Espalexa library - * @version 2.4.3 + * @version 2.4.4 * @author Christian Schwinne * @license MIT * @contributors d-999 @@ -49,7 +49,7 @@ #include #ifdef ESPALEXA_DEBUG - #pragma message "Espalexa 2.4.3 debug mode" + #pragma message "Espalexa 2.4.4 debug mode" #define EA_DEBUG(x) Serial.print (x) #define EA_DEBUGLN(x) Serial.println (x) #else @@ -164,7 +164,7 @@ private: json += "\",\"modelid\":\"" + modelidString(dev->getType()); json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast(dev->getType())); json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1)); - json += "\",\"swversion\":\"espalexa-2.4.3\"}"; + json += "\",\"swversion\":\"espalexa-2.4.4\"}"; return json; } @@ -188,7 +188,7 @@ private: } res += "\r\nFree Heap: " + (String)ESP.getFreeHeap(); res += "\r\nUptime: " + (String)millis(); - res += "\r\n\r\nEspalexa library v2.4.3 by Christian Schwinne 2019"; + res += "\r\n\r\nEspalexa library v2.4.4 by Christian Schwinne 2020"; server->send(200, "text/plain", res); } #endif @@ -298,7 +298,7 @@ private: "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber "hue-bridgeid: "+ escapedMac +"\r\n" "ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType - "USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::upnp:rootdevice\r\n" // _uuid::_deviceType + "USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::ssdp:all\r\n" // _uuid::_deviceType "\r\n"; espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort()); @@ -372,7 +372,7 @@ public: String request = packetBuffer; if(request.indexOf("M-SEARCH") >= 0) { EA_DEBUGLN(request); - if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0) { + if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0 || request.indexOf("ssdp:all") > 0) { EA_DEBUGLN("Responding search req..."); respondToSearch(); } @@ -584,4 +584,4 @@ public: ~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed }; -#endif \ No newline at end of file +#endif diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.cpp b/wled00/src/dependencies/espalexa/EspalexaDevice.cpp index df859c1d9..25d70bb0b 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.cpp +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.cpp @@ -130,30 +130,28 @@ uint32_t EspalexaDevice::getRGB() case 350: rgb[0]=130,rgb[1]=90,rgb[2]=0;rgb[3]=255;break; case 383: rgb[0]=255,rgb[1]=153,rgb[2]=0;rgb[3]=255;break; default: { - - if( temp <= 66 ){ - r = 255; - g = temp; - g = 99.470802 * log(g) - 161.119568; - if( temp <= 19){ - b = 0; - } else { - b = temp-10; - b = 138.517731 * log(b) - 305.044793; - } - } else { - r = temp - 60; - r = 329.698727 * pow(r, -0.13320476); - g = temp - 60; - g = 288.12217 * pow(g, -0.07551485 ); - b = 255; - } + if( temp <= 66 ){ + r = 255; + g = temp; + g = 99.470802 * log(g) - 161.119568; + if( temp <= 19){ + b = 0; + } else { + b = temp-10; + b = 138.517731 * log(b) - 305.044793; + } + } else { + r = temp - 60; + r = 329.698727 * pow(r, -0.13320476); + g = temp - 60; + g = 288.12217 * pow(g, -0.07551485 ); + b = 255; + } - rgb[0] = (byte)constrain(r,0.1,255.1); - rgb[1] = (byte)constrain(g,0.1,255.1); - rgb[2] = (byte)constrain(b,0.1,255.1); - - } + rgb[0] = (byte)constrain(r,0.1,255.1); + rgb[1] = (byte)constrain(g,0.1,255.1); + rgb[2] = (byte)constrain(b,0.1,255.1); + } } } else if (_mode == EspalexaColorMode::hs) { @@ -232,6 +230,12 @@ uint32_t EspalexaDevice::getRGB() return _rgb; } +//white channel for RGBW lights. Always 0 unless colormode is ct +uint8_t EspalexaDevice::getW() +{ + return (getRGB() >> 24) & 0xFF; +} + uint8_t EspalexaDevice::getR() { return (getRGB() >> 16) & 0xFF; diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.h b/wled00/src/dependencies/espalexa/EspalexaDevice.h index 8d7be8736..a66449165 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.h +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.h @@ -51,6 +51,7 @@ public: uint8_t getR(); uint8_t getG(); uint8_t getB(); + uint8_t getW(); EspalexaColorMode getColorMode(); EspalexaDeviceType getType(); diff --git a/wled00/wled00.ino b/wled00/wled00.ino index b9e36a83f..71356f6e7 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001052 +#define VERSION 2001071 char versionString[] = "0.9.0-b2";