Update rainbow: n1 controls step increment

This commit is contained in:
2025-11-12 19:20:33 +13:00
parent 1d82ea6a91
commit 2b0b83f981

View File

@@ -22,28 +22,39 @@ param_mapping = {
"n4": "n4", "n4": "n4",
"n5": "n5", "n5": "n5",
"n6": "n6", "n6": "n6",
"auto": "auto",
} }
class Patterns(PatternsBase): class Patterns(PatternsBase):
def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100): 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) super().__init__(pin, num_leds, color1, color2, brightness, selected, delay)
self.auto = True
self.step = 0
self.patterns = { self.patterns = {
"off": self.off, "off": self.off,
"on" : self.on, "on" : self.on,
"blink": self.blink, "blink": self.blink,
"rainbow": self.rainbow, "rainbow": self.rainbow,
"pulse": self.pulse, "pulse": self.pulse,
"transition": self.transition,
} }
def blink(self): def blink(self):
self.stopped = False self.stopped = False
self.running = True self.running = True
state = True # True = on, False = off
last_update = utime.ticks_ms()
while self.running: while self.running:
self.fill(self.apply_brightness(self.colors[0])) current_time = utime.ticks_ms()
utime.sleep_ms(self.delay) if utime.ticks_diff(current_time, last_update) >= self.delay:
self.fill((0, 0, 0)) if state:
utime.sleep_ms(self.delay) 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.running = False
self.stopped = True self.stopped = True
@@ -51,17 +62,35 @@ class Patterns(PatternsBase):
def rainbow(self): def rainbow(self):
self.stopped = False self.stopped = False
self.running = True self.running = True
step = self.pattern_step % 256 step = self.step % 256
while self.running: 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): for i in range(self.num_leds):
rc_index = (i * 256 // self.num_leds) + step rc_index = (i * 256 // self.num_leds) + step
self.n[i] = self.apply_brightness(self.wheel(rc_index & 255)) self.n[i] = self.apply_brightness(self.wheel(rc_index & 255))
self.n.write() self.n.write()
step = (step + 1) % 256 # Increment step by n1 for next call
self.pattern_step = step self.step = (step + step_amount) % 256
# faster animation even with larger delays; scale delay self.running = False
sleep_ms = max(1, int(self.delay / 5)) self.stopped = True
utime.sleep_ms(sleep_ms) 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.running = False
self.stopped = True self.stopped = True
@@ -76,12 +105,23 @@ class Patterns(PatternsBase):
hold_ms = getattr(self, 'n2', 200) # Hold time in ms hold_ms = getattr(self, 'n2', 200) # Hold time in ms
decay_ms = getattr(self, 'n3', 200) # Decay 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) # Ensure we have at least one color
update_interval = 10 # Update every ~10ms for smoothness 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: while self.running:
cycle_start = utime.ticks_ms() 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 # Attack phase: fade from 0 to full brightness
if attack_ms > 0: if attack_ms > 0:
attack_start = utime.ticks_ms() attack_start = utime.ticks_ms()
@@ -115,8 +155,11 @@ class Patterns(PatternsBase):
self.fill(self.apply_brightness(color)) self.fill(self.apply_brightness(color))
last_update = now last_update = now
# If delay is 0, run only once and exit # Move to next color in the cycle
if self.delay == 0: color_index += 1
# If auto flag is False, run only once and exit
if not self.auto:
break break
# Ensure the cycle takes exactly delay milliseconds before restarting # Ensure the cycle takes exactly delay milliseconds before restarting
@@ -129,3 +172,102 @@ class Patterns(PatternsBase):
self.running = False self.running = False
self.stopped = True 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