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:
58
leds/backends/pio.py
Normal file
58
leds/backends/pio.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""WS2812 via Pi 5 kernel ws2812-pio driver (/dev/ledsN)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class PioBackend:
|
||||
def __init__(self, led_count: int, *, device: str = "/dev/leds0") -> None:
|
||||
self._led_count = led_count
|
||||
self._device = device
|
||||
self._path = Path(device)
|
||||
if not self._path.exists():
|
||||
raise FileNotFoundError(
|
||||
f"{device} not found — enable ws2812-pio in /boot/firmware/config.txt "
|
||||
"and reboot. Run: pipenv run python examples/setup_pi5_leds.py"
|
||||
)
|
||||
|
||||
@property
|
||||
def led_count(self) -> int:
|
||||
return self._led_count
|
||||
|
||||
@property
|
||||
def device(self) -> str:
|
||||
return self._device
|
||||
|
||||
def write_grb(self, grb_bytes: bytes) -> None:
|
||||
if len(grb_bytes) != self._led_count * 3:
|
||||
raise ValueError(
|
||||
f"expected {self._led_count * 3} bytes, got {len(grb_bytes)}"
|
||||
)
|
||||
|
||||
# Kernel driver expects RGBW uint32 little-endian per pixel (W=0 for RGB).
|
||||
frame = bytearray(self._led_count * 4)
|
||||
for i in range(self._led_count):
|
||||
base = i * 3
|
||||
g, r, b = grb_bytes[base], grb_bytes[base + 1], grb_bytes[base + 2]
|
||||
offset = i * 4
|
||||
frame[offset] = r
|
||||
frame[offset + 1] = g
|
||||
frame[offset + 2] = b
|
||||
frame[offset + 3] = 0
|
||||
|
||||
with open(self._path, "wb") as dev:
|
||||
dev.write(frame)
|
||||
|
||||
def clear(self) -> None:
|
||||
with open(self._path, "wb") as dev:
|
||||
dev.write(b"\x00" * (self._led_count * 4))
|
||||
|
||||
def close(self) -> None:
|
||||
self.clear()
|
||||
|
||||
def __enter__(self) -> PioBackend:
|
||||
return self
|
||||
|
||||
def __exit__(self, *_exc) -> None:
|
||||
self.close()
|
||||
Reference in New Issue
Block a user