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>
This commit is contained in:
2026-07-30 14:54:51 +12:00
parent d5cab2efdf
commit 5094c7bcee
78 changed files with 62251 additions and 832 deletions

114
examples/spi_rgb_test.py Normal file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""Full-panel red / green / blue over SPI (GPIO 10 on Pi 5)."""
from __future__ import annotations
import argparse
import os
import sys
import time
from leds.array_config import (
MATRIX_BRIGHTNESS,
MATRIX_SPI_BUS,
MATRIX_SPI_DEVICE,
panel_layout,
panel_pixel_count,
)
from leds.colors import BLUE, GREEN, OFF, RED, WHITE
from leds.config import panel_strip_config, spi_strip_config
from leds.matrix import LedMatrix
COLORS = (
("RED", RED),
("GREEN", GREEN),
("BLUE", BLUE),
("WHITE", WHITE),
("OFF", OFF),
)
def main() -> int:
parser = argparse.ArgumentParser(
description="R/G/B color test on a WS2812 strip via SPI"
)
parser.add_argument(
"--panel-index",
type=int,
default=2,
choices=range(5),
metavar="N",
help="Panel layout for width×height (default: 2 = top / Pi)",
)
parser.add_argument(
"--count",
type=int,
default=None,
help="LED count override (default: panel width × 9)",
)
parser.add_argument("--brightness", type=float, default=MATRIX_BRIGHTNESS)
parser.add_argument(
"--spi-bus",
type=int,
default=int(os.environ.get("PORTAL_SPI_BUS", MATRIX_SPI_BUS)),
)
parser.add_argument("--spi-device", type=int, default=MATRIX_SPI_DEVICE)
parser.add_argument(
"--spi-mhz",
type=float,
default=4.2,
help="SPI clock in MHz (default: 4.2)",
)
parser.add_argument("--pause", type=float, default=1.5)
args = parser.parse_args()
width, height = panel_layout(args.panel_index)
pixels = args.count if args.count is not None else panel_pixel_count(args.panel_index)
spi_hz = max(1, int(args.spi_mhz * 1_000_000))
if args.count is not None:
if pixels % width != 0:
parser.error(f"--count {pixels} is not a multiple of width {width}")
height = pixels // width
cfg = spi_strip_config(
pixels,
spi_bus=args.spi_bus,
spi_device=args.spi_device,
spi_max_speed_hz=spi_hz,
brightness=args.brightness,
passthrough=True,
swap_rg=True,
)
else:
cfg = panel_strip_config(
args.panel_index,
pixels,
spi_max_speed_hz=spi_hz,
brightness=args.brightness,
passthrough=True,
swap_rg=True,
)
print(
f"SPI RGB test: {width}×{height} ({pixels} LEDs), "
f"bus {args.spi_bus}, {args.spi_mhz:g} MHz, passthrough RGB"
)
try:
with LedMatrix(width, height, config0=cfg) as matrix:
for label, color in COLORS:
print(f" {label}")
matrix.fill(color)
matrix.show()
time.sleep(args.pause)
except KeyboardInterrupt:
print("\nStopped.")
except OSError as exc:
print(f"SPI error: {exc}", file=sys.stderr)
print("Enable SPI and wire DIN → GPIO 10 (/dev/spidev0.0).", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())