led-bar/src/patterns.py

329 lines
13 KiB
Python

from machine import Pin
from neopixel import NeoPixel
import utime
import random
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.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,
"external": None
}
self.selected = selected
self.colors = [color1, color2]
self.transition_duration = delay * 10 # Default transition duration is 10 times the delay
self.transition_step = 0
self.current_color = self.colors[0]
# New: Track the current index for color transitions
self.current_color_idx = 0
def sync(self):
self.pattern_step=0
self.last_update = utime.ticks_ms() - self.delay
self.tick()
def set_pattern_step(self, step):
self.pattern_step = step
def tick(self):
if self.patterns[self.selected]:
self.patterns[self.selected]()
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_delay(self, delay):
self.delay = delay
# Update transition duration when delay changes for color_transition pattern
if self.selected == "color_transition":
self.transition_duration = self.delay * 10 # Or some other multiplier
def set_brightness(self, brightness):
self.brightness = brightness
def set_color1(self, color):
self.colors[0] = color
if self.selected == "color_transition":
# Restart transition if color 0 (start color) is changed
self.transition_step = 0
self.current_color_idx = 0 # Ensure we start from the new color[0]
self.current_color = self.colors[0]
def set_color2(self, color):
self.colors[1] = color
if self.selected == "color_transition":
# No direct effect on current_color here, but transition will eventually use it
pass
def set_colors(self, colors):
self.colors = colors
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
if self.selected == "color_transition":
current_from_idx = self.current_color_idx
current_to_idx = (self.current_color_idx + 1) % len(self.colors)
if num == current_from_idx or num == current_to_idx:
self.transition_step = 0
# Optionally reset current_color_idx if num is the start color
if num == current_from_idx:
self.current_color_idx = num
self.current_color = self.colors[num]
return True
elif num == len(self.colors): # Allow setting a new color at the end
self.colors.append(color)
return True
return False
def add_color(self, color):
self.colors.append(color)
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]
# If the color being deleted was part of the current transition,
# re-evaluate the current_color_idx
if self.selected == "color_transition":
if len(self.colors) < 2: # Need at least two colors for transition
self.select("off") # Or some other default
else:
self.current_color_idx %= len(self.colors) # Adjust index if it's out of bounds
self.transition_step = 0
self.current_color = self.colors[self.current_color_idx]
return True
return False
def apply_brightness(self, color):
return tuple(int(c * self.brightness / 255) for c in color)
def select(self, pattern):
if pattern in self.patterns:
self.selected = pattern
self.sync() # Reset pattern state when selecting a new pattern
if pattern == "color_transition":
if len(self.colors) < 2:
print("Warning: 'color_transition' requires at least two colors. Switching to 'on'.")
self.selected = "on" # Fallback if not enough colors
self.sync() # Re-sync for the new pattern
else:
self.transition_step = 0
self.current_color_idx = 0 # Start from the first color in the list
self.current_color = self.colors[self.current_color_idx]
self.transition_duration = self.delay * 10 # Initialize transition duration
return True
return False
def set(self, i, color):
self.n[i] = color
def write(self):
self.n.write()
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 color_wipe_step(self):
color = self.apply_brightness(self.colors[0])
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
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 rainbow_cycle_step(self):
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, self.last_update) >= self.delay/5:
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)
for i in range(self.num_leds):
rc_index = (i * 256 // self.num_leds) + self.pattern_step
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 theater_chase_step(self):
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
for i in range(self.num_leds):
if (i + self.pattern_step) % 3 == 0:
self.n[i] = self.apply_brightness(self.colors[0])
else:
self.n[i] = (0, 0, 0)
self.n.write()
self.pattern_step = (self.pattern_step + 1) % 3
self.last_update = current_time
def blink_step(self):
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
if self.pattern_step % 2 == 0:
self.fill(self.apply_brightness(self.colors[0]))
else:
self.fill((0, 0, 0))
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:
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 interpolate_color(self, color_a, color_b, factor):
"""Interpolates between two colors."""
return tuple(int(a + (b - a) * factor) for a, b in zip(color_a, color_b))
def color_transition_step(self):
current_time = utime.ticks_ms()
if len(self.colors) < 2:
# Not enough colors to transition, possibly switch to 'on' or 'off'
self.fill(self.apply_brightness(self.colors[0]))
return # Exit if there aren't enough colors
if utime.ticks_diff(current_time, self.last_update) >= 1: # Update frequently for smooth transition
self.transition_step += utime.ticks_diff(current_time, self.last_update)
self.last_update = current_time
color_from = self.colors[self.current_color_idx]
color_to_idx = (self.current_color_idx + 1) % len(self.colors)
color_to = self.colors[color_to_idx]
if self.transition_step >= self.transition_duration:
# Transition complete to the next color
self.current_color_idx = color_to_idx # Move to the next color in the sequence
self.transition_step = 0 # Reset transition step
# Calculate the interpolation factor (0 to 1)
factor = self.transition_step / self.transition_duration
# Get the interpolated color and apply brightness
interpolated_color = self.interpolate_color(color_from, color_to, factor)
self.current_color = self.apply_brightness(interpolated_color)
# Fill the LEDs with the current interpolated color
self.fill(self.current_color)
if __name__ == "__main__":
p = Patterns(4, 180)
p.set_color1((255,0,0))
p.set_color2((0,0,255)) # Blue
p.add_color((0,255,0)) # Green
p.add_color((255,255,0)) # Yellow
#p.set_delay(10)
try:
while True:
for key in p.patterns:
print(key)
p.select(key)
for _ in range(2000):
p.tick()
utime.sleep_ms(1)
except KeyboardInterrupt:
p.fill((0, 0, 0))