From 49c40b5967e993db1ec6ed386e3da56b70e17150 Mon Sep 17 00:00:00 2001 From: Benjam Welker Date: Tue, 14 Oct 2025 22:31:35 -0600 Subject: [PATCH 1/4] Add Cycle mode to Twinkle --- wled00/FX.cpp | 110 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 20 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c8b44a18d3..381f87c062 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -662,35 +662,105 @@ static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!"; * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_twinkle(void) { - SEGMENT.fade_out(224); - - uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; - uint32_t it = strip.now / cycleTime; - if (it != SEGENV.step) + // Cycle mode: randomly switch all LEDs to new color, then back to old color + if (SEGMENT.check1) { - unsigned maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on - if (SEGENV.aux0 >= maxOn) + unsigned dataSize = (SEGLEN + 7) >> 3; // 1 bit per LED for tracking + if ( ! SEGENV.allocateData(dataSize)) { - SEGENV.aux0 = 0; - SEGENV.aux1 = hw_random(); //new seed for our PRNG + return mode_static(); // allocation failed + } + + uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; + uint32_t it = strip.now / cycleTime; + + // Initialize on first call + if (SEGENV.call == 0) + { + SEGENV.aux0 = 0; // Count of LEDs switched in current cycle + SEGENV.aux1 = 0; // Direction: 0 = switching to new color, 1 = switching back + memset(SEGENV.data, 0, dataSize); // Clear all bits + SEGMENT.fill(SEGCOLOR(1)); // Start with secondary color + } + + if (it != SEGENV.step) + { + SEGENV.step = it; + + // Check if all LEDs have been switched + if (SEGENV.aux0 >= SEGLEN) + { + // Flip direction and reset counter + SEGENV.aux1 = ! SEGENV.aux1; + SEGENV.aux0 = 0; + memset(SEGENV.data, 0, dataSize); // Clear all bits for next cycle + } + else + { + // Switch one random LED that hasn't been switched yet + unsigned attempts = 0; + unsigned maxAttempts = SEGLEN * 2; // Avoid infinite loop + while (attempts < maxAttempts) + { + unsigned j = hw_random16(SEGLEN); + unsigned index = j >> 3; + unsigned bitNum = j & 0x07; + + if ( ! bitRead(SEGENV.data[index], bitNum)) + { + // This LED hasn't been switched yet + bitWrite(SEGENV.data[index], bitNum, true); + SEGENV.aux0++; + + if (SEGENV.aux1 == 0) + { + // Switching to primary color + SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + } + else + { + // Switching back to secondary color + SEGMENT.setPixelColor(j, SEGCOLOR(1)); + } + break; + } + attempts++; + } + } + } + } + else // Original twinkle behavior + { + SEGMENT.fade_out(224); + + uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; + uint32_t it = strip.now / cycleTime; + if (it != SEGENV.step) + { + unsigned maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on + if (SEGENV.aux0 >= maxOn) + { + SEGENV.aux0 = 0; + SEGENV.aux1 = hw_random(); //new seed for our PRNG + } + SEGENV.aux0++; + SEGENV.step = it; } - SEGENV.aux0++; - SEGENV.step = it; - } - unsigned PRNG16 = SEGENV.aux1; + unsigned PRNG16 = SEGENV.aux1; - for (unsigned i = 0; i < SEGENV.aux0; i++) - { - PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number - uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16; - unsigned j = p >> 16; - SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + for (unsigned i = 0; i < SEGENV.aux0; i++) + { + PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number + uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16; + unsigned j = p >> 16; + SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + } } return FRAMETIME; } -static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; //pixels +static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!,,,,Cycle mode;!,!;!;;m12=0"; //pixels /* From ac0e54312729a056b6ad10819bc0afd9b8e17ae0 Mon Sep 17 00:00:00 2001 From: Benjam Welker Date: Tue, 14 Oct 2025 23:19:36 -0600 Subject: [PATCH 2/4] add suggested fixes --- wled00/FX.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 381f87c062..f534b7cd5f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -665,6 +665,12 @@ uint16_t mode_twinkle(void) { // Cycle mode: randomly switch all LEDs to new color, then back to old color if (SEGMENT.check1) { + // tiny segments: no cycling needed, avoid bitset allocation + if (SEGLEN <= 1) + { + return mode_static(); + } + unsigned dataSize = (SEGLEN + 7) >> 3; // 1 bit per LED for tracking if ( ! SEGENV.allocateData(dataSize)) { @@ -675,7 +681,7 @@ uint16_t mode_twinkle(void) { uint32_t it = strip.now / cycleTime; // Initialize on first call - if (SEGENV.call == 0) + if ((SEGENV.call == 0) || (SEGENV.aux1 > 1)) { SEGENV.aux0 = 0; // Count of LEDs switched in current cycle SEGENV.aux1 = 0; // Direction: 0 = switching to new color, 1 = switching back From a2a2553fe81de5645111bf4bbb8be81ffd82e154 Mon Sep 17 00:00:00 2001 From: Benjam Welker Date: Tue, 14 Oct 2025 23:32:23 -0600 Subject: [PATCH 3/4] more suggestions --- wled00/FX.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index f534b7cd5f..df33c697c8 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -732,6 +732,11 @@ uint16_t mode_twinkle(void) { } attempts++; } + + if (attempts >= maxAttempts) { + // Fail-safe: complete the cycle to avoid getting stuck + SEGENV.aux0 = SEGLEN; + } } } } From 966f23ec8df1705b30e48d425b115f83a26ce284 Mon Sep 17 00:00:00 2001 From: benjamw Date: Sun, 19 Oct 2025 17:34:20 -0600 Subject: [PATCH 4/4] shrink code --- wled00/FX.cpp | 131 ++++++++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 83 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index df33c697c8..b8b996e951 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -660,92 +660,16 @@ static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!"; /* * Blink several LEDs in random colors on, reset, repeat. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + * Cycle mode checkbox is check2 */ uint16_t mode_twinkle(void) { - // Cycle mode: randomly switch all LEDs to new color, then back to old color - if (SEGMENT.check1) - { - // tiny segments: no cycling needed, avoid bitset allocation - if (SEGLEN <= 1) - { - return mode_static(); - } + uint32_t cycleTime = 20 + (255 - SEGMENT.speed) * 5; + uint32_t it = strip.now / cycleTime; - unsigned dataSize = (SEGLEN + 7) >> 3; // 1 bit per LED for tracking - if ( ! SEGENV.allocateData(dataSize)) - { - return mode_static(); // allocation failed - } - - uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; - uint32_t it = strip.now / cycleTime; - - // Initialize on first call - if ((SEGENV.call == 0) || (SEGENV.aux1 > 1)) - { - SEGENV.aux0 = 0; // Count of LEDs switched in current cycle - SEGENV.aux1 = 0; // Direction: 0 = switching to new color, 1 = switching back - memset(SEGENV.data, 0, dataSize); // Clear all bits - SEGMENT.fill(SEGCOLOR(1)); // Start with secondary color - } - - if (it != SEGENV.step) - { - SEGENV.step = it; - - // Check if all LEDs have been switched - if (SEGENV.aux0 >= SEGLEN) - { - // Flip direction and reset counter - SEGENV.aux1 = ! SEGENV.aux1; - SEGENV.aux0 = 0; - memset(SEGENV.data, 0, dataSize); // Clear all bits for next cycle - } - else - { - // Switch one random LED that hasn't been switched yet - unsigned attempts = 0; - unsigned maxAttempts = SEGLEN * 2; // Avoid infinite loop - while (attempts < maxAttempts) - { - unsigned j = hw_random16(SEGLEN); - unsigned index = j >> 3; - unsigned bitNum = j & 0x07; - - if ( ! bitRead(SEGENV.data[index], bitNum)) - { - // This LED hasn't been switched yet - bitWrite(SEGENV.data[index], bitNum, true); - SEGENV.aux0++; - - if (SEGENV.aux1 == 0) - { - // Switching to primary color - SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); - } - else - { - // Switching back to secondary color - SEGMENT.setPixelColor(j, SEGCOLOR(1)); - } - break; - } - attempts++; - } - - if (attempts >= maxAttempts) { - // Fail-safe: complete the cycle to avoid getting stuck - SEGENV.aux0 = SEGLEN; - } - } - } - } - else // Original twinkle behavior - { + // Standard twinkle + if ( ! SEGMENT.check2) { SEGMENT.fade_out(224); - uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; - uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { unsigned maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on @@ -758,7 +682,7 @@ uint16_t mode_twinkle(void) { SEGENV.step = it; } - unsigned PRNG16 = SEGENV.aux1; + uint16_t PRNG16 = SEGENV.aux1; for (unsigned i = 0; i < SEGENV.aux0; i++) { @@ -767,11 +691,52 @@ uint16_t mode_twinkle(void) { unsigned j = p >> 16; SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } + + return FRAMETIME; + } + + // Cycle mode: randomly switch all LEDs to new color, then back to old color + if (SEGLEN <= 1) return mode_static(); + + unsigned dataSize = (SEGLEN + 7) >> 3; + if ( ! SEGENV.allocateData(dataSize)) return mode_static(); + + if (SEGENV.call == 0 || SEGENV.aux1 > 1) + { + SEGENV.aux0 = 0; + SEGENV.aux1 = 0; + memset(SEGENV.data, 0, dataSize); + SEGMENT.fill(SEGCOLOR(1)); + } + + if (it != SEGENV.step) + { + SEGENV.step = it; + if (SEGENV.aux0 >= SEGLEN) + { + SEGENV.aux1 = ! SEGENV.aux1; + SEGENV.aux0 = 0; + memset(SEGENV.data, 0, dataSize); + } + else + { + for (unsigned attempts = 0; attempts < SEGLEN * 2; attempts++) { + unsigned j = hw_random16(SEGLEN); + unsigned byteIndex = j >> 3, bitNum = j & 0x07; + if ( ! bitRead(SEGENV.data[byteIndex], bitNum)) + { + bitWrite(SEGENV.data[byteIndex], bitNum, true); + SEGENV.aux0++; + SEGMENT.setPixelColor(j, SEGENV.aux1 ? SEGCOLOR(1) : SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + break; + } + } + } } return FRAMETIME; } -static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!,,,,Cycle mode;!,!;!;;m12=0"; //pixels +static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!,,,,,Cycle mode;!,!;!;;m12=0"; //pixels /*