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>
102 lines
3.2 KiB
Python
Executable File
102 lines
3.2 KiB
Python
Executable File
#!/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())
|