from machine import Pin from neopixel import NeoPixel import utime import random import _thread import asyncio from patterns_base import Patterns as PatternsBase # 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", } 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.patterns = { "off": self.off, "on" : self.on, "blink": self.blink, "rainbow": self.rainbow, "pulse": self.pulse, } def blink(self): self.stopped = False self.running = True 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) self.running = False self.stopped = True def rainbow(self): self.stopped = False self.running = True step = self.pattern_step % 256 while self.running: 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) self.running = False self.stopped = True def pulse(self): self.stopped = False self.running = True self.off() # Get timing parameters with defaults if not set attack_ms = getattr(self, 'n1', 200) # Attack time in ms 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 while self.running: cycle_start = utime.ticks_ms() # Attack phase: fade from 0 to full brightness if attack_ms > 0: attack_start = utime.ticks_ms() last_update = attack_start while self.running and utime.ticks_diff(utime.ticks_ms(), attack_start) < attack_ms: now = utime.ticks_ms() if utime.ticks_diff(now, last_update) >= update_interval: elapsed = utime.ticks_diff(now, attack_start) brightness_factor = min(1.0, elapsed / attack_ms) color = tuple(int(c * brightness_factor) for c in base_color) self.fill(self.apply_brightness(color)) last_update = now # Hold phase: maintain full brightness if hold_ms > 0 and self.running: self.fill(self.apply_brightness(base_color)) hold_start = utime.ticks_ms() while self.running and utime.ticks_diff(utime.ticks_ms(), hold_start) < hold_ms: pass # Decay phase: fade from full brightness to 0 if decay_ms > 0: decay_start = utime.ticks_ms() last_update = decay_start while self.running and utime.ticks_diff(utime.ticks_ms(), decay_start) < decay_ms: now = utime.ticks_ms() if utime.ticks_diff(now, last_update) >= update_interval: elapsed = utime.ticks_diff(now, decay_start) brightness_factor = max(0.0, 1.0 - (elapsed / decay_ms)) color = tuple(int(c * brightness_factor) for c in base_color) self.fill(self.apply_brightness(color)) last_update = now # If delay is 0, run only once and exit if self.delay == 0: break # Ensure the cycle takes exactly delay milliseconds before restarting if self.running: self.off() wait_until = utime.ticks_add(cycle_start, self.delay) while self.running and utime.ticks_diff(wait_until, utime.ticks_ms()) > 0: pass self.running = False self.stopped = True