3 Commits

Author SHA1 Message Date
10b6e5ee89 Update patterns.py 2025-11-30 17:42:06 +13:00
33aee7a3af Update patterns_base.py 2025-11-30 17:41:32 +13:00
501fd37def Update dev.py 2025-11-30 17:40:43 +13:00
4 changed files with 96 additions and 44 deletions

2
dev.py
View File

@@ -28,6 +28,8 @@ for cmd in sys.argv[1:]:
if ser.in_waiting > 0: # Check if there is data in the buffer
data = ser.readline().decode('utf-8').strip() # Read and decode the data
print(data)
case "clean":
subprocess.call(["mpremote", "connect", port, "fs", "rm", ":/settings.json"])

View File

@@ -37,6 +37,7 @@ class Patterns(PatternsBase):
"rainbow": self.rainbow,
"pulse": self.pulse,
"transition": self.transition,
"chase": self.n_chase,
"n_chase": self.n_chase,
"circle": self.circle,
}
@@ -48,7 +49,8 @@ class Patterns(PatternsBase):
state = True # True = on, False = off
last_update = utime.ticks_ms()
while self.running:
# Only continue running this pattern while it is the selected one
while self.running and self.selected == "blink":
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, last_update) >= self.delay:
if state:
@@ -80,11 +82,12 @@ class Patterns(PatternsBase):
return
# Auto is True: run continuously
sleep_ms = max(1, int(self.delay))
last_update = utime.ticks_ms()
while self.running:
# Only continue running this pattern while it is the selected one
while self.running and self.selected == "rainbow":
current_time = utime.ticks_ms()
sleep_ms = max(1, int(self.delay)) # Access delay directly
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
@@ -118,7 +121,8 @@ class Patterns(PatternsBase):
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:
# Only continue running this pattern while it is the selected one
while self.running and self.selected == "pulse":
cycle_start = utime.ticks_ms()
# Get the current color from the cycle
@@ -167,7 +171,8 @@ class Patterns(PatternsBase):
# Ensure the cycle takes exactly delay milliseconds before restarting
if self.running:
self.off()
wait_until = utime.ticks_add(cycle_start, self.delay)
delay_ms = int(self.delay) # Access delay directly
wait_until = utime.ticks_add(cycle_start, delay_ms)
while self.running and utime.ticks_diff(wait_until, utime.ticks_ms()) > 0:
pass
@@ -189,7 +194,7 @@ class Patterns(PatternsBase):
if len(self.colors) == 1:
# Only one color, just stay that color
last_update = utime.ticks_ms()
while self.running:
while self.running and self.selected == "transition":
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, last_update) >= 100:
self.fill(self.apply_brightness(self.colors[0]))
@@ -206,17 +211,18 @@ class Patterns(PatternsBase):
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:
while self.running:
# Access delay and colors directly for live updates
transition_duration = max(10, int(self.delay)) # At least 10ms
update_interval = max(10, transition_duration // 50) # Update every ~2% of transition
color1 = self.colors[0] if len(self.colors) > 0 else (0, 0, 0)
color2 = self.colors[1] if len(self.colors) > 1 else color1
if utime.ticks_diff(utime.ticks_ms(), transition_start) >= transition_duration:
break
now = utime.ticks_ms()
if utime.ticks_diff(now, last_update) >= update_interval:
# Calculate interpolation factor (0.0 to 1.0)
@@ -239,10 +245,12 @@ class Patterns(PatternsBase):
# 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:
# Auto is True: cycle through all colors continuously
while self.running and self.selected == "transition":
# Access colors directly for live updates
if not self.colors:
break
# Get current and next color
current_color = self.colors[color_index % len(self.colors)]
next_color = self.colors[(color_index + 1) % len(self.colors)]
@@ -251,7 +259,13 @@ class Patterns(PatternsBase):
transition_start = utime.ticks_ms()
last_update = transition_start
while self.running and utime.ticks_diff(utime.ticks_ms(), transition_start) < transition_duration:
while self.running:
# Access delay directly for live updates
transition_duration = max(10, int(self.delay)) # At least 10ms
update_interval = max(10, transition_duration // 50) # Update every ~2% of transition
if utime.ticks_diff(utime.ticks_ms(), transition_start) >= transition_duration:
break
now = utime.ticks_ms()
if utime.ticks_diff(now, last_update) >= update_interval:
# Calculate interpolation factor (0.0 to 1.0)
@@ -280,28 +294,43 @@ class Patterns(PatternsBase):
self.stopped = False
self.running = True
if len(self.colors) < 2:
# Need at least 2 colors
if len(self.colors) < 1:
# Need at least 1 color
self.running = False
self.stopped = True
return
n1 = max(1, int(self.n1)) # LEDs of color 0
n2 = max(1, int(self.n2)) # LEDs of color 1
n3 = int(self.n3) # Step movement on odd steps (can be negative)
n4 = int(self.n4) # Step movement on even steps (can be negative)
segment_length = n1 + n2
segment_length = 0 # Will be calculated in loop
position = 0 # Current position offset
step_count = 0 # Track which step we're on
color0 = self.apply_brightness(self.colors[0])
color1 = self.apply_brightness(self.colors[1])
transition_duration = max(10, self.delay)
last_update = utime.ticks_ms()
while self.running:
# Only continue running this pattern while it is the selected one
# Note: this pattern can be selected as "n_chase" or "chase"
while self.running and self.selected in ("n_chase", "chase"):
# Access colors, delay, and n values directly for live updates
if not self.colors:
break
# If only one color provided, use it for both colors
if len(self.colors) < 2:
color0 = self.colors[0]
color1 = self.colors[0]
else:
color0 = self.colors[0]
color1 = self.colors[1]
color0 = self.apply_brightness(color0)
color1 = self.apply_brightness(color1)
n1 = max(1, int(self.n1)) # LEDs of color 0
n2 = max(1, int(self.n2)) # LEDs of color 1
n3 = int(self.n3) # Step movement on odd steps (can be negative)
n4 = int(self.n4) # Step movement on even steps (can be negative)
segment_length = n1 + n2
transition_duration = max(10, int(self.delay))
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, last_update) >= transition_duration:
# Clear all LEDs
@@ -361,7 +390,8 @@ class Patterns(PatternsBase):
phase = "growing" # "growing", "shrinking", or "off"
while self.running:
# Only continue running this pattern while it is the selected one
while self.running and self.selected == "circle":
current_time = utime.ticks_ms()
# Clear all LEDs

View File

@@ -5,7 +5,6 @@ import random
import _thread
import asyncio
import json
from presets import Presets
# Short-key parameter mapping for convenience setters
param_mapping = {
@@ -27,7 +26,7 @@ param_mapping = {
}
class Patterns:
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="off", delay=100):
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
self.num_leds = num_leds
self.pattern_step = 0
@@ -64,28 +63,44 @@ class Patterns:
self.n6 = 0
def select(self, pattern):
if pattern in self.patterns:
self.selected = pattern
return True
# If pattern doesn't exist, default to "off"
if "off" in self.patterns:
self.selected = "off"
return False
async def run(self):
print(f"Stopping pattern")
await self.stop()
self.running = True
# Ensure we wait a bit more to let the thread fully terminate
# If selected pattern doesn't exist, default to "off"
if self.selected not in self.patterns:
print(f"Pattern {self.selected} not found, defaulting to 'off'")
if "off" in self.patterns:
self.selected = "off"
else:
print("No patterns available")
self.running = False
self.stopped = True
return
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")
_thread.start_new_thread(self.patterns[self.selected], ())
async def stop(self):
if not self.running:
# Already stopped
self.stopped = True
return
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
timeout = 2000 # Increased timeout to 2 seconds
while not self.stopped and utime.ticks_diff(utime.ticks_ms(), start) < timeout:
await asyncio.sleep_ms(10) # Check every 10ms instead of 0ms
if not self.stopped:
# Timeout reached, force stop
print("Warning: Pattern did not stop within timeout")
self.stopped = True
def set_param(self, key, value):
if key in param_mapping:

View File

@@ -136,3 +136,8 @@ if __name__ == "__main__":
asyncio.run(main())