Update rainbow: n1 controls step increment
This commit is contained in:
172
src/patterns.py
172
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
|
||||
Reference in New Issue
Block a user