Bring the five-panel hex portal online with a browser 3D/schematic preview, Pi SPI backends, and renamed multi-panel Pico UDP firmware. Co-authored-by: Cursor <cursoragent@cursor.com>
55 lines
1.4 KiB
Python
55 lines
1.4 KiB
Python
"""WS2812 over SPI via the rpi5-ws2812 package (Pi 5, GPIO 10)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
from rpi5_ws2812.ws2812 import WS2812SpiDriver
|
|
|
|
from leds.backends.spi import spidev_bufsize_hint
|
|
|
|
|
|
class Rpi5Ws2812Backend:
|
|
def __init__(
|
|
self,
|
|
led_count: int,
|
|
*,
|
|
spi_bus: int = 0,
|
|
spi_device: int = 0,
|
|
max_speed_hz: int = 4_200_000,
|
|
) -> None:
|
|
hint = spidev_bufsize_hint(led_count)
|
|
if hint:
|
|
raise OSError(hint)
|
|
|
|
self._led_count = led_count
|
|
self._driver = WS2812SpiDriver(spi_bus, spi_device, led_count)
|
|
self._driver._device.max_speed_hz = max_speed_hz
|
|
|
|
@property
|
|
def led_count(self) -> int:
|
|
return self._led_count
|
|
|
|
def write_grb(self, rgb_bytes: bytes) -> None:
|
|
"""RGB bytes passed through to SPI unchanged."""
|
|
if len(rgb_bytes) != self._led_count * 3:
|
|
raise ValueError(
|
|
f"expected {self._led_count * 3} bytes, got {len(rgb_bytes)}"
|
|
)
|
|
buf = np.frombuffer(rgb_bytes, dtype=np.uint8).reshape(self._led_count, 3)
|
|
self._driver.write(buf)
|
|
|
|
def clear(self) -> None:
|
|
self._driver.clear()
|
|
|
|
def close(self) -> None:
|
|
try:
|
|
self.clear()
|
|
finally:
|
|
self._driver._device.close()
|
|
|
|
def __enter__(self) -> Rpi5Ws2812Backend:
|
|
return self
|
|
|
|
def __exit__(self, *_exc) -> None:
|
|
self.close()
|