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:
101
examples/panel_text.py
Executable file
101
examples/panel_text.py
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Display text on Pico panel(s) over UDP."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
from leds.panel_udp import (
|
||||
HeadlessMatrix,
|
||||
add_panel_args,
|
||||
format_panel_targets,
|
||||
panel_targets_from_args,
|
||||
send_frame,
|
||||
)
|
||||
from leds.text import draw_text, draw_text_centered
|
||||
|
||||
DEFAULT_NAME = "JIMMY"
|
||||
|
||||
|
||||
def _default_name() -> str:
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["git", "config", "user.name"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
name = out.stdout.strip()
|
||||
if name:
|
||||
return name.upper()
|
||||
except OSError:
|
||||
pass
|
||||
return DEFAULT_NAME
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Display text on LED panel(s)")
|
||||
parser.add_argument("text", nargs="?", default=None)
|
||||
add_panel_args(parser)
|
||||
parser.add_argument("--color", default="255,200,80", metavar="R,G,B")
|
||||
parser.add_argument("--hold", type=float, default=0.0, help="0 = show until Ctrl+C")
|
||||
parser.add_argument("--scroll", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
text = (args.text or _default_name()).upper()
|
||||
parts = [int(p.strip()) for p in args.color.split(",")]
|
||||
if len(parts) != 3:
|
||||
print("Error: --color must be R,G,B", file=sys.stderr)
|
||||
return 1
|
||||
color = (parts[0], parts[1], parts[2])
|
||||
|
||||
targets = panel_targets_from_args(args)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
print(f"Displaying '{text}' on {format_panel_targets(targets, args.port)}")
|
||||
|
||||
try:
|
||||
matrices = [
|
||||
(target, HeadlessMatrix(target.width, target.height, brightness=args.brightness))
|
||||
for target in targets
|
||||
]
|
||||
if args.scroll:
|
||||
start = time.monotonic()
|
||||
offsets = {target.index: matrix.width for target, matrix in matrices}
|
||||
while True:
|
||||
for target, matrix in matrices:
|
||||
matrix.clear()
|
||||
draw_text(matrix, text, offsets[target.index], 1, color)
|
||||
send_frame(sock, target.host, args.port, matrix.rgb_bytes(), args.panel_id)
|
||||
offsets[target.index] -= 1
|
||||
if offsets[target.index] < -len(text) * 6:
|
||||
offsets[target.index] = matrix.width
|
||||
time.sleep(0.08)
|
||||
if args.hold > 0 and time.monotonic() - start >= args.hold:
|
||||
break
|
||||
else:
|
||||
for target, matrix in matrices:
|
||||
matrix.clear()
|
||||
draw_text_centered(matrix, text, color)
|
||||
send_frame(sock, target.host, args.port, matrix.rgb_bytes(), args.panel_id)
|
||||
if args.hold <= 0:
|
||||
print("Showing (Ctrl+C to clear)")
|
||||
while True:
|
||||
time.sleep(1)
|
||||
else:
|
||||
time.sleep(args.hold)
|
||||
except KeyboardInterrupt:
|
||||
print("\nClearing.")
|
||||
finally:
|
||||
for target, matrix in matrices:
|
||||
matrix.clear()
|
||||
send_frame(sock, target.host, args.port, matrix.rgb_bytes(), args.panel_id)
|
||||
sock.close()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user