diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dac4aade..2c6bc116 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 } } } @@ -2376,6 +2416,110 @@ 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_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 + uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); + + float gravity = -9.81; // standard value of gravity + float impactVelocityStart = sqrt( -2 * gravity); + + unsigned long time = millis(); + + if (SEGENV.call == 0) { + for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; + } + + bool hasCol2 = SEGCOLOR(2); + fill(hasCol2 ? BLACK : SEGCOLOR(1)); + + for (uint8_t i = 0; i < numBalls; i++) { + float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)*8/256 +1); + balls[i].height = 0.5 * gravity * pow(timeSinceLastBounce/1000 , 2.0) + balls[i].impactVelocity * timeSinceLastBounce/1000; + + if (balls[i].height < 0) { //start bounce + balls[i].height = 0; + //damping for better effect using multiple balls + float dampening = 0.90 - float(i)/pow(numBalls,2); + balls[i].impactVelocity = dampening * balls[i].impactVelocity; + balls[i].lastBounceTime = time; + + if (balls[i].impactVelocity < 0.015) { + balls[i].impactVelocity = impactVelocityStart; + } + } + + 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); + + uint32_t color1 = color_from_palette(pos, true, false, 0); + if (rainbow) { + color1 = color_wheel((pos & 0x07) * 32); + } + setPixelColor(SEGMENT.start + pos, color1); + + if (dual) { + uint32_t color2 = SEGCOLOR(2); + + if (!color2) color2 = color_from_palette(pos, true, false, 0); + if (rainbow) color2 = color1; //rainbow + + setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2); + } + + 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() { @@ -2390,6 +2534,72 @@ 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 +*/ +uint16_t WS2812FX::mode_popcorn(void) { + //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); + + float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s + gravity *= SEGLEN; + + bool hasCol2 = SEGCOLOR(2); + fill(hasCol2 ? BLACK : SEGCOLOR(1)); + + 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].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); + + 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].pos = 0.01f; + + 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; + } + } + } + } + + return FRAMETIME; +} + + //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/ @@ -2446,6 +2656,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 +2667,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 @@ -2552,3 +2769,117 @@ uint16_t WS2812FX::mode_starburst(void) { } return FRAMETIME; } + + +/* + * Exploding fireworks effect + * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ + */ + +uint16_t WS2812FX::mode_exploding_fireworks(void) +{ + //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); + + 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.0004 - (SEGMENT.speed/800000.0); // m/s/s + gravity *= SEGLEN; + + if (SEGENV.aux0 < 2) { //FLARE + if (SEGENV.aux0 == 0) { //init flare + flare->pos = 0; + 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 > 12 * gravity) { + // flare + 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 -= 2; + } else { + SEGENV.aux0 = 2; // ready to explode + } + } else if (SEGENV.aux0 < 4) { + /* + * Explode! + * + * Explosion happens where the flare ended. + * Size is proportional to the height. + */ + int nSparks = flare->pos; + nSparks = constrain(nSparks, 0, numSparks); + static float dying_gravity; + + // initialize sparks + 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) - 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 = 345; // this will be our known spark + dying_gravity = gravity/2; + SEGENV.aux0 = 3; + } + + 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].vel += dying_gravity; + 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 *= .99; // as sparks burn out they fall slower + } else { + 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/FX.h b/wled00/FX.h index bb33741d..fdbbfb55 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -91,7 +91,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 90 +#define MODE_COUNT 96 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -183,10 +183,19 @@ #define FX_MODE_GLITTER 87 #define FX_MODE_CANDLE 88 #define FX_MODE_STARBURST 89 +#define FX_MODE_EXPLODING_FIREWORKS 90 +#define FX_MODE_BOUNCINGBALLS 91 +#define FX_MODE_SINELON 92 +#define FX_MODE_SINELON_DUAL 93 +#define FX_MODE_SINELON_RAINBOW 94 +#define FX_MODE_POPCORN 95 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); + + // pre show callback + typedef void (*show_callback) (void); // segment parameters public: @@ -248,9 +257,7 @@ class WS2812FX { return true; } void deallocateData(){ - if (data) { - delete[] data; - } + delete[] data; data = nullptr; WS2812FX::_usedSegmentData -= _dataLen; _dataLen = 0; @@ -352,6 +359,12 @@ class WS2812FX { _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; _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_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; + _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -359,8 +372,6 @@ class WS2812FX { ablMilliampsMax = 850; currentMilliamps = 0; timebase = 0; - _locked = nullptr; - _modeUsesLock = false; bus = new NeoPixelWrapper(); resetSegments(); } @@ -377,13 +388,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), @@ -535,7 +541,13 @@ class WS2812FX { mode_spots_fade(void), mode_glitter(void), mode_candle(void), - mode_starburst(void); + mode_starburst(void), + mode_exploding_fireworks(void), + mode_bouncing_balls(void), + mode_sinelon(void), + mode_sinelon_dual(void), + mode_sinelon_rainbow(void), + mode_popcorn(void); private: @@ -554,21 +566,20 @@ class WS2812FX { void handle_palette(void); void fill(uint32_t); - bool modeUsesLock(uint8_t); bool - _modeUsesLock, _rgbwMode, _cronixieMode, _cronixieBacklightEnabled, _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), @@ -577,6 +588,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), @@ -612,7 +624,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","Fireworks Starburst" +"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", +"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn" ])====="; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7fa6eae8..3081c2be 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); } @@ -96,14 +92,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 +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] && !_modeUsesLock) return; if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment byte tmpg = g; switch (colorOrder) //0 = Grb, default @@ -208,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 @@ -273,7 +262,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 +269,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 +435,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 { @@ -489,61 +465,20 @@ void WS2812FX::resetSegments() { _segment_runtimes[0].reset(); } -void WS2812FX::setIndividual(uint16_t i, uint32_t col) -{ - if (modeUsesLock(SEGMENT.mode)) return; - 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 (_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; - } -} - -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; - } -} - -void WS2812FX::unlockAll() -{ - for (int i=0; i < _length; i++) _locked[i] = false; + _callback = cb; } void WS2812FX::setTransitionMode(bool t) diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 678aa281..a15e62eb 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/src/dependencies/espalexa/Espalexa.h b/wled00/src/dependencies/espalexa/Espalexa.h index 0c817dcf..49c9b6a9 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 df859c1d..25d70bb0 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 8d7be873..a6644916 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 c77eae7a..71356f6e 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 2001071 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 9c753fec..b20103ce 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 684893f1..a347eb4d 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 22370072..8bb739e0 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 fc3a47d8..cfe8a1f9 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 4c5a7496..de89aede 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; diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 802941a6..12339db4 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -257,7 +257,9 @@ void serializeInfo(JsonObject root) JsonObject wifi_info = root.createNestedObject("wifi"); wifi_info["bssid"] = WiFi.BSSIDstr(); - wifi_info["signal"] = getSignalQuality(WiFi.RSSI()); + int qrssi = WiFi.RSSI(); + wifi_info["rssi"] = qrssi; + wifi_info["signal"] = getSignalQuality(qrssi); wifi_info["channel"] = WiFi.channel(); #ifdef ARDUINO_ARCH_ESP32