convert blink to use a thread

This commit is contained in:
2025-10-25 19:53:16 +13:00
parent 9fad8b1ae5
commit 059bd41a59
3 changed files with 421 additions and 389 deletions

View File

@@ -1,20 +0,0 @@
import asyncio
import aioespnow
import json
async def p2p(settings, patterns):
e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support
e.active(True)
async for mac, msg in e:
try:
data = json.loads(msg)
except:
print(f"Failed to load espnow data {msg}")
continue
print(data)
if "names" not in data or settings.get("name") in data.get("names", []):
if "step" in settings and isinstance(settings["step"], int):
patterns.set_pattern_step(settings["step"])
else:
settings.set_settings(data.get("settings", {}), patterns, data.get("save", False))
print("should not print")

View File

@@ -2,6 +2,8 @@
import utime import utime
import random import random
from patterns_base import PatternBase # Import PatternBase from patterns_base import PatternBase # Import PatternBase
import _thread
from machine import WDT
class Patterns(PatternBase): # Inherit from PatternBase class Patterns(PatternBase): # Inherit from PatternBase
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):
@@ -16,438 +18,448 @@ class Patterns(PatternBase): # Inherit from PatternBase
self.n4 = 0 self.n4 = 0
self.oneshot = False # New: One-shot flag for patterns like fill_range self.oneshot = False # New: One-shot flag for patterns like fill_range
self.patterns = { self.patterns = {
"on": self.on,
"off": self.off,
"flicker": self.flicker,
"fill_range": self.fill_range,
"n_chase": self.n_chase,
"alternating": self.alternating,
"pulse": self.pulse,
"rainbow": self.rainbow,
"specto": self.specto,
"radiate": self.radiate,
"segmented_movement": self.segmented_movement,
# Shortened pattern names for optimized JSON payloads # Shortened pattern names for optimized JSON payloads
"o": self.off, "o": self.off,
"f": self.flicker, "on": self.on,
"fr": self.fill_range, "bl": self.blink,
"nc": self.n_chase,
"a": self.alternating,
"p": self.pulse,
"r": self.rainbow,
"s": self.specto,
"rd": self.radiate,
"sm": self.segmented_movement,
} }
self.step = 0 self.step = 0
self.run = True
self.running = False
self.wdt = WDT(timeout=10000)
def select(self, pattern):
self.selected = pattern
self.run = False
if pattern not in self.patterns:
return False
while self.running:
utime.sleep_ms(1)
self.running = True
_thread.start_new_thread(self.patterns[pattern], ())
def on(self): def on(self):
"""Turn on all LEDs with current color""" """Turn on all LEDs with current color"""
self.fill(self.apply_brightness(self.colors[0])) self.fill(self.apply_brightness(self.colors[0]))
self.n.write()
return self.delay
def off(self): def off(self):
"""Turn off all LEDs""" """Turn off all LEDs"""
self.fill((0, 0, 0)) self.fill((0, 0, 0))
self.n.write()
return self.delay
def flicker(self): def blink(self):
current_time = utime.ticks_ms() self.run = True
base_color = self.colors[0] start = utime.ticks_ms()
# Use fixed minimum brightness of 10, flicker between 10 and full brightness while self.run:
# Use n3 as step rate multiplier to control how fast patterns step self.wdt.feed()
min_brightness = 10 diff = utime.ticks_diff(utime.ticks_ms(), start)
step_rate = max(1, int(self.n3)) if diff >= self.delay:
flicker_brightness_offset = random.randint(-int(self.brightness // 1.5), int(self.brightness // 1.5)) self.fill((0, 0, 0))
flicker_brightness = max(min_brightness, min(255, self.brightness + flicker_brightness_offset)) start = utime.ticks_ms()
elif diff >= self.delay/2:
flicker_color = self.apply_brightness(base_color, brightness_override=flicker_brightness) self.fill(self.apply_brightness(self.colors[0]))
self.fill(flicker_color)
self.last_update = current_time
return max(1, int(self.delay // (5 * step_rate)))
def fill_range(self):
"""
Fills a range of LEDs from n1 to n2 with a solid color.
If self.oneshot is True, it fills once and then turns off the LEDs.
"""
current_time = utime.ticks_ms()
if self.oneshot and self.pattern_step >= 1:
self.fill((0, 0, 0)) # Turn off LEDs if one-shot already happened
else:
color = self.apply_brightness(self.colors[0])
for i in range(self.n1, self.n2 + 1):
self.n[i] = color
self.n.write()
self.last_update = current_time
return self.delay
self.last_update = current_time
return self.delay
def n_chase(self):
"""
A theater chase pattern using n1 for on-width and n2 for off-width.
"""
current_time = utime.ticks_ms()
step_rate = max(1, int(self.n3))
segment_length = self.n1 + self.n2
if segment_length == 0: # Avoid division by zero
self.fill((0,0,0))
self.n.write()
self.last_update = current_time
return self.delay
# Use controller's step for synchronization, but scale it for chasing
chase_step = (self.step * step_rate) % self.num_leds
for i in range(self.num_leds): self.run = False
# Calculate position relative to the chase head self.running = False
pos_from_head = (i - chase_step) % self.num_leds
if pos_from_head < self.n1: # def flicker(self):
self.n[i] = self.apply_brightness(self.colors[0]) # current_time = utime.ticks_ms()
else: # base_color = self.colors[0]
self.n[i] = (0, 0, 0)
self.n.write() # # Use fixed minimum brightness of 10, flicker between 10 and full brightness
# # Use n3 as step rate multiplier to control how fast patterns step
# min_brightness = 10
# step_rate = max(1, int(self.n3))
# flicker_brightness_offset = random.randint(-int(self.brightness // 1.5), int(self.brightness // 1.5))
# flicker_brightness = max(min_brightness, min(255, self.brightness + flicker_brightness_offset))
# flicker_color = self.apply_brightness(base_color, brightness_override=flicker_brightness)
# self.fill(flicker_color)
# self.last_update = current_time
# return max(1, int(self.delay // (5 * step_rate)))
# def fill_range(self):
# """
# Fills a range of LEDs from n1 to n2 with a solid color.
# If self.oneshot is True, it fills once and then turns off the LEDs.
# """
# current_time = utime.ticks_ms()
# if self.oneshot and self.pattern_step >= 1:
# self.fill((0, 0, 0)) # Turn off LEDs if one-shot already happened
# else:
# color = self.apply_brightness(self.colors[0])
# for i in range(self.n1, self.n2 + 1):
# self.n[i] = color
# self.n.write()
# self.last_update = current_time
# return self.delay
# self.last_update = current_time
# return self.delay
# def n_chase(self):
# """
# A theater chase pattern using n1 for on-width and n2 for off-width.
# """
# current_time = utime.ticks_ms()
# step_rate = max(1, int(self.n3))
# segment_length = self.n1 + self.n2
# if segment_length == 0: # Avoid division by zero
# self.fill((0,0,0))
# self.n.write()
# self.last_update = current_time
# return self.delay
# Don't update internal step - use controller's step for sync # # Use controller's step for synchronization, but scale it for chasing
self.last_update = current_time # chase_step = (self.step * step_rate) % self.num_leds
return self.delay
# for i in range(self.num_leds):
# # Calculate position relative to the chase head
# pos_from_head = (i - chase_step) % self.num_leds
# if pos_from_head < self.n1:
# self.n[i] = self.apply_brightness(self.colors[0])
# else:
# self.n[i] = (0, 0, 0)
# self.n.write()
# # Don't update internal step - use controller's step for sync
# self.last_update = current_time
# return self.delay
def alternating(self): # def alternating(self):
# Use n1 as ON width and n2 as OFF width # # Use n1 as ON width and n2 as OFF width
segment_on = max(0, int(self.n1)) # segment_on = max(0, int(self.n1))
segment_off = max(0, int(self.n2)) # segment_off = max(0, int(self.n2))
total_segment_length = segment_on + segment_off # total_segment_length = segment_on + segment_off
if total_segment_length <= 0: # if total_segment_length <= 0:
self.fill((0, 0, 0)) # self.fill((0, 0, 0))
self.n.write() # self.n.write()
return self.delay # return self.delay
current_phase = self.step % 2 # current_phase = self.step % 2
active_color = self.apply_brightness(self.colors[0]) # active_color = self.apply_brightness(self.colors[0])
for i in range(self.num_leds): # for i in range(self.num_leds):
pos_in_segment = i % total_segment_length # pos_in_segment = i % total_segment_length
if current_phase == 0: # if current_phase == 0:
# ON then OFF # # ON then OFF
if pos_in_segment < segment_on: # if pos_in_segment < segment_on:
self.n[i] = active_color # self.n[i] = active_color
else: # else:
self.n[i] = (0, 0, 0) # self.n[i] = (0, 0, 0)
else: # else:
# OFF then ON # # OFF then ON
if pos_in_segment < segment_on: # if pos_in_segment < segment_on:
self.n[i] = (0, 0, 0) # self.n[i] = (0, 0, 0)
else: # else:
self.n[i] = active_color # self.n[i] = active_color
self.n.write() # self.n.write()
# Don't update step - use the step value sent from controller for synchronization # # Don't update step - use the step value sent from controller for synchronization
return max(1, int(self.delay // 2)) # return max(1, int(self.delay // 2))
def pulse(self): # def pulse(self):
# Envelope: attack=n1 ms, hold=delay ms, decay=n2 ms # # Envelope: attack=n1 ms, hold=delay ms, decay=n2 ms
attack_ms = max(0, int(self.n1)) # attack_ms = max(0, int(self.n1))
hold_ms = max(0, int(self.delay)) # hold_ms = max(0, int(self.delay))
decay_ms = max(0, int(self.n2)) # decay_ms = max(0, int(self.n2))
base = self.colors[0] if len(self.colors) > 0 else (255, 255, 255) # base = self.colors[0] if len(self.colors) > 0 else (255, 255, 255)
full_brightness = max(0, min(255, int(self.brightness))) # full_brightness = max(0, min(255, int(self.brightness)))
# Attack phase (0 -> full) # # Attack phase (0 -> full)
if attack_ms > 0: # if attack_ms > 0:
start = utime.ticks_ms() # start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < attack_ms: # while utime.ticks_diff(utime.ticks_ms(), start) < attack_ms:
elapsed = utime.ticks_diff(utime.ticks_ms(), start) # elapsed = utime.ticks_diff(utime.ticks_ms(), start)
frac = elapsed / attack_ms if attack_ms > 0 else 1.0 # frac = elapsed / attack_ms if attack_ms > 0 else 1.0
b = int(full_brightness * frac) # b = int(full_brightness * frac)
self.fill(self.apply_brightness(base, brightness_override=b)) # self.fill(self.apply_brightness(base, brightness_override=b))
else: # else:
self.fill(self.apply_brightness(base, brightness_override=full_brightness)) # self.fill(self.apply_brightness(base, brightness_override=full_brightness))
# Hold phase # # Hold phase
if hold_ms > 0: # if hold_ms > 0:
start = utime.ticks_ms() # start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < hold_ms: # while utime.ticks_diff(utime.ticks_ms(), start) < hold_ms:
pass # pass
# Decay phase (full -> 0) # # Decay phase (full -> 0)
if decay_ms > 0: # if decay_ms > 0:
start = utime.ticks_ms() # start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < decay_ms: # while utime.ticks_diff(utime.ticks_ms(), start) < decay_ms:
elapsed = utime.ticks_diff(utime.ticks_ms(), start) # elapsed = utime.ticks_diff(utime.ticks_ms(), start)
frac = 1.0 - (elapsed / decay_ms if decay_ms > 0 else 1.0) # frac = 1.0 - (elapsed / decay_ms if decay_ms > 0 else 1.0)
if frac < 0: # if frac < 0:
frac = 0 # frac = 0
b = int(full_brightness * frac) # b = int(full_brightness * frac)
self.fill(self.apply_brightness(base, brightness_override=b)) # self.fill(self.apply_brightness(base, brightness_override=b))
# Ensure off at the end and stop auto-run # # Ensure off at the end and stop auto-run
self.fill((0, 0, 0)) # self.fill((0, 0, 0))
self.run = False # self.run = False
return self.delay # return self.delay
def rainbow(self): # def rainbow(self):
# Wheel function to map 0-255 to RGB # # Wheel function to map 0-255 to RGB
def wheel(pos): # def wheel(pos):
if pos < 85: # if pos < 85:
return (pos * 3, 255 - pos * 3, 0) # return (pos * 3, 255 - pos * 3, 0)
elif pos < 170: # elif pos < 170:
pos -= 85 # pos -= 85
return (255 - pos * 3, 0, pos * 3) # return (255 - pos * 3, 0, pos * 3)
else: # else:
pos -= 170 # pos -= 170
return (0, pos * 3, 255 - pos * 3) # return (0, pos * 3, 255 - pos * 3)
step_rate = max(1, int(self.n3)) # step_rate = max(1, int(self.n3))
# Use controller's step for synchronization, scaled for rainbow cycling # # Use controller's step for synchronization, scaled for rainbow cycling
rainbow_step = (self.step * step_rate) % 256 # rainbow_step = (self.step * step_rate) % 256
for i in range(self.num_leds): # for i in range(self.num_leds):
rc_index = (i * 256 // max(1, self.num_leds)) + rainbow_step # rc_index = (i * 256 // max(1, self.num_leds)) + rainbow_step
self.n[i] = self.apply_brightness(wheel(rc_index & 255)) # self.n[i] = self.apply_brightness(wheel(rc_index & 255))
self.n.write() # self.n.write()
# Don't update internal step - use controller's step for sync # # Don't update internal step - use controller's step for sync
return max(1, int(self.delay // 5)) # return max(1, int(self.delay // 5))
def specto(self): # def specto(self):
# Light up LEDs from 0 up to n1 (exclusive) and turn the rest off # # Light up LEDs from 0 up to n1 (exclusive) and turn the rest off
count = int(self.n1) # count = int(self.n1)
if count < 0: # if count < 0:
count = 0 # count = 0
if count > self.num_leds: # if count > self.num_leds:
count = self.num_leds # count = self.num_leds
color = self.apply_brightness(self.colors[0] if len(self.colors) > 0 else (255, 255, 255)) # color = self.apply_brightness(self.colors[0] if len(self.colors) > 0 else (255, 255, 255))
for i in range(self.num_leds): # for i in range(self.num_leds):
self.n[i] = color if i < count else (0, 0, 0) # self.n[i] = color if i < count else (0, 0, 0)
self.n.write() # self.n.write()
return self.delay # return self.delay
def radiate(self): # def radiate(self):
# Radiate outward from origins spaced every n1 LEDs, stepping each ring by self.delay # # Radiate outward from origins spaced every n1 LEDs, stepping each ring by self.delay
sep = max(1, int(self.n1) if self.n1 else 1) # sep = max(1, int(self.n1) if self.n1 else 1)
color = self.apply_brightness(self.colors[0] if len(self.colors) > 0 else (255, 255, 255)) # color = self.apply_brightness(self.colors[0] if len(self.colors) > 0 else (255, 255, 255))
# Start with strip off # # Start with strip off
self.fill((0, 0, 0)) # self.fill((0, 0, 0))
origins = list(range(0, self.num_leds, sep)) # origins = list(range(0, self.num_leds, sep))
radius = 0 # radius = 0
lit_total = 0 # lit_total = 0
while True: # while True:
drew_any = False # drew_any = False
for o in origins: # for o in origins:
left = o - radius # left = o - radius
right = o + radius # right = o + radius
if 0 <= left < self.num_leds: # if 0 <= left < self.num_leds:
if self.n[left] == (0, 0, 0): # if self.n[left] == (0, 0, 0):
lit_total += 1 # lit_total += 1
self.n[left] = color # self.n[left] = color
drew_any = True # drew_any = True
if 0 <= right < self.num_leds: # if 0 <= right < self.num_leds:
if self.n[right] == (0, 0, 0): # if self.n[right] == (0, 0, 0):
lit_total += 1 # lit_total += 1
self.n[right] = color # self.n[right] = color
drew_any = True # drew_any = True
self.n.write() # self.n.write()
# If we didn't draw anything new, we've reached beyond edges # # If we didn't draw anything new, we've reached beyond edges
if not drew_any: # if not drew_any:
break # break
# If all LEDs are now lit, immediately proceed to dark sweep # # If all LEDs are now lit, immediately proceed to dark sweep
if lit_total >= self.num_leds: # if lit_total >= self.num_leds:
break # break
# wait self.delay ms before next ring # # wait self.delay ms before next ring
start = utime.ticks_us() # start = utime.ticks_us()
while utime.ticks_diff(utime.ticks_us(), start) < self.delay: # while utime.ticks_diff(utime.ticks_us(), start) < self.delay:
pass # pass
radius += 1 # radius += 1
# Radiate back out (darkness outward): turn off from center to edges # # Radiate back out (darkness outward): turn off from center to edges
last_radius = max(0, radius - 1) # last_radius = max(0, radius - 1)
for r in range(0, last_radius + 1): # for r in range(0, last_radius + 1):
for o in origins: # for o in origins:
left = o - r # left = o - r
right = o + r # right = o + r
if 0 <= left < self.num_leds: # if 0 <= left < self.num_leds:
self.n[left] = (0, 0, 0) # self.n[left] = (0, 0, 0)
if 0 <= right < self.num_leds: # if 0 <= right < self.num_leds:
self.n[right] = (0, 0, 0) # self.n[right] = (0, 0, 0)
self.n.write() # self.n.write()
start = utime.ticks_us() # start = utime.ticks_us()
while utime.ticks_diff(utime.ticks_us(), start) < self.delay: # while utime.ticks_diff(utime.ticks_us(), start) < self.delay:
pass # pass
# ensure all LEDs are off at completion # # ensure all LEDs are off at completion
self.fill((0, 0, 0)) # self.fill((0, 0, 0))
# mark complete so scheduler won't auto-run again until re-selected # # mark complete so scheduler won't auto-run again until re-selected
self.run = False # self.run = False
return self.delay # return self.delay
def segmented_movement(self): # def segmented_movement(self):
""" # """
Segmented movement pattern that alternates forward and backward. # Segmented movement pattern that alternates forward and backward.
Parameters: # Parameters:
n1: Number of LEDs per segment # n1: Number of LEDs per segment
n2: Spacing between segments (currently unused) # n2: Spacing between segments (currently unused)
n3: Forward movement steps per beat # n3: Forward movement steps per beat
n4: Backward movement steps per beat # n4: Backward movement steps per beat
Movement: Alternates between moving forward n3 steps and backward n4 steps each beat. # Movement: Alternates between moving forward n3 steps and backward n4 steps each beat.
""" # """
try: # try:
# Get parameters # # Get parameters
segment_length = max(1, int(self.n1)) if hasattr(self, 'n1') else 3 # segment_length = max(1, int(self.n1)) if hasattr(self, 'n1') else 3
segment_spacing = max(0, int(self.n2)) if hasattr(self, 'n2') else 2 # segment_spacing = max(0, int(self.n2)) if hasattr(self, 'n2') else 2
forward_step = max(0, int(self.n3)) if hasattr(self, 'n3') else 1 # forward_step = max(0, int(self.n3)) if hasattr(self, 'n3') else 1
backward_step = max(0, int(self.n4)) if hasattr(self, 'n4') else 0 # backward_step = max(0, int(self.n4)) if hasattr(self, 'n4') else 0
# Initialize position tracking if not exists # # Initialize position tracking if not exists
if not hasattr(self, '_sm_position'): # if not hasattr(self, '_sm_position'):
self._sm_position = 0 # self._sm_position = 0
self._sm_last_step = -1 # self._sm_last_step = -1
# Check if this is a new beat (step changed) # # Check if this is a new beat (step changed)
if self.step != self._sm_last_step: # if self.step != self._sm_last_step:
# Alternate between forward and backward movement # # Alternate between forward and backward movement
if self.step % 2 == 0: # if self.step % 2 == 0:
# Even steps: move forward (if n3 > 0) # # Even steps: move forward (if n3 > 0)
if forward_step > 0: # if forward_step > 0:
self._sm_position += forward_step # self._sm_position += forward_step
direction = "FWD" # direction = "FWD"
elif backward_step > 0: # elif backward_step > 0:
# If no forward, still move backward # # If no forward, still move backward
self._sm_position -= backward_step # self._sm_position -= backward_step
direction = "BWD" # direction = "BWD"
else: # else:
direction = "NONE" # direction = "NONE"
else: # else:
# Odd steps: move backward (if n4 > 0) # # Odd steps: move backward (if n4 > 0)
if backward_step > 0: # if backward_step > 0:
self._sm_position -= backward_step # self._sm_position -= backward_step
direction = "BWD" # direction = "BWD"
elif forward_step > 0: # elif forward_step > 0:
# If no backward, still move forward # # If no backward, still move forward
self._sm_position += forward_step # self._sm_position += forward_step
direction = "FWD" # direction = "FWD"
else: # else:
direction = "NONE" # direction = "NONE"
# Wrap position around strip length # # Wrap position around strip length
strip_length = self.num_leds + segment_length # strip_length = self.num_leds + segment_length
self._sm_position = self._sm_position % strip_length # self._sm_position = self._sm_position % strip_length
# Update last step # # Update last step
self._sm_last_step = self.step # self._sm_last_step = self.step
# DEBUG: Print every beat # # DEBUG: Print every beat
if self.step % 5 == 0: # if self.step % 5 == 0:
print(f"SM: step={self.step}, dir={direction}, n3={forward_step}, n4={backward_step}, pos={self._sm_position}") # print(f"SM: step={self.step}, dir={direction}, n3={forward_step}, n4={backward_step}, pos={self._sm_position}")
# Clear all LEDs # # Clear all LEDs
self.fill((0, 0, 0)) # self.fill((0, 0, 0))
# Get color # # Get color
color = self.apply_brightness(self.colors[0]) # color = self.apply_brightness(self.colors[0])
# Calculate segment width (segment + spacing) # # Calculate segment width (segment + spacing)
segment_width = segment_length + segment_spacing # segment_width = segment_length + segment_spacing
# Draw multiple segments across the strip # # Draw multiple segments across the strip
if segment_width > 0: # if segment_width > 0:
base_position = int(self._sm_position) % segment_width # base_position = int(self._sm_position) % segment_width
# Draw segments starting from base_position # # Draw segments starting from base_position
current_pos = base_position # current_pos = base_position
while current_pos < self.num_leds: # while current_pos < self.num_leds:
# Draw segment from current_pos to current_pos + segment_length # # Draw segment from current_pos to current_pos + segment_length
segment_end = min(current_pos + segment_length, self.num_leds) # segment_end = min(current_pos + segment_length, self.num_leds)
for i in range(max(0, current_pos), segment_end): # for i in range(max(0, current_pos), segment_end):
self.n[i] = color # self.n[i] = color
# Move to next segment position # # Move to next segment position
current_pos += segment_width # current_pos += segment_width
# Handle wrap-around: draw segments that start before 0 # # Handle wrap-around: draw segments that start before 0
wrap_position = base_position - segment_width # wrap_position = base_position - segment_width
while wrap_position > -segment_length: # while wrap_position > -segment_length:
if wrap_position < 0: # if wrap_position < 0:
# Partial segment at start # # Partial segment at start
segment_end = min(wrap_position + segment_length, self.num_leds) # segment_end = min(wrap_position + segment_length, self.num_leds)
for i in range(0, segment_end): # for i in range(0, segment_end):
self.n[i] = color # self.n[i] = color
wrap_position -= segment_width # wrap_position -= segment_width
self.n.write() # self.n.write()
return self.delay # return self.delay
except Exception as e: # except Exception as e:
# DEBUG: Print error # # DEBUG: Print error
print(f"SM Error: {e}") # print(f"SM Error: {e}")
# If anything goes wrong, turn off LEDs and return # # If anything goes wrong, turn off LEDs and return
self.fill((0, 0, 0)) # self.fill((0, 0, 0))
self.n.write() # self.n.write()
return self.delay # return self.delay
if __name__ == "__main__": # if __name__ == "__main__":
import time # import time
from machine import WDT # from machine import WDT
wdt = WDT(timeout=2000) # Enable watchdog with a 2 second timeout # wdt = WDT(timeout=2000) # Enable watchdog with a 2 second timeout
p = Patterns(pin=4, num_leds=60, color1=(255,0,0), color2=(0,0,255), brightness=127, selected="off", delay=100) # p = Patterns(pin=4, num_leds=60, color1=(255,0,0), color2=(0,0,255), brightness=127, selected="off", delay=100)
print(p.colors, p.brightness) # print(p.colors, p.brightness)
tests = [ # tests = [
("off", {"duration_ms": 500}), # ("off", {"duration_ms": 500}),
("on", {"duration_ms": 500}), # ("on", {"duration_ms": 500}),
("color_wipe", {"delay": 200, "duration_ms": 1000}), # ("color_wipe", {"delay": 200, "duration_ms": 1000}),
("rainbow_cycle", {"delay": 100, "duration_ms": 2500}), # ("rainbow_cycle", {"delay": 100, "duration_ms": 2500}),
("theater_chase", {"on_width": 3, "off_width": 3, "delay": 1000, "duration_ms": 2500}), # ("theater_chase", {"on_width": 3, "off_width": 3, "delay": 1000, "duration_ms": 2500}),
("blink", {"delay": 500, "duration_ms": 2000}), # ("blink", {"delay": 500, "duration_ms": 2000}),
("color_transition", {"delay": 150, "colors": [(255,0,0),(0,255,0),(0,0,255)], "duration_ms": 5000}), # ("color_transition", {"delay": 150, "colors": [(255,0,0),(0,255,0),(0,0,255)], "duration_ms": 5000}),
("flicker", {"delay": 100, "duration_ms": 2000}), # ("flicker", {"delay": 100, "duration_ms": 2000}),
("scanner", {"delay": 150, "duration_ms": 2500}), # ("scanner", {"delay": 150, "duration_ms": 2500}),
("bidirectional_scanner", {"delay": 50, "duration_ms": 2500}), # ("bidirectional_scanner", {"delay": 50, "duration_ms": 2500}),
("fill_range", {"n1": 10, "n2": 20, "delay": 500, "duration_ms": 2000}), # ("fill_range", {"n1": 10, "n2": 20, "delay": 500, "duration_ms": 2000}),
("n_chase", {"n1": 5, "n2": 5, "delay": 2000, "duration_ms": 2500}), # ("n_chase", {"n1": 5, "n2": 5, "delay": 2000, "duration_ms": 2500}),
("alternating", {"n1": 5, "n2": 5, "delay": 500, "duration_ms": 2500}), # ("alternating", {"n1": 5, "n2": 5, "delay": 500, "duration_ms": 2500}),
("pulse", {"delay": 100, "duration_ms": 700}), # ("pulse", {"delay": 100, "duration_ms": 700}),
] # ]
print("\n--- Running pattern self-test ---") # print("\n--- Running pattern self-test ---")
for name, cfg in tests: # for name, cfg in tests:
print(f"\nPattern: {name}") # print(f"\nPattern: {name}")
# apply simple config helpers # # apply simple config helpers
if "delay" in cfg: # if "delay" in cfg:
p.set_delay(cfg["delay"]) # p.set_delay(cfg["delay"])
if "on_width" in cfg: # if "on_width" in cfg:
p.set_on_width(cfg["on_width"]) # p.set_on_width(cfg["on_width"])
if "off_width" in cfg: # if "off_width" in cfg:
p.set_off_width(cfg["off_width"]) # p.set_off_width(cfg["off_width"])
if "n1" in cfg and "n2" in cfg: # if "n1" in cfg and "n2" in cfg:
p.set_fill_range(cfg["n1"], cfg["n2"]) # p.set_fill_range(cfg["n1"], cfg["n2"])
if "colors" in cfg: # if "colors" in cfg:
p.set_colors(cfg["colors"]) # p.set_colors(cfg["colors"])
p.select(name) # p.select(name)
# run per configured duration using absolute-scheduled tick(next_due_ms) # # run per configured duration using absolute-scheduled tick(next_due_ms)
start = utime.ticks_ms() # start = utime.ticks_ms()
duration_ms = cfg["duration_ms"] # duration_ms = cfg["duration_ms"]
delay = cfg.get("delay", 0) # delay = cfg.get("delay", 0)
next_due = utime.ticks_ms() - 1 # force immediate first call # next_due = utime.ticks_ms() - 1 # force immediate first call
while utime.ticks_diff(utime.ticks_ms(), start) < duration_ms: # while utime.ticks_diff(utime.ticks_ms(), start) < duration_ms:
delay = p.tick(delay) # delay = p.tick(delay)
wdt.feed() # wdt.feed()
print("\n--- Test routine finished ---") # print("\n--- Test routine finished ---")

40
test/blink.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
"""
Test script for blink pattern
Run with: mpremote run test/blink.py
"""
import patterns
import utime
def test_blink():
print("Testing blink pattern...")
# Initialize patterns with LED pin 10 and 59 LEDs at 20% brightness
p = patterns.Patterns(pin=10, num_leds=59, brightness=51, delay=500)
# Set a bright red color
p.colors = [(255, 0, 0)] # Red
print(f"LEDs: {p.num_leds}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Color: {p.colors[0]}")
# Test blink pattern
print("Starting blink pattern for 5 seconds...")
p.select("bl")
# Let it run for 5 seconds
utime.sleep(5)
# Stop the pattern
p.run = False
print("Blink test completed")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_blink()