diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index 56fbc118..989a398d 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -45,21 +45,35 @@ uint16_t WS2812FX::mode_static(void) { * if(strobe == true) then create a strobe effect */ uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { - uint32_t color = ((SEGENV.call & 1) == 0) ? color1 : color2; + uint16_t stateTime = SEGENV.aux1; + uint32_t cycleTime = (255 - SEGMENT.speed)*20; + uint32_t onTime = 0; + uint32_t offTime = cycleTime; + + if (!strobe) { + onTime = (cycleTime * SEGMENT.intensity) >> 8; + offTime = cycleTime - onTime; + } + + stateTime = ((SEGENV.aux0 & 1) == 0) ? onTime : offTime; + stateTime += 20; + + if (now - SEGENV.step > stateTime) + { + SEGENV.aux0++; + SEGENV.aux1 = stateTime; + SEGENV.step = now; + } + + uint32_t color = ((SEGENV.aux0 & 1) == 0) ? color1 : color2; if (color == color1 && do_palette) { for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - } else { - fill(color); - } + } else fill(color); - if((SEGENV.call & 1) == 0) { - return strobe ? 20 : (100 + ((1986 * (uint32_t)(255 - SEGMENT.speed)) / 255))*(float)(SEGMENT.intensity/128.0); - } else { - return strobe ? 50 + ((1986 * (uint32_t)(255 - SEGMENT.speed)) / 255) : (100 + ((1986 * (uint32_t)(255 - SEGMENT.speed)) / 255))*(float)(2.0-(SEGMENT.intensity/128.0)); - } + return 20; } @@ -2044,7 +2058,7 @@ uint16_t WS2812FX::mode_ripple() uint16_t origin = SEGMENT.start + random16(SEGLEN); _locked[storeI+1] = origin >> 8; _locked[storeI+2] = origin & 0xFF; - _locked[storeI+3] = random8(); + _locked[storeI+3] = random8(); //color } } } @@ -2062,7 +2076,7 @@ uint16_t WS2812FX::mode_ripple() // incandescent bulbs change color as they get dim down. #define COOL_LIKE_INCANDESCENT 1 -CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt) +CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) uint16_t ticks = ms / (32 - (SEGMENT.speed >> 3)); @@ -2083,11 +2097,16 @@ CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt) // This is like 'triwave8', which produces a // symmetrical up-and-down triangle sawtooth waveform, except that this // function produces a triangle wave with a faster attack and a slower decay - if (ph < 86) { - bright = ph * 3; - } else { - ph -= 86; - bright = 255 - (ph + (ph/2)); + if (cat) //twinklecat, variant where the leds instantly turn on + { + bright = 255 - ph; + } else { //vanilla twinklefox + if (ph < 86) { + bright = ph * 3; + } else { + ph -= 86; + bright = 255 - (ph + (ph/2)); + } } } @@ -2117,7 +2136,7 @@ CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt) // "CalculateOneTwinkle" on each pixel. It then displays // either the twinkle color of the background color, // whichever is brighter. -uint16_t WS2812FX::mode_twinklefox() +void WS2812FX::twinklefox_base(bool cat) { // "PRNG16" is the pseudorandom number generator // It MUST be reset to the same starting value each time @@ -2125,8 +2144,6 @@ uint16_t WS2812FX::mode_twinklefox() // numbers that it generates is (paradoxically) stable. uint16_t PRNG16 = 11337; - uint32_t clock32 = millis(); - // Set up the background color, "bg". // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of // the current palette are identical, then a deeply faded version of @@ -2151,13 +2168,13 @@ uint16_t WS2812FX::mode_twinklefox() PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths) uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08; - uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; + uint32_t myclock30 = (uint32_t)((now * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel // We now have the adjusted 'clock' for this pixel, now we call // the function that computes what color the pixel should be based // on the "brightness = f( time )" idea. - CRGB c = twinklefox_one_twinkle(myclock30, myunique8); + CRGB c = twinklefox_one_twinkle(myclock30, myunique8, cat); uint8_t cbright = c.getAverageLight(); int16_t deltabright = cbright - backgroundBrightness; @@ -2175,5 +2192,74 @@ uint16_t WS2812FX::mode_twinklefox() setPixelColor(i, bg); } } +} + +uint16_t WS2812FX::mode_twinklefox() +{ + twinklefox_base(false); + return 20; +} + +uint16_t WS2812FX::mode_twinklecat() +{ + twinklefox_base(true); + return 20; +} + + +//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes +#define HALLOWEEN_EYE_SPACE 3 +#define HALLOWEEN_EYE_WIDTH 1 + +uint16_t WS2812FX::mode_halloween_eyes() +{ + uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; + if (eyeLength > SEGLEN) return mode_static(); //bail if segment too short + + fill(SEGCOLOR(1)); //fill background + + uint8_t state = SEGENV.aux1 >> 8; + uint16_t stateTime = SEGENV.call; + if (stateTime == 0) stateTime = 2000; + + if (state == 0) { //spawn eyes + SEGENV.aux0 = random(SEGMENT.start, SEGMENT.stop - eyeLength); //start pos + SEGENV.aux1 = random8(); //color + state = 1; + } + + if (state < 2) { //fade eyes + uint16_t startPos = SEGENV.aux0; + uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE; + + uint32_t fadestage = (now - SEGENV.step)*255 / stateTime; + if (fadestage > 255) fadestage = 255; + uint32_t c = color_blend(color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage); + + for (uint16_t i = 0; i < HALLOWEEN_EYE_WIDTH; i++) + { + setPixelColor(startPos + i, c); + setPixelColor(start2ndEye + i, c); + } + } + + if (now - SEGENV.step > stateTime) + { + state++; + if (state > 2) state = 0; + + if (state < 2) + { + stateTime = 100 + (255 - SEGMENT.intensity)*10; //eye fade time + } else { + uint16_t eyeOffTimeBase = (255 - SEGMENT.speed)*10; + stateTime = eyeOffTimeBase + random(eyeOffTimeBase); + } + SEGENV.step = now; + SEGENV.call = stateTime; + } + + SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); //save state + return 20; } diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 150ce115..d4b02709 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -79,7 +79,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 81 +#define MODE_COUNT 83 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -163,6 +163,8 @@ #define FX_MODE_RAILWAY 78 #define FX_MODE_RIPPLE 79 #define FX_MODE_TWINKLEFOX 80 +#define FX_MODE_TWINKLECAT 81 +#define FX_MODE_HALLOWEEN_EYES 82 class WS2812FX { @@ -299,6 +301,8 @@ class WS2812FX { _mode[FX_MODE_RAILWAY] = &WS2812FX::mode_railway; _mode[FX_MODE_RIPPLE] = &WS2812FX::mode_ripple; _mode[FX_MODE_TWINKLEFOX] = &WS2812FX::mode_twinklefox; + _mode[FX_MODE_TWINKLECAT] = &WS2812FX::mode_twinklecat; + _mode[FX_MODE_HALLOWEEN_EYES] = &WS2812FX::mode_halloween_eyes; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -469,7 +473,9 @@ class WS2812FX { mode_meteor_smooth(void), mode_railway(void), mode_ripple(void), - mode_twinklefox(void); + mode_twinklefox(void), + mode_twinklecat(void), + mode_halloween_eyes(void); private: NeoPixelWrapper *bus; @@ -478,7 +484,8 @@ class WS2812FX { CRGB col_to_crgb(uint32_t); CRGBPalette16 currentPalette; CRGBPalette16 targetPalette; - + + uint32_t now; uint16_t _length; uint16_t _rand16seed; uint8_t _brightness; @@ -486,6 +493,7 @@ class WS2812FX { void handle_palette(void); void fill(uint32_t); bool modeUsesLock(uint8_t); + void twinklefox_base(bool); bool _modeUsesLock, @@ -513,7 +521,7 @@ class WS2812FX { running(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t); - CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt); + CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat); uint32_t _lastPaletteChange = 0; uint32_t _lastShow = 0; @@ -536,8 +544,9 @@ 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","In Out","In In", "Out Out","Out In","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","Colortwinkle","Lake","Meteor","Smooth Meteor","Railway","Ripple","Twinklefox" +"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" ])====="; diff --git a/wled00/WS2812FX_fcn.cpp b/wled00/WS2812FX_fcn.cpp index 67bc46cb..1bdd121a 100644 --- a/wled00/WS2812FX_fcn.cpp +++ b/wled00/WS2812FX_fcn.cpp @@ -55,7 +55,7 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) } void WS2812FX::service() { - unsigned long now = millis(); // Be aware, millis() rolls over every 49 days + now = millis(); // Be aware, millis() rolls over every 49 days if (now - _lastShow < MIN_SHOW_DELAY) return; bool doShow = false; for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++) @@ -69,7 +69,7 @@ void WS2812FX::service() { handle_palette(); uint16_t delay = (this->*_mode[SEGMENT.mode])(); SEGENV.next_time = now + delay; - SEGENV.call++; + if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++; } } } diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 4aad5ded..95993b3f 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1909194 +#define VERSION 1910012 char versionString[] = "0.8.5";