152 lines
4.7 KiB
Python
152 lines
4.7 KiB
Python
from machine import Pin
|
|
from neopixel import NeoPixel
|
|
import utime
|
|
import random
|
|
import _thread
|
|
import asyncio
|
|
import json
|
|
from presets import Presets
|
|
|
|
# Short-key parameter mapping for convenience setters
|
|
param_mapping = {
|
|
"pt": "selected",
|
|
"pa": "selected",
|
|
"cl": "colors",
|
|
"br": "brightness",
|
|
"dl": "delay",
|
|
"nl": "num_leds",
|
|
"co": "color_order",
|
|
"lp": "led_pin",
|
|
"n1": "n1",
|
|
"n2": "n2",
|
|
"n3": "n3",
|
|
"n4": "n4",
|
|
"n5": "n5",
|
|
"n6": "n6",
|
|
"auto": "auto",
|
|
}
|
|
|
|
class Patterns:
|
|
def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100):
|
|
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
|
self.num_leds = num_leds
|
|
self.pattern_step = 0
|
|
self.last_update = utime.ticks_ms()
|
|
self.delay = delay
|
|
self.brightness = brightness
|
|
self.auto = False
|
|
self.patterns = {}
|
|
self.selected = selected
|
|
# Ensure colors list always starts with at least two for robust transition handling
|
|
self.colors = [color1, color2] if color1 != color2 else [color1, (255, 255, 255)] # Fallback if initial colors are same
|
|
if not self.colors: # Ensure at least one color exists
|
|
self.colors = [(0, 0, 0)]
|
|
|
|
self.transition_duration = delay * 50 # Default transition duration
|
|
self.hold_duration = delay * 10 # Default hold duration at each color
|
|
self.transition_step = 0 # Current step in the transition
|
|
self.current_color_idx = 0 # Index of the color currently being held/transitioned from
|
|
self.current_color = self.colors[self.current_color_idx] # The actual blended color
|
|
|
|
self.hold_start_time = utime.ticks_ms() # Time when the current color hold started
|
|
|
|
# New attributes for scanner patterns
|
|
self.scanner_direction = 1 # 1 for forward, -1 for backward
|
|
self.scanner_tail_length = 3 # Number of trailing pixels
|
|
self.running = False
|
|
self.stopped = True
|
|
self.presets = Presets()
|
|
self.n1 = 0
|
|
self.n2 = 0
|
|
self.n3 = 0
|
|
self.n4 = 0
|
|
self.n5 = 0
|
|
self.n6 = 0
|
|
|
|
def select(self, pattern):
|
|
|
|
if pattern in self.patterns:
|
|
self.selected = pattern
|
|
return True
|
|
return False
|
|
|
|
async def run(self):
|
|
print(f"Stopping pattern")
|
|
await self.stop()
|
|
self.running = True
|
|
print(f"Starting pattern {self.selected}")
|
|
if self.selected in self.patterns:
|
|
_thread.start_new_thread(self.patterns[self.selected], ())
|
|
else:
|
|
print(f"Pattern {self.selected} not found")
|
|
|
|
async def stop(self):
|
|
self.running = False
|
|
start = utime.ticks_ms()
|
|
while not self.stopped and utime.ticks_diff(utime.ticks_ms(), start) < 1000:
|
|
await asyncio.sleep_ms(0)
|
|
self.stopped = True
|
|
|
|
def set_param(self, key, value):
|
|
if key in param_mapping:
|
|
setattr(self, param_mapping[key], value)
|
|
return True
|
|
print(f"Invalid parameter: {key}")
|
|
return False
|
|
|
|
def update_num_leds(self, pin, num_leds):
|
|
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
|
self.num_leds = num_leds
|
|
self.pattern_step = 0
|
|
|
|
|
|
def set_color(self, num, color):
|
|
# Changed: More robust index check
|
|
if 0 <= num < len(self.colors):
|
|
self.colors[num] = color
|
|
# If the changed color is part of the current or next transition,
|
|
# restart the transition for smoother updates
|
|
return True
|
|
elif num == len(self.colors): # Allow setting a new color at the end
|
|
self.colors.append(color)
|
|
return True
|
|
return False
|
|
|
|
|
|
def del_color(self, num):
|
|
# Changed: More robust index check and using del for lists
|
|
if 0 <= num < len(self.colors):
|
|
del self.colors[num]
|
|
return True
|
|
return False
|
|
|
|
def apply_brightness(self, color, brightness_override=None):
|
|
effective_brightness = brightness_override if brightness_override is not None else self.brightness
|
|
return tuple(int(c * effective_brightness / 255) for c in color)
|
|
|
|
def fill(self, color=None):
|
|
fill_color = color if color is not None else self.colors[0]
|
|
for i in range(self.num_leds):
|
|
self.n[i] = fill_color
|
|
self.n.write()
|
|
|
|
def off(self):
|
|
self.fill((0, 0, 0))
|
|
|
|
def on(self):
|
|
self.fill(self.apply_brightness(self.colors[0]))
|
|
|
|
|
|
|
|
|
|
def wheel(self, pos):
|
|
if pos < 85:
|
|
return (pos * 3, 255 - pos * 3, 0)
|
|
elif pos < 170:
|
|
pos -= 85
|
|
return (255 - pos * 3, 0, pos * 3)
|
|
else:
|
|
pos -= 170
|
|
return (0, pos * 3, 255 - pos * 3)
|
|
|
|
|