Wire the Pico to UART-driven preset selection, add pattern modules and presets data, remove old p2p/settings code, and update tests and LED driver. Made-with: Cursor
155 lines
5.5 KiB
Python
155 lines
5.5 KiB
Python
import utime
|
|
|
|
|
|
class Roll:
|
|
def __init__(self, driver):
|
|
self.driver = driver
|
|
|
|
def run(self, preset):
|
|
"""Roll: moving band with gradient from color1 to color2 over the strips.
|
|
|
|
- n1: offset from start of strip (effective start = start + n1)
|
|
- n2: offset from end of strip (effective end = end - n2, inclusive)
|
|
- n3: number of full rotations before stopping (0 = infinite)
|
|
- n4: direction (0 = clockwise, 1 = anti-clockwise)
|
|
- c[0]: color1 at the head strip
|
|
- c[1]: color2 at the tail strip
|
|
"""
|
|
colors = preset.c
|
|
color1_raw = colors[0] if colors else (255, 255, 255)
|
|
color2_raw = colors[1] if len(colors) > 1 else (0, 0, 0)
|
|
color1 = self.driver.apply_brightness(color1_raw, preset.b)
|
|
color2 = self.driver.apply_brightness(color2_raw, preset.b)
|
|
|
|
n_segments = self.driver.n.num_strips if hasattr(self.driver.n, "num_strips") else 1
|
|
# Margins from the start and end of each strip
|
|
start_margin = max(0, int(getattr(preset, "n1", 0)))
|
|
end_margin = max(0, int(getattr(preset, "n2", 0)))
|
|
|
|
# Debug info to see why roll might be black
|
|
try:
|
|
print(
|
|
"ROLL preset",
|
|
"p=", getattr(preset, "p", None),
|
|
"b=", getattr(preset, "b", None),
|
|
"colors_raw=", color1_raw, color2_raw,
|
|
"colors_bright=", color1, color2,
|
|
)
|
|
print(
|
|
"ROLL n1..n4",
|
|
getattr(preset, "n1", None),
|
|
getattr(preset, "n2", None),
|
|
getattr(preset, "n3", None),
|
|
getattr(preset, "n4", None),
|
|
"n_segments=", n_segments,
|
|
"start_margin=", start_margin,
|
|
"end_margin=", end_margin,
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
# n3: number of rotations (0 = infinite)
|
|
max_rotations = int(getattr(preset, "n3", 0)) or 0
|
|
# n4: direction (0=cw, 1=ccw); default clockwise if missing
|
|
clockwise = int(getattr(preset, "n4", 0)) == 0
|
|
|
|
step = self.driver.step
|
|
delay_ms = max(1, int(preset.d) or 1)
|
|
last_update = utime.ticks_ms()
|
|
rotations_done = 0
|
|
|
|
def scale_color(c, f):
|
|
return tuple(int(x * f) for x in c)
|
|
|
|
def lerp_color(c1, c2, t):
|
|
"""Linear gradient between two colors."""
|
|
if t <= 0:
|
|
return c1
|
|
if t >= 1:
|
|
return c2
|
|
return (
|
|
int(c1[0] + (c2[0] - c1[0]) * t),
|
|
int(c1[1] + (c2[1] - c1[1]) * t),
|
|
int(c1[2] + (c2[2] - c1[2]) * t),
|
|
)
|
|
|
|
def draw(head):
|
|
# Remember head strip for flare
|
|
try:
|
|
self.driver.last_roll_head = head
|
|
except AttributeError:
|
|
pass
|
|
|
|
strips_list = self.driver.strips
|
|
|
|
for strip_idx, strip in enumerate(strips_list):
|
|
if strip_idx < 0 or strip_idx >= n_segments:
|
|
continue
|
|
|
|
# Distance from head along direction, 0..n_segments-1
|
|
if clockwise:
|
|
dist = (head - strip_idx) % n_segments
|
|
else:
|
|
dist = (strip_idx - head) % n_segments
|
|
|
|
# Color gradient from color1 at the head strip to color2 at the tail strip
|
|
if n_segments > 1:
|
|
t = dist / (n_segments - 1)
|
|
else:
|
|
t = 0.0
|
|
c_strip = lerp_color(color1, color2, t)
|
|
|
|
n = strip.num_leds
|
|
# Effective segment per strip:
|
|
# start = 0 + start_margin
|
|
# end = (n - 1) - end_margin (inclusive)
|
|
width = n - start_margin - end_margin
|
|
if width <= 0:
|
|
# If margins are too large, fall back to full strip
|
|
seg_s = 0
|
|
seg_e = n
|
|
else:
|
|
seg_s = max(0, min(n, start_margin))
|
|
seg_e = min(n, n - end_margin)
|
|
|
|
# Debug for first strip/head to see segment
|
|
try:
|
|
if strip_idx == 0 and head == 0:
|
|
print("ROLL seg strip0 n=", n, "seg_s=", seg_s, "seg_e=", seg_e)
|
|
except Exception:
|
|
pass
|
|
for i in range(n):
|
|
if seg_s <= i < seg_e:
|
|
strip.set(i, c_strip)
|
|
else:
|
|
strip.set(i, (0, 0, 0))
|
|
strip.show()
|
|
|
|
if not preset.a:
|
|
head = step % n_segments if n_segments > 0 else 0
|
|
draw(head)
|
|
self.driver.step = step + 1
|
|
yield
|
|
return
|
|
|
|
while True:
|
|
current_time = utime.ticks_ms()
|
|
if utime.ticks_diff(current_time, last_update) >= delay_ms:
|
|
head = step % n_segments if n_segments > 0 else 0
|
|
if not clockwise and n_segments > 0:
|
|
head = (n_segments - 1 - head)
|
|
|
|
draw(head)
|
|
step += 1
|
|
|
|
if max_rotations > 0 and n_segments > 0 and (step % n_segments) == 0:
|
|
rotations_done += 1
|
|
if rotations_done >= max_rotations:
|
|
self.driver.step = step
|
|
last_update = current_time
|
|
return
|
|
|
|
self.driver.step = step
|
|
last_update = current_time
|
|
yield
|