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:
2026-06-28 22:46:08 +12:00
parent d8323bb9a3
commit d5cab2efdf
52 changed files with 4677 additions and 1 deletions

69
leds/array_config.py Normal file
View File

@@ -0,0 +1,69 @@
"""LED matrix layout defaults."""
from __future__ import annotations
import os
MATRIX_WIDTH = 45
MATRIX_HEIGHT = 9
MATRIX_PIXELS = MATRIX_WIDTH * MATRIX_HEIGHT # 405
# Per-panel width (height is always 9 rows). Edit to match your hardware.
# Panel 4 may still be 38 or 39 — run: panel_sync_test.py --test width --panel-index 4
PANEL_HEIGHT = 9
PANEL_WIDTH_BY_INDEX: dict[int, int] = {
0: 39,
1: 45,
2: 45,
3: 45,
4: 39,
}
DEFAULT_PANEL_WIDTH = 45
PANEL_COUNT = 5
# SPI data line: bus 0 = GPIO 10 (MOSI), bus 1 = GPIO 20 (SPI1 MOSI)
MATRIX_SPI_BUS = 0
MATRIX_SPI_DEVICE = 0
# One continuous chain on a single data pin; rows all run left → right.
MATRIX_ROWS_PER_STRIP = None
MATRIX_SERPENTINE = "none" # use "rows" for zigzag / serpentine wiring
# Set True if DIN enters at the bottom row instead of the top.
MATRIX_FLIP_Y = False
MATRIX_BRIGHTNESS = 0.25
# LED wire order for direct SPI on Pi (not used for Pico UDP — firmware packs GRB).
WIRE_ORDER = os.environ.get("PORTAL_WIRE_ORDER", "grb").lower()
def panel_width(panel_index: int) -> int:
return PANEL_WIDTH_BY_INDEX.get(panel_index, DEFAULT_PANEL_WIDTH)
def panel_layout(panel_index: int) -> tuple[int, int]:
return panel_width(panel_index), PANEL_HEIGHT
def panel_pixel_count(panel_index: int) -> int:
w, h = panel_layout(panel_index)
return w * h
def matrix_pixel_index(
x: int,
y: int,
width: int = MATRIX_WIDTH,
height: int = MATRIX_HEIGHT,
*,
serpentine: str = MATRIX_SERPENTINE,
flip_y: bool = MATRIX_FLIP_Y,
) -> int:
"""Map (x, y) to strip index. Default: every row left → right."""
if flip_y:
y = height - 1 - y
col = x
if serpentine == "rows" and y % 2 == 1:
col = width - 1 - x
return y * width + col