2 Commits

Author SHA1 Message Date
a0b85e57a1 Add simplified radiate pattern to dev branch
- Add radiate pattern to patterns dictionary
- Implement clean radiate function with single-shot and auto modes
- Add WDT import and radiate-specific attributes
- Single wave at a time in auto mode (no overlapping complexity)
- Simple debug output for restart timing
- Maintains same functionality with cleaner implementation
2025-10-24 20:32:31 +13:00
7547efe7fd Remove random patterns 2025-08-28 23:05:08 +12:00

View File

@@ -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
@@ -39,6 +34,17 @@ class Patterns:
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
# 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):
@@ -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