126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
import sys
|
|
# So "from ws2812 import WS2812B" finds pico/lib when run from device / or test/
|
|
if "lib" not in sys.path:
|
|
sys.path.insert(0, "lib")
|
|
if "../lib" not in sys.path:
|
|
sys.path.insert(0, "../lib")
|
|
from ws2812 import WS2812B
|
|
import time
|
|
|
|
# --- Rainbow pattern (outside ws2812): pregen double buffer, show via head offset ---
|
|
|
|
def hue_to_rgb(hue):
|
|
"""Hue 0..360 -> (r, g, b). Simple HSV with S=V=1."""
|
|
h = hue % 360
|
|
x = 1 - abs((h / 60) % 2 - 1)
|
|
if h < 60:
|
|
r, g, b = 1, x, 0
|
|
elif h < 120:
|
|
r, g, b = x, 1, 0
|
|
elif h < 180:
|
|
r, g, b = 0, 1, x
|
|
elif h < 240:
|
|
r, g, b = 0, x, 1
|
|
elif h < 300:
|
|
r, g, b = x, 0, 1
|
|
else:
|
|
r, g, b = 1, 0, x
|
|
return (int(r * 255), int(g * 255), int(b * 255))
|
|
|
|
|
|
def make_rainbow_ring(total_leds, brightness=1.0):
|
|
"""Build one rainbow over the whole ring: 2 full hue cycles over total_leds (GRB).
|
|
Returns (double_buf, ring_len_bytes). All strips sample from this so phase is continuous."""
|
|
n = 2 * total_leds
|
|
double_buf = bytearray(n * 3)
|
|
for i in range(n):
|
|
hue = ((i % total_leds) / total_leds) * 360 * 2
|
|
r, g, b = hue_to_rgb(hue)
|
|
g = int(g * brightness) & 0xFF
|
|
r = int(r * brightness) & 0xFF
|
|
b = int(b * brightness) & 0xFF
|
|
o = i * 3
|
|
double_buf[o] = g
|
|
double_buf[o + 1] = r
|
|
double_buf[o + 2] = b
|
|
ring_len_bytes = total_leds * 3
|
|
return (double_buf, ring_len_bytes)
|
|
|
|
|
|
def make_strip_rainbow(num_leds, cumulative_leds, total_ring_leds, brightness=1.0):
|
|
"""Per-strip double buffer: pixel j has hue at global position (cumulative_leds + j) % total_ring_leds.
|
|
Use same head for all strips: head = rainbow_head % (2*num_leds*3)."""
|
|
n = 2 * num_leds
|
|
buf = bytearray(n * 3)
|
|
for j in range(n):
|
|
global_pos = (cumulative_leds + j) % total_ring_leds
|
|
hue = (global_pos / total_ring_leds) * 360 * 2
|
|
r, g, b = hue_to_rgb(hue)
|
|
g = int(g * brightness) & 0xFF
|
|
r = int(r * brightness) & 0xFF
|
|
b = int(b * brightness) & 0xFF
|
|
o = j * 3
|
|
buf[o] = g
|
|
buf[o + 1] = r
|
|
buf[o + 2] = b
|
|
strip_len_bytes = num_leds * 3
|
|
return (buf, strip_len_bytes)
|
|
|
|
|
|
def show_rainbow_segment(strip, buf, strip_len_bytes, head):
|
|
"""DMA reads strip's segment from buf at head."""
|
|
strip.show(buf, head)
|
|
|
|
|
|
# --- Strips + one global ring rainbow (all strips in phase) ---
|
|
# Each strip can have a different length; one rainbow spans total_ring_leds so hue is continuous.
|
|
|
|
# (pin, num_leds) per strip — lengths differ per segment
|
|
STRIP_CONFIG = (
|
|
(2, 291),
|
|
(3, 290),
|
|
(4, 283),
|
|
(7, 278),
|
|
(0, 275),
|
|
(28, 278),
|
|
(29, 283),
|
|
(6, 290),
|
|
)
|
|
|
|
strips = []
|
|
sm = 0
|
|
for pin, num_leds in STRIP_CONFIG:
|
|
print(pin, num_leds)
|
|
ws = WS2812B(num_leds, pin, sm, brightness=1.0) # 1.0 so fill() is visible
|
|
strips.append(ws)
|
|
sm += 1
|
|
|
|
# Cumulative LED count before each strip; total ring size
|
|
cumulative_leds = [0]
|
|
for ws in strips[:-1]:
|
|
cumulative_leds.append(cumulative_leds[-1] + ws.num_leds)
|
|
total_ring_leds = cumulative_leds[-1] + strips[-1].num_leds
|
|
bytes_per_cycle = total_ring_leds * 3
|
|
|
|
# Per-strip rainbow buffers: each strip's segment of the ring (same phase, no shared-buffer DMA)
|
|
now = time.ticks_ms()
|
|
rainbow_data = [
|
|
make_strip_rainbow(ws.num_leds, cumulative_leds[i], total_ring_leds, ws.brightness)
|
|
for i, ws in enumerate(strips)
|
|
]
|
|
print(time.ticks_diff(time.ticks_ms(), now), "ms")
|
|
|
|
rainbow_head = 0
|
|
step = 3
|
|
|
|
while True:
|
|
now = time.ticks_ms()
|
|
for i, (strip, (buf, strip_len_bytes)) in enumerate(zip(strips, rainbow_data)):
|
|
# Same head for all: each strip's buffer is already offset by cumulative_leds[i]
|
|
double_len_bytes = 2 * strip.num_leds * 3
|
|
head = rainbow_head % double_len_bytes
|
|
show_rainbow_segment(strip, buf, strip_len_bytes, head)
|
|
rainbow_head = (rainbow_head + step) % bytes_per_cycle
|
|
#print(time.ticks_diff(time.ticks_ms(), now), "ms")
|
|
time.sleep_ms(10)
|