Files
portal/leds/config.py
Jimmy 5094c7bcee Add portal web simulator, SPI bridges, and Pico firmware updates.
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>
2026-07-30 14:54:51 +12:00

197 lines
5.2 KiB
Python

"""Default wiring for one or two WS2812 strips per board."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal
from leds.detect import board_family, pio_available, pio_device, pi5_second_spi_bus
try:
from leds.array_config import MATRIX_SPI_BUS, MATRIX_SPI_DEVICE
except ImportError:
MATRIX_SPI_BUS = 0
MATRIX_SPI_DEVICE = 0
BackendName = Literal["auto", "pio", "spi", "rpi5_ws2812", "ws281x"]
@dataclass
class StripConfig:
count: int
backend: BackendName = "auto"
pin: int = 18
channel: int = 0
device: str = "/dev/leds0"
spi_bus: int = 0
spi_device: int = 0
spi_max_speed_hz: int | None = None
wire_order: str | None = None
brightness: float = 1.0
passthrough: bool = False
swap_rg: bool = True
def panel_strip_config(
panel_index: int,
count: int,
*,
spi_max_speed_hz: int | None = None,
wire_order: str | None = None,
brightness: float = 1.0,
passthrough: bool = True,
swap_rg: bool = True,
) -> StripConfig:
from leds.array_config import LOCAL_PANEL_INDICES, PANEL_SPI_BUS_BY_INDEX
if panel_index in PANEL_SPI_BUS_BY_INDEX:
spi_bus = PANEL_SPI_BUS_BY_INDEX[panel_index]
elif panel_index in LOCAL_PANEL_INDICES:
spi_bus = pi5_second_spi_bus()
else:
spi_bus = MATRIX_SPI_BUS
return spi_strip_config(
count,
spi_bus=spi_bus,
spi_device=MATRIX_SPI_DEVICE,
spi_max_speed_hz=spi_max_speed_hz,
wire_order=wire_order,
brightness=brightness,
passthrough=passthrough,
swap_rg=swap_rg,
)
def spi_strip_config(
count: int,
*,
spi_bus: int = MATRIX_SPI_BUS,
spi_device: int = MATRIX_SPI_DEVICE,
spi_max_speed_hz: int | None = None,
wire_order: str | None = None,
brightness: float = 1.0,
backend: BackendName | None = None,
passthrough: bool = False,
swap_rg: bool = True,
) -> StripConfig:
if backend is None:
backend = "rpi5_ws2812" if board_family() == "pi5" else "spi"
return StripConfig(
count,
backend=backend,
spi_bus=spi_bus,
spi_device=spi_device,
spi_max_speed_hz=spi_max_speed_hz,
wire_order=wire_order,
brightness=brightness,
passthrough=passthrough,
swap_rg=swap_rg,
)
def default_strip_config(count: int, *, brightness: float = 1.0) -> StripConfig:
family = board_family()
if family == "pi5":
if pio_available():
return StripConfig(
count,
backend="pio",
pin=18,
device=pio_device(0) or "/dev/leds0",
brightness=brightness,
)
return spi_strip_config(count, brightness=brightness)
return StripConfig(
count,
backend="ws281x",
pin=18,
channel=0,
brightness=brightness,
)
def default_dual_strip_configs(
count0: int,
count1: int,
*,
brightness: float = 1.0,
) -> tuple[StripConfig, StripConfig]:
"""
Suggested wiring for two independent strips:
Pi 5 (SPI — requires dtoverlay=spi1-3cs under [all], not [cm5]):
strip 0 → GPIO 10 (SPI0 MOSI), /dev/spidev0.0
strip 1 → GPIO 20 (SPI1 MOSI), /dev/spidev1.0
Pi 5 (ws2812-pio — alternative):
strip 0 → GPIO 18, /dev/leds0
strip 1 → GPIO 13, /dev/leds1
Pi 4 / Zero:
strip 0 → GPIO 18 (PWM channel 0)
strip 1 → GPIO 13 (PWM channel 1)
"""
family = board_family()
if family == "pi5":
if pio_available():
return (
StripConfig(
count0,
backend="pio",
pin=18,
device=pio_device(0) or "/dev/leds0",
brightness=brightness,
),
StripConfig(
count1,
backend="pio",
pin=13,
device=pio_device(1) or "/dev/leds1",
brightness=brightness,
),
)
return (
StripConfig(
count0,
backend="spi",
spi_bus=0,
spi_device=0,
brightness=brightness,
),
StripConfig(
count1,
backend="spi",
spi_bus=pi5_second_spi_bus(),
spi_device=0,
brightness=brightness,
),
)
return (
StripConfig(
count0,
backend="ws281x",
pin=18,
channel=0,
brightness=brightness,
),
StripConfig(
count1,
backend="ws281x",
pin=13,
channel=1,
brightness=brightness,
),
)
def pi5_setup_hint(count0: int, count1: int) -> str:
return (
"Pi 5 second SPI bus not found. Add under [all] in /boot/firmware/config.txt:\n"
" dtparam=spi=on\n"
" dtoverlay=spi1-3cs\n"
"Remove dtparam=spi1=on (ignored on Pi 5). Do not put the overlay under [cm5]\n"
"unless you have a Compute Module 5.\n"
"Wire strip 0 DIN → GPIO 10, strip 1 DIN → GPIO 20, then reboot."
)