diff --git a/platformio.ini b/platformio.ini index c4cac76d1..45f2148fe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,6 +41,8 @@ lib_deps_external = IRremoteESP8266@2.5.5 #Time@1.5 #Timezone@1.2.1 + #For use SSD1306 0.91" OLED display uncomment following + #U8g2@~2.27.2 [common:esp8266] # ------------------------------------------------------------------------------ @@ -214,4 +216,4 @@ build_flags = -D WLED_ENABLE_5CH_LEDS lib_deps = ${common.lib_deps_external} - \ No newline at end of file + diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt index 62401f8ee..612873360 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt @@ -4,5 +4,5 @@ This code uses Aircookie's WLED software. It has a premade file for user modific To install: -Add the enties in the WLED00 file to the top of the same file from Aircoookies WLED. +Add the entries in the WLED00 file to the top of the same file from Aircoookies WLED. Replace the WLED06_usermod.ino file in Aircoookies WLED folder. diff --git a/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino b/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino index c73170645..593625b91 100644 --- a/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino +++ b/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino @@ -1,7 +1,7 @@ #include // from https://github.com/olikraus/u8g2/ //The SCL and SDA pins are defined here. -//Lolin32 boards use SCL=4 SDA=5 +//Lolin32 boards use SCL=5 SDA=4 #define U8X8_PIN_SCL 5 #define U8X8_PIN_SDA 4 diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 79837df0d..4549a0ed0 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) //new color + 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; } @@ -414,7 +416,6 @@ uint16_t WS2812FX::mode_theater_chase(void) { * Inspired by the Adafruit examples. */ uint16_t WS2812FX::mode_theater_chase_rainbow(void) { - SEGENV.step = (SEGENV.step + 1) & 0xFF; return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false); } @@ -1294,20 +1295,38 @@ uint16_t WS2812FX::mode_icu(void) { */ uint16_t WS2812FX::mode_tricolor_wipe(void) { - if(SEGENV.step < SEGLEN) { - uint32_t led_offset = SEGENV.step; - setPixelColor(SEGMENT.start + led_offset, SEGCOLOR(0)); - } else if (SEGENV.step < SEGLEN*2) { - uint32_t led_offset = SEGENV.step - SEGLEN; - setPixelColor(SEGMENT.start + led_offset, SEGCOLOR(1)); - } else + uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200; + uint32_t perc = now % cycleTime; + uint16_t prog = (perc * 65535) / cycleTime; + uint16_t ledIndex = (prog * SEGLEN * 3) >> 16; + uint16_t ledOffset = ledIndex; + + for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { - uint32_t led_offset = SEGENV.step - SEGLEN*2; - setPixelColor(SEGMENT.start + led_offset, color_from_palette(SEGMENT.start + led_offset, true, PALETTE_SOLID_WRAP, 2)); + setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); + } + + if(ledIndex < SEGLEN) { //wipe from 0 to 1 + for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) + { + setPixelColor(i, (i - SEGMENT.start > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1)); + } + } else if (ledIndex < SEGLEN*2) { //wipe from 1 to 2 + ledOffset = ledIndex - SEGLEN; + for (uint16_t i = SEGMENT.start +ledOffset +1; i < SEGMENT.stop; i++) + { + setPixelColor(i, SEGCOLOR(1)); + } + } else //wipe from 2 to 0 + { + ledOffset = ledIndex - SEGLEN*2; + for (uint16_t i = SEGMENT.start; i <= SEGMENT.start +ledOffset; i++) + { + setPixelColor(i, SEGCOLOR(0)); + } } - SEGENV.step = (SEGENV.step + 1) % (SEGLEN * 3); - return SPEED_FORMULA_L; + return FRAMETIME; } @@ -1318,14 +1337,17 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) */ uint16_t WS2812FX::mode_tricolor_fade(void) { + uint16_t counter = now * ((SEGMENT.speed >> 3) +1); + uint32_t prog = (counter * 768) >> 16; + uint32_t color1 = 0, color2 = 0; byte stage = 0; - if(SEGENV.step < 256) { + if(prog < 256) { color1 = SEGCOLOR(0); color2 = SEGCOLOR(1); stage = 0; - } else if(SEGENV.step < 512) { + } else if(prog < 512) { color1 = SEGCOLOR(1); color2 = SEGCOLOR(2); stage = 1; @@ -1335,7 +1357,7 @@ uint16_t WS2812FX::mode_tricolor_fade(void) stage = 2; } - byte stp = SEGENV.step % 256; + byte stp = prog; // % 256 uint32_t color = 0; for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { if (stage == 2) { @@ -1432,17 +1454,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); @@ -1459,9 +1488,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); } @@ -1631,30 +1660,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; } @@ -1835,18 +1868,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); @@ -1863,13 +1903,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 } @@ -1903,29 +1945,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; @@ -1936,29 +1982,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; @@ -2000,23 +2050,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; @@ -2026,7 +2088,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)); } @@ -2037,16 +2099,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 } } } @@ -2567,3 +2627,247 @@ uint16_t WS2812FX::mode_candle() return FRAMETIME; } + + +/* +/ Fireworks in starburst effect +/ based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/ +/ Speed sets frequency of new starbursts, intensity is the intensity of the burst +*/ +#define STARBURST_MAX_FRAG 12 + +//each needs 64 byte +typedef struct particle { + CRGB color; + uint32_t birth =0; + uint32_t last =0; + float vel =0; + uint16_t pos =-1; + float fragment[STARBURST_MAX_FRAG]; +} 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(); + + 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 + + for (int j = 0; j < numStars; j++) + { + // speed to adjust chance of a burst, max is nearly always. + if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) + { + // Pick a random color and location. + uint16_t startPos = random16(SEGLEN-1); + float multiplier = (float)(random8())/255.0 * 1.0; + + stars[j].color = col_to_crgb(color_wheel(random8())); + stars[j].pos = startPos; + stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier; + stars[j].birth = it; + stars[j].last = it; + // more fragments means larger burst effect + int num = random8(3,6 + (SEGMENT.intensity >> 5)); + + for (int i=0; i < STARBURST_MAX_FRAG; i++) { + if (i < num) stars[j].fragment[i] = startPos; + else stars[j].fragment[i] = -1; + } + } + } + + fill(SEGCOLOR(1)); + + for (int j=0; j> 1; + + if (stars[j].fragment[i] > 0) { + //all fragments travel right, will be mirrored on other side + stars[j].fragment[i] += stars[j].vel * dt * (float)var/3.0; + } + } + stars[j].last = it; + stars[j].vel -= 3*stars[j].vel*dt; + } + + CRGB c = stars[j].color; + + // If the star is brand new, it flashes white briefly. + // Otherwise it just fades over time. + float fade = 0.0f; + float age = it-stars[j].birth; + + if (age < particleIgnition) { + c = col_to_crgb(color_blend(WHITE, crgb_to_col(c), 254.5f*((age / particleIgnition)))); + } else { + // Figure out how much to fade and shrink the star based on + // its age relative to its lifetime + if (age > particleIgnition + particleFadeTime) { + fade = 1.0f; // Black hole, all faded out + stars[j].birth = 0; + c = col_to_crgb(SEGCOLOR(1)); + } else { + age -= particleIgnition; + fade = (age / particleFadeTime); // Fading star + byte f = 254.5f*fade; + c = col_to_crgb(color_blend(crgb_to_col(c), SEGCOLOR(1), f)); + } + } + + float particleSize = (1.0 - fade) * 2; + + for (uint8_t index=0; index < STARBURST_MAX_FRAG*2; index++) { + bool mirrored = index & 0x1; + uint8_t i = index >> 1; + if (stars[j].fragment[i] > 0) { + float loc = stars[j].fragment[i]; + if (mirrored) loc -= (loc-stars[j].pos)*2; + int start = loc - particleSize; + int end = loc + particleSize; + if (start < 0) start = 0; + if (start == end) end++; + if (end > SEGLEN) end = SEGLEN; + for (int p = start; p < end; p++) { + setPixelColor(SEGMENT.start+p, c.r, c.g, c.b); + } + } + } + } + return FRAMETIME; +} + + +/* + * Exploding fireworks effect + * 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 + 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 29a667939..fbad203f9 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -44,10 +44,17 @@ #define WLED_FPS 42 #define FRAMETIME (1000/WLED_FPS) -/* each segment uses 37 bytes of SRAM memory, so if you're application fails because of +/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ #define MAX_NUM_SEGMENTS 10 +/* How much data bytes all segments combined may allocate */ +#ifdef ESP8266 +#define MAX_SEGMENT_DATA 2048 +#else +#define MAX_SEGMENT_DATA 8192 +#endif + #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT _segments[_segment_index] #define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x]) @@ -84,7 +91,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 94 +#define MODE_COUNT 96 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -175,15 +182,20 @@ #define FX_MODE_SPOTS_FADE 86 #define FX_MODE_GLITTER 87 #define FX_MODE_CANDLE 88 -#define FX_MODE_BOUNCINGBALLS 89 -#define FX_MODE_SINELON 90 -#define FX_MODE_SINELON_DUAL 91 -#define FX_MODE_SINELON_RAINBOW 92 -#define FX_MODE_POPCORN 93 +#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: @@ -226,13 +238,33 @@ class WS2812FX { } segment; // segment runtime parameters - typedef struct Segment_runtime { // 16 bytes + typedef struct Segment_runtime { // 28 bytes unsigned long next_time; uint32_t step; uint32_t call; uint16_t aux0; uint16_t aux1; - void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;}; + byte* data = nullptr; + bool allocateData(uint16_t len){ + if (data && _dataLen == len) return true; //already allocated + deallocateData(); + if (WS2812FX::_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory + data = new (std::nothrow) byte[len]; + if (!data) return false; //allocation failed + WS2812FX::_usedSegmentData += len; + _dataLen = len; + memset(data, 0, len); + return true; + } + void deallocateData(){ + delete[] data; + data = nullptr; + WS2812FX::_usedSegmentData -= _dataLen; + _dataLen = 0; + } + void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; deallocateData();} + private: + uint16_t _dataLen = 0; } segment_runtime; WS2812FX() { @@ -324,13 +356,15 @@ class WS2812FX { _mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern; _mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots; _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_STARBURST] = &WS2812FX::mode_starburst; + _mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; _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; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -338,8 +372,6 @@ class WS2812FX { ablMilliampsMax = 850; currentMilliamps = 0; timebase = 0; - _locked = nullptr; - _modeUsesLock = false; bus = new NeoPixelWrapper(); resetSegments(); } @@ -356,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), @@ -512,13 +539,15 @@ class WS2812FX { mode_tri_static_pattern(void), mode_spots(void), mode_spots_fade(void), + mode_glitter(void), + mode_candle(void), + mode_starburst(void), + mode_exploding_fireworks(void), mode_BouncingBalls(void), mode_sinelon(void), mode_sinelon_dual(void), mode_sinelon_rainbow(void), - mode_popcorn(void), - mode_glitter(void), - mode_candle(void); + mode_popcorn(void); private: @@ -533,24 +562,24 @@ class WS2812FX { uint16_t _length, _lengthRaw, _usableCount; uint16_t _rand16seed; uint8_t _brightness; + static uint16_t _usedSegmentData; 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), @@ -580,7 +609,8 @@ class WS2812FX { // start, stop, speed, intensity, palette, mode, options, 3 unused bytes (group, spacing, opacity), color[] { 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}} }; - segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element + segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element + friend class Segment_runtime; }; @@ -592,10 +622,10 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream", "Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All", "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", +"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","Sinelon Dual","Sinelon Rainbow","Popcorn" +"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 2b7d6ec1d..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); } @@ -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 { @@ -473,7 +449,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) { void WS2812FX::resetSegments() { memset(_segments, 0, sizeof(_segments)); - memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); + //memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); _segment_index = 0; _segments[0].mode = DEFAULT_MODE; _segments[0].colors[0] = DEFAULT_COLOR; @@ -484,64 +460,25 @@ void WS2812FX::resetSegments() { for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) { _segments[i].colors[0] = color_wheel(i*51); + _segment_runtimes[i].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; - } + _segment_runtimes[0].reset(); } 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) @@ -862,3 +799,5 @@ uint32_t WS2812FX::gamma32(uint32_t color) b = gammaT[b]; return ((w << 24) | (r << 16) | (g << 8) | (b)); } + +uint16_t WS2812FX::_usedSegmentData = 0; diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index a704e4269..05d738680 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -130,26 +130,25 @@ public: _pGrbw = new NeoPixelBrightnessBus(countPixels, LEDPIN); #endif _pGrbw->Begin(); - break; - - #ifdef WLED_USE_ANALOG_LEDS - //init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller - pinMode(RPIN, OUTPUT); - pinMode(GPIN, OUTPUT); - pinMode(BPIN, OUTPUT); - switch (_type) { - case NeoPixelType_Grb: break; - #ifdef WLED_USE_5CH_LEDS - case NeoPixelType_Grbw: pinMode(WPIN, OUTPUT); pinMode(W2PIN, OUTPUT); break; - #else - case NeoPixelType_Grbw: pinMode(WPIN, OUTPUT); break; - #endif - } - analogWriteRange(255); //same range as one RGB channel - analogWriteFreq(880); //PWM frequency proven as good for LEDs + + #ifdef WLED_USE_ANALOG_LEDS + pinMode(WPIN, OUTPUT); + #ifdef WLED_USE_5CH_LEDS + pinMode(W2PIN, OUTPUT); #endif - + #endif + + break; } + + #ifdef WLED_USE_ANALOG_LEDS + //init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller + pinMode(RPIN, OUTPUT); + pinMode(GPIN, OUTPUT); + pinMode(BPIN, OUTPUT); + analogWriteRange(255); //same range as one RGB channel + analogWriteFreq(880); //PWM frequency proven as good for LEDs + #endif } #ifdef WLED_USE_ANALOG_LEDS 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 a577c95d4..52951f616 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1912232 +#define VERSION 2001031 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 3f08d2807..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 @@ -622,6 +598,10 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) //cronixie #ifndef WLED_DISABLE_CRONIXIE + //mode, 1 countdown + pos = req.indexOf("NM="); + if (pos > 0) countdownMode = (req.charAt(pos+3) != '0'); + pos = req.indexOf("NX="); //sets digits to code if (pos > 0) { strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 6); @@ -636,9 +616,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) overlayRefreshedTime = 0; } #endif - //mode, 1 countdown - pos = req.indexOf("NM="); - if (pos > 0) countdownMode = (req.charAt(pos+3) != '0'); pos = req.indexOf("U0="); //user var 0 if (pos > 0) { 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/wled10_ntp.ino b/wled00/wled10_ntp.ino index 57a810f14..bf8df064d 100644 --- a/wled00/wled10_ntp.ino +++ b/wled00/wled10_ntp.ino @@ -163,13 +163,16 @@ void setCountdown() //returns true if countdown just over bool checkCountdown() { - long diff = countdownTime - now(); - local = abs(diff); - if (diff <0 && !countdownOverTriggered) - { - if (macroCountdown != 0) applyMacro(macroCountdown); - countdownOverTriggered = true; - return true; + unsigned long n = now(); + local = countdownTime - n; + if (n > countdownTime) { + local = n - countdownTime; + if (!countdownOverTriggered) + { + if (macroCountdown != 0) applyMacro(macroCountdown); + countdownOverTriggered = true; + return true; + } } return false; } 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 dac324751..de89aede2 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/wled13_cronixie.ino @@ -145,9 +145,7 @@ void setCronixie() void _overlayCronixie() { - if (countdownMode) checkCountdown(); #ifndef WLED_DISABLE_CRONIXIE - byte h = hour(local); byte h0 = h; byte m = minute(local); diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 3e135d406..802941a62 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -243,7 +243,7 @@ void serializeInfo(JsonObject root) leds_pin.add(LEDPIN); leds["pwr"] = strip.currentMilliamps; - leds["maxpwr"] = strip.ablMilliampsMax; + leds["maxpwr"] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds["maxseg"] = strip.getMaxSegments(); leds["seglock"] = false; //will be used in the future to prevent modifications to segment config