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",
|
"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
|
||||||
Reference in New Issue
Block a user