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)