Add multi-panel Pico UDP firmware and Python LED control.
Pico panels get static IPs on 10.1.1.10–14 with per-panel LED counts, Makefile deploy targets, and Python examples for animations, sync tests, and direct Pi SPI control. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
99
examples/led_demo.py
Executable file
99
examples/led_demo.py
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Two strips showing different patterns at the same time."""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import time
|
||||
|
||||
from leds import LedStrips, board_family, board_model, default_dual_strip_configs, pi5_setup_hint
|
||||
|
||||
|
||||
def _describe(cfg) -> str:
|
||||
if cfg.backend == "pio":
|
||||
return f"GPIO {cfg.pin} → {cfg.device}"
|
||||
if cfg.backend == "ws281x":
|
||||
return f"GPIO {cfg.pin} (channel {cfg.channel})"
|
||||
return f"/dev/spidev{cfg.spi_bus}.{cfg.spi_device}"
|
||||
|
||||
|
||||
def _wheel(pos: int) -> tuple[int, int, int]:
|
||||
pos = pos % 256
|
||||
if pos < 85:
|
||||
return (255 - pos * 3, pos * 3, 0)
|
||||
if pos < 170:
|
||||
pos -= 85
|
||||
return (0, 255 - pos * 3, pos * 3)
|
||||
pos -= 170
|
||||
return (pos * 3, 0, 255 - pos * 3)
|
||||
|
||||
|
||||
def chase(strip, color: tuple[int, int, int], step: int) -> None:
|
||||
count = len(strip)
|
||||
strip.fill((0, 0, 0))
|
||||
strip[step % count] = color
|
||||
|
||||
|
||||
def rainbow(strip, offset: int) -> None:
|
||||
for i in range(len(strip)):
|
||||
strip[i] = _wheel(offset + i * 256 // max(len(strip), 1))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="WS2812 dual-strip demo")
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--count",
|
||||
type=int,
|
||||
nargs=2,
|
||||
default=[200, 200],
|
||||
metavar=("STRIP0", "STRIP1"),
|
||||
help="LED count per strip (default: 10 10)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--brightness",
|
||||
type=float,
|
||||
default=0.25,
|
||||
help="brightness 0.0-1.0 (default: 0.25)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"Board: {board_model()}")
|
||||
cfg0, cfg1 = default_dual_strip_configs(
|
||||
args.count[0], args.count[1], brightness=args.brightness
|
||||
)
|
||||
print(f"Strip 0: {cfg0.backend} → {_describe(cfg0)} (red chase)")
|
||||
print(f"Strip 1: {cfg1.backend} → {_describe(cfg1)} (rainbow)")
|
||||
|
||||
step = 0
|
||||
try:
|
||||
with LedStrips(args.count[0], args.count[1], brightness=args.brightness) as strips:
|
||||
while True:
|
||||
# strip 0: red dot chasing along the strip
|
||||
chase(strips[0], (255, 0, 0), step)
|
||||
|
||||
# strip 1: rainbow scroll — completely independent
|
||||
rainbow(strips[1], step * 4)
|
||||
|
||||
strips.show()
|
||||
step += 1
|
||||
time.sleep(0.05)
|
||||
except KeyboardInterrupt:
|
||||
print("\nStopped.")
|
||||
return 0
|
||||
except FileNotFoundError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
if board_family() == "pi5":
|
||||
print("\n" + pi5_setup_hint(args.count[0], args.count[1]), file=sys.stderr)
|
||||
return 1
|
||||
except OSError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
if board_family() == "pi5" and "No such file" in str(exc):
|
||||
print("\n" + pi5_setup_hint(args.count[0], args.count[1]), file=sys.stderr)
|
||||
return 1
|
||||
except Exception as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user