Compare commits
2 Commits
059bd41a59
...
a0b85e57a1
| Author | SHA1 | Date | |
|---|---|---|---|
| a0b85e57a1 | |||
| 7547efe7fd |
232
src/patterns.py
232
src/patterns.py
@@ -1,4 +1,4 @@
|
||||
from machine import Pin
|
||||
from machine import Pin, WDT
|
||||
from neopixel import NeoPixel
|
||||
import utime
|
||||
import random
|
||||
@@ -14,17 +14,12 @@ class Patterns:
|
||||
self.patterns = {
|
||||
"off": self.off,
|
||||
"on" : self.on,
|
||||
"color_wipe": self.color_wipe_step,
|
||||
"rainbow_cycle": self.rainbow_cycle_step,
|
||||
"theater_chase": self.theater_chase_step,
|
||||
"blink": self.blink_step,
|
||||
"random_color_wipe": self.random_color_wipe_step,
|
||||
"random_rainbow_cycle": self.random_rainbow_cycle_step,
|
||||
"random_theater_chase": self.random_theater_chase_step,
|
||||
"random_blink": self.random_blink_step,
|
||||
"color_transition": self.color_transition_step, # Added new pattern
|
||||
"flicker": self.flicker_step,
|
||||
"external": None
|
||||
"radiate": self.radiate,
|
||||
}
|
||||
self.selected = selected
|
||||
# Ensure colors list always starts with at least two for robust transition handling
|
||||
@@ -40,6 +35,17 @@ class Patterns:
|
||||
|
||||
self.hold_start_time = utime.ticks_ms() # Time when the current color hold started
|
||||
|
||||
# Radiate pattern attributes
|
||||
self.n1 = 10 # Distance between nodes
|
||||
self.n2 = 0 # Unused
|
||||
self.n3 = 500 # Time to radiate out in ms
|
||||
self.n4 = 500 # Time to radiate in in ms
|
||||
self.run = True
|
||||
self.stopped = False
|
||||
self.debug = False
|
||||
self._last_debug_print_ms = 0
|
||||
self._wdt = WDT()
|
||||
|
||||
|
||||
def sync(self):
|
||||
self.pattern_step=0
|
||||
@@ -258,65 +264,6 @@ class Patterns:
|
||||
self.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
|
||||
def random_color_wipe_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
if self.pattern_step < self.num_leds:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n[self.pattern_step] = self.apply_brightness(color)
|
||||
self.n.write()
|
||||
self.pattern_step += 1
|
||||
else:
|
||||
self.pattern_step = 0
|
||||
self.last_update = current_time
|
||||
|
||||
def random_rainbow_cycle_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay: # Kept original delay for now
|
||||
def wheel(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)
|
||||
|
||||
random_offset = random.randint(0, 255)
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step + random_offset
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 256
|
||||
self.last_update = current_time
|
||||
|
||||
def random_theater_chase_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
for i in range(self.num_leds):
|
||||
if (i + self.pattern_step) % 3 == 0:
|
||||
self.n[i] = self.apply_brightness(color)
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 3
|
||||
self.last_update = current_time
|
||||
|
||||
def random_blink_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay*10:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
if self.pattern_step % 2 == 0:
|
||||
self.fill(self.apply_brightness(color))
|
||||
else:
|
||||
self.fill((0, 0, 0))
|
||||
self.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
|
||||
def color_transition_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
|
||||
@@ -377,3 +324,156 @@ class Patterns:
|
||||
flicker_color = self.apply_brightness(base_color, brightness_override=flicker_brightness)
|
||||
self.fill(flicker_color)
|
||||
self.last_update = current_time
|
||||
|
||||
def radiate(self):
|
||||
# Radiate pattern: creates radiating effects from multiple nodes
|
||||
# n1 = distance between nodes, n2 = time for pattern to complete in ms
|
||||
# Pattern repeats every self.delay milliseconds
|
||||
self.run = True
|
||||
last = utime.ticks_ms()
|
||||
accum = 0
|
||||
pattern_start = utime.ticks_ms()
|
||||
step = 0
|
||||
|
||||
if self.delay == 0:
|
||||
# Single-shot mode: run one complete cycle
|
||||
delay_ms = 20 # Use 20ms for smooth animation
|
||||
while self.run:
|
||||
now = utime.ticks_ms()
|
||||
dt = utime.ticks_diff(now, last)
|
||||
if dt < 0:
|
||||
dt = 0
|
||||
last = now
|
||||
accum += dt
|
||||
|
||||
if accum < delay_ms:
|
||||
continue
|
||||
accum = 0
|
||||
step += 1
|
||||
|
||||
# Clear all LEDs
|
||||
self.n.fill((0, 0, 0))
|
||||
|
||||
# Calculate pattern parameters
|
||||
n1 = self.n1 if self.n1 > 0 else 10 # Distance between nodes
|
||||
n3 = self.n3 if self.n3 > 0 else 500 # Time to radiate out in ms
|
||||
n4 = self.n4 if self.n4 > 0 else 500 # Time to radiate in in ms
|
||||
|
||||
# Calculate current time within the radiate cycle (out + in)
|
||||
elapsed = utime.ticks_diff(now, pattern_start)
|
||||
total_cycle_time = n3 + n4 if (n3 + n4) > 0 else 1
|
||||
cycle_time = elapsed % total_cycle_time
|
||||
|
||||
# Calculate nodes positions
|
||||
nodes = []
|
||||
for i in range(0, self.num_leds, n1):
|
||||
nodes.append(i)
|
||||
|
||||
# For each node, create radiating effect
|
||||
for node_pos in nodes:
|
||||
max_radius = min(node_pos, self.num_leds - node_pos - 1)
|
||||
if max_radius <= 0:
|
||||
continue
|
||||
if cycle_time <= n3 and n3 > 0:
|
||||
out_progress = cycle_time / n3
|
||||
wave_radius = int(out_progress * max_radius)
|
||||
else:
|
||||
in_time = cycle_time - n3
|
||||
in_progress = (in_time / n4) if n4 > 0 else 1.0
|
||||
wave_radius = int((1.0 - in_progress) * max_radius)
|
||||
for radius in range(wave_radius + 1):
|
||||
intensity = 1.0 - (radius / max_radius) if max_radius > 0 else 1.0
|
||||
if intensity <= 0:
|
||||
continue
|
||||
scaled_intensity = int(self.brightness * intensity)
|
||||
r = (self.colors[0][0] * scaled_intensity) // 255
|
||||
g = (self.colors[0][1] * scaled_intensity) // 255
|
||||
b = (self.colors[0][2] * scaled_intensity) // 255
|
||||
left_pos = node_pos - radius
|
||||
if left_pos >= 0:
|
||||
self.n[left_pos] = (r, g, b)
|
||||
right_pos = node_pos + radius
|
||||
if right_pos < self.num_leds:
|
||||
self.n[right_pos] = (r, g, b)
|
||||
|
||||
self.n.write()
|
||||
self._wdt.feed()
|
||||
|
||||
# Single-shot: stop after one full out+in cycle
|
||||
if elapsed >= total_cycle_time:
|
||||
self.run = False
|
||||
self.n.fill((0, 0, 0))
|
||||
self.n.write()
|
||||
self.stopped = True
|
||||
return
|
||||
else:
|
||||
# Auto mode: restart wave every delay_ms
|
||||
delay_ms = self.delay
|
||||
while self.run:
|
||||
now = utime.ticks_ms()
|
||||
dt = utime.ticks_diff(now, last)
|
||||
if dt < 0:
|
||||
dt = 0
|
||||
last = now
|
||||
accum += dt
|
||||
|
||||
if accum < delay_ms:
|
||||
continue
|
||||
accum = 0
|
||||
step += 1
|
||||
|
||||
# Clear all LEDs
|
||||
self.n.fill((0, 0, 0))
|
||||
|
||||
# Calculate pattern parameters
|
||||
n1 = self.n1 if self.n1 > 0 else 10 # Distance between nodes
|
||||
n3 = self.n3 if self.n3 > 0 else 500 # Time to radiate out in ms
|
||||
n4 = self.n4 if self.n4 > 0 else 500 # Time to radiate in in ms
|
||||
|
||||
# Calculate current time within the radiate cycle (out + in)
|
||||
elapsed = utime.ticks_diff(now, pattern_start)
|
||||
total_cycle_time = n3 + n4 if (n3 + n4) > 0 else 1
|
||||
cycle_time = elapsed % total_cycle_time
|
||||
|
||||
# Calculate nodes positions
|
||||
nodes = []
|
||||
for i in range(0, self.num_leds, n1):
|
||||
nodes.append(i)
|
||||
|
||||
# For each node, create radiating effect
|
||||
for node_pos in nodes:
|
||||
max_radius = min(node_pos, self.num_leds - node_pos - 1)
|
||||
if max_radius <= 0:
|
||||
continue
|
||||
if cycle_time <= n3 and n3 > 0:
|
||||
out_progress = cycle_time / n3
|
||||
wave_radius = int(out_progress * max_radius)
|
||||
else:
|
||||
in_time = cycle_time - n3
|
||||
in_progress = (in_time / n4) if n4 > 0 else 1.0
|
||||
wave_radius = int((1.0 - in_progress) * max_radius)
|
||||
for radius in range(wave_radius + 1):
|
||||
intensity = 1.0 - (radius / max_radius) if max_radius > 0 else 1.0
|
||||
if intensity <= 0:
|
||||
continue
|
||||
scaled_intensity = int(self.brightness * intensity)
|
||||
r = (self.colors[0][0] * scaled_intensity) // 255
|
||||
g = (self.colors[0][1] * scaled_intensity) // 255
|
||||
b = (self.colors[0][2] * scaled_intensity) // 255
|
||||
left_pos = node_pos - radius
|
||||
if left_pos >= 0:
|
||||
self.n[left_pos] = (r, g, b)
|
||||
right_pos = node_pos + radius
|
||||
if right_pos < self.num_leds:
|
||||
self.n[right_pos] = (r, g, b)
|
||||
|
||||
self.n.write()
|
||||
self._wdt.feed()
|
||||
|
||||
# Auto mode: restart the wave every delay_ms
|
||||
if elapsed >= delay_ms:
|
||||
if self.debug:
|
||||
print("[radiate] restart cycle: delay=", delay_ms, "elapsed=", elapsed, "step=", step)
|
||||
pattern_start = now
|
||||
|
||||
self.stopped = True
|
||||
|
||||
Reference in New Issue
Block a user