From 2b0b83f981ff41cc813689d574c3b3dbbf2340ac Mon Sep 17 00:00:00 2001 From: jimmy Date: Wed, 12 Nov 2025 19:20:33 +1300 Subject: [PATCH] Update rainbow: n1 controls step increment --- src/patterns.py | 172 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 157 insertions(+), 15 deletions(-) diff --git a/src/patterns.py b/src/patterns.py index 80da747..1567306 100644 --- a/src/patterns.py +++ b/src/patterns.py @@ -22,28 +22,39 @@ param_mapping = { "n4": "n4", "n5": "n5", "n6": "n6", + "auto": "auto", } class Patterns(PatternsBase): def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100): super().__init__(pin, num_leds, color1, color2, brightness, selected, delay) + self.auto = True + self.step = 0 self.patterns = { "off": self.off, "on" : self.on, "blink": self.blink, "rainbow": self.rainbow, "pulse": self.pulse, + "transition": self.transition, } def blink(self): self.stopped = False self.running = True + state = True # True = on, False = off + last_update = utime.ticks_ms() + while self.running: - self.fill(self.apply_brightness(self.colors[0])) - utime.sleep_ms(self.delay) - self.fill((0, 0, 0)) - utime.sleep_ms(self.delay) + current_time = utime.ticks_ms() + if utime.ticks_diff(current_time, last_update) >= self.delay: + if state: + self.fill(self.apply_brightness(self.colors[0])) + else: + self.fill((0, 0, 0)) + state = not state + last_update = current_time self.running = False self.stopped = True @@ -51,17 +62,35 @@ class Patterns(PatternsBase): def rainbow(self): self.stopped = False self.running = True - step = self.pattern_step % 256 - while self.running: + step = self.step % 256 + step_amount = max(1, int(self.n1)) # n1 controls step increment + + # If auto is False, run once and update step + if not self.auto: for i in range(self.num_leds): rc_index = (i * 256 // self.num_leds) + step self.n[i] = self.apply_brightness(self.wheel(rc_index & 255)) self.n.write() - step = (step + 1) % 256 - self.pattern_step = step - # faster animation even with larger delays; scale delay - sleep_ms = max(1, int(self.delay / 5)) - utime.sleep_ms(sleep_ms) + # Increment step by n1 for next call + self.step = (step + step_amount) % 256 + self.running = False + self.stopped = True + return + + # Auto is True: run continuously + sleep_ms = max(1, int(self.delay / 5)) + last_update = utime.ticks_ms() + + while self.running: + current_time = utime.ticks_ms() + if utime.ticks_diff(current_time, last_update) >= sleep_ms: + for i in range(self.num_leds): + rc_index = (i * 256 // self.num_leds) + step + self.n[i] = self.apply_brightness(self.wheel(rc_index & 255)) + self.n.write() + step = (step + step_amount) % 256 + self.step = step + last_update = current_time self.running = False self.stopped = True @@ -76,12 +105,23 @@ class Patterns(PatternsBase): hold_ms = getattr(self, 'n2', 200) # Hold time in ms decay_ms = getattr(self, 'n3', 200) # Decay time in ms - base_color = self.colors[0] if self.colors else (255, 255, 255) - update_interval = 10 # Update every ~10ms for smoothness + # Ensure we have at least one color + if not self.colors: + self.colors = [(255, 255, 255)] + + color_index = 0 + # Calculate minimum update interval based on LED count + # NeoPixel timing: ~30µs per LED + reset time = ~6ms for 200 LEDs + # Use 10ms minimum to ensure writes complete + overhead + min_write_time_ms = (self.num_leds * 30) // 1000 + 1 # Convert µs to ms, add 1ms overhead + update_interval = max(10, min_write_time_ms + 4) # At least 10ms, add margin for safety while self.running: cycle_start = utime.ticks_ms() + # Get the current color from the cycle + base_color = self.colors[color_index % len(self.colors)] + # Attack phase: fade from 0 to full brightness if attack_ms > 0: attack_start = utime.ticks_ms() @@ -115,8 +155,11 @@ class Patterns(PatternsBase): self.fill(self.apply_brightness(color)) last_update = now - # If delay is 0, run only once and exit - if self.delay == 0: + # Move to next color in the cycle + color_index += 1 + + # If auto flag is False, run only once and exit + if not self.auto: break # Ensure the cycle takes exactly delay milliseconds before restarting @@ -129,3 +172,102 @@ class Patterns(PatternsBase): self.running = False self.stopped = True + def transition(self): + """Transition between colors, taking delay ms between each color""" + self.stopped = False + self.running = True + + if not self.colors: + # No colors, turn off + self.off() + self.running = False + self.stopped = True + return + + if len(self.colors) == 1: + # Only one color, just stay that color + last_update = utime.ticks_ms() + while self.running: + current_time = utime.ticks_ms() + if utime.ticks_diff(current_time, last_update) >= 100: + self.fill(self.apply_brightness(self.colors[0])) + last_update = current_time + self.running = False + self.stopped = True + return + + # If auto is False, only transition between color1 and color2 + if not self.auto: + if len(self.colors) < 2: + # Need at least 2 colors for transition + self.running = False + self.stopped = True + return + + transition_duration = max(10, self.delay) # At least 10ms + update_interval = max(10, transition_duration // 50) # Update every ~2% of transition + + # Transition from color1 to color2 + color1 = self.colors[0] + color2 = self.colors[1] + + transition_start = utime.ticks_ms() + last_update = transition_start + + while self.running and utime.ticks_diff(utime.ticks_ms(), transition_start) < transition_duration: + now = utime.ticks_ms() + if utime.ticks_diff(now, last_update) >= update_interval: + # Calculate interpolation factor (0.0 to 1.0) + elapsed = utime.ticks_diff(now, transition_start) + factor = min(1.0, elapsed / transition_duration) + + # Interpolate between color1 and color2 + interpolated = tuple( + int(color1[i] + (color2[i] - color1[i]) * factor) + for i in range(3) + ) + + # Apply brightness and fill + self.fill(self.apply_brightness(interpolated)) + last_update = now + + self.running = False + self.stopped = True + return + + # Auto is True: cycle through all colors continuously + color_index = 0 + transition_duration = max(10, self.delay) # At least 10ms + update_interval = max(10, transition_duration // 50) # Update every ~2% of transition + + while self.running: + # Get current and next color + current_color = self.colors[color_index % len(self.colors)] + next_color = self.colors[(color_index + 1) % len(self.colors)] + + # Transition from current to next color + transition_start = utime.ticks_ms() + last_update = transition_start + + while self.running and utime.ticks_diff(utime.ticks_ms(), transition_start) < transition_duration: + now = utime.ticks_ms() + if utime.ticks_diff(now, last_update) >= update_interval: + # Calculate interpolation factor (0.0 to 1.0) + elapsed = utime.ticks_diff(now, transition_start) + factor = min(1.0, elapsed / transition_duration) + + # Interpolate between colors + interpolated = tuple( + int(current_color[i] + (next_color[i] - current_color[i]) * factor) + for i in range(3) + ) + + # Apply brightness and fill + self.fill(self.apply_brightness(interpolated)) + last_update = now + + # Move to next color + color_index = (color_index + 1) % len(self.colors) + + self.running = False + self.stopped = True \ No newline at end of file