chat py
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
PIN = 18 # GPIO18 (hardware PWM) for WS2812B on RPi4
|
||||||
|
|
||||||
|
PANEL_PIXEL_COUNT = 16
|
||||||
|
PANEL_COUNT = 3
|
||||||
|
NUMPIXELS = PANEL_PIXEL_COUNT * PANEL_PIXEL_COUNT * PANEL_COUNT # 768
|
||||||
|
DISPLAY_MAX_X = PANEL_PIXEL_COUNT * PANEL_COUNT - 1 # 47
|
||||||
|
DISPLAY_MAX_Y = PANEL_PIXEL_COUNT - 1 # 15
|
||||||
|
|
||||||
|
TEXT_MAX_LENGTH = 64
|
||||||
|
MAX_TEXT_NODES_COUNT = 4
|
||||||
|
MAX_IMAGES_SAVED = 2
|
||||||
|
MAX_ANIMATION_FRAME_COUNT = 12
|
||||||
|
MAX_ANIMATIONS_COUNT = 2
|
||||||
|
|
||||||
|
# 0xFF111111 in NeoPixel = R=0x11, G=0x11, B=0x11 (W byte ignored on GRB strip)
|
||||||
|
VEHICLE_COLOR = (17, 17, 17)
|
||||||
|
|
||||||
|
SMALL_TEXT_HEIGHT = 7
|
||||||
|
SMALL_TEXT_WIDTH = 5
|
||||||
|
MEDIUM_TEXT_HEIGHT = 7
|
||||||
|
MEDIUM_TEXT_WIDTH = 5
|
||||||
|
|
||||||
|
PROGRAM2_TEXT_ITERATIONS = 5
|
||||||
|
PROGRAM2_TRIGGER_CHANCE = 25
|
||||||
|
PROGRAM2_VEHICLE_COUNT = 25
|
||||||
+196
@@ -0,0 +1,196 @@
|
|||||||
|
T = True
|
||||||
|
F = False
|
||||||
|
|
||||||
|
# font7x5[ascii - 32][row 0..6][col 0..4]
|
||||||
|
FONT_7X5 = [
|
||||||
|
# ' ' (32)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# ! (33)
|
||||||
|
[[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,F,F,F],[F,F,T,F,F]],
|
||||||
|
# " (34)
|
||||||
|
[[F,T,F,T,F],[F,T,F,T,F],[F,T,F,T,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# # (35)
|
||||||
|
[[F,T,F,T,F],[F,T,F,T,F],[T,T,T,T,T],[F,T,F,T,F],[T,T,T,T,T],[F,T,F,T,F],[F,T,F,T,F]],
|
||||||
|
# $ (36)
|
||||||
|
[[F,F,T,F,F],[F,T,T,T,T],[T,F,T,F,F],[F,T,T,T,F],[F,F,T,F,T],[T,T,T,T,F],[F,F,T,F,F]],
|
||||||
|
# % (37)
|
||||||
|
[[T,T,F,F,T],[T,T,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[T,F,F,T,T],[T,F,F,T,T]],
|
||||||
|
# & (38)
|
||||||
|
[[F,T,T,F,F],[T,F,F,T,F],[T,F,F,T,F],[F,T,T,F,F],[T,F,T,F,T],[T,F,F,T,F],[F,T,T,F,T]],
|
||||||
|
# ' (39)
|
||||||
|
[[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# ( (40)
|
||||||
|
[[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,F,T,F,F],[F,F,F,T,F]],
|
||||||
|
# ) (41)
|
||||||
|
[[F,T,F,F,F],[F,F,T,F,F],[F,F,F,T,F],[F,F,F,T,F],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F]],
|
||||||
|
# * (42)
|
||||||
|
[[F,F,F,F,F],[F,T,F,T,F],[F,F,T,F,F],[T,T,T,T,T],[F,F,T,F,F],[F,T,F,T,F],[F,F,F,F,F]],
|
||||||
|
# + (43)
|
||||||
|
[[F,F,F,F,F],[F,F,T,F,F],[F,F,T,F,F],[T,T,T,T,T],[F,F,T,F,F],[F,F,T,F,F],[F,F,F,F,F]],
|
||||||
|
# , (44)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,T,T,F],[F,F,T,T,F],[F,T,T,F,F]],
|
||||||
|
# - (45)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[T,T,T,T,T],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# . (46)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,T,T,F,F],[F,T,T,F,F]],
|
||||||
|
# / (47)
|
||||||
|
[[F,F,F,F,T],[F,F,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[T,F,F,F,F],[T,F,F,F,F]],
|
||||||
|
# 0 (48)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,T,T],[T,F,T,F,T],[T,T,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# 1 (49)
|
||||||
|
[[F,F,T,F,F],[F,T,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,T,T,T,F]],
|
||||||
|
# 2 (50)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[F,F,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[T,T,T,T,T]],
|
||||||
|
# 3 (51)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[F,F,F,F,T],[F,F,T,T,F],[F,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# 4 (52)
|
||||||
|
[[F,F,F,T,F],[F,F,T,T,F],[F,T,F,T,F],[T,F,F,T,F],[T,T,T,T,T],[F,F,F,T,F],[F,F,F,T,F]],
|
||||||
|
# 5 (53)
|
||||||
|
[[T,T,T,T,T],[T,F,F,F,F],[T,T,T,T,F],[F,F,F,F,T],[F,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# 6 (54)
|
||||||
|
[[F,F,T,T,F],[F,T,F,F,F],[T,F,F,F,F],[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# 7 (55)
|
||||||
|
[[T,T,T,T,T],[F,F,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,F]],
|
||||||
|
# 8 (56)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# 9 (57)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,T],[F,F,F,F,T],[F,F,F,T,F],[F,T,T,F,F]],
|
||||||
|
# : (58)
|
||||||
|
[[F,F,F,F,F],[F,T,T,F,F],[F,T,T,F,F],[F,F,F,F,F],[F,T,T,F,F],[F,T,T,F,F],[F,F,F,F,F]],
|
||||||
|
# ; (59)
|
||||||
|
[[F,F,F,F,F],[F,T,T,F,F],[F,T,T,F,F],[F,F,F,F,F],[F,F,T,T,F],[F,F,T,T,F],[F,T,T,F,F]],
|
||||||
|
# < (60)
|
||||||
|
[[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[T,F,F,F,F],[F,T,F,F,F],[F,F,T,F,F],[F,F,F,T,F]],
|
||||||
|
# = (61)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,T,T,T,T],[F,F,F,F,F],[T,T,T,T,T],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# > (62)
|
||||||
|
[[F,T,F,F,F],[F,F,T,F,F],[F,F,F,T,F],[F,F,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F]],
|
||||||
|
# ? (63)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[F,F,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,F,F,F,F],[F,F,T,F,F]],
|
||||||
|
# @ (64)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[F,F,F,F,T],[F,T,T,F,T],[T,F,T,F,T],[T,F,T,T,T],[F,T,T,F,F]],
|
||||||
|
# A (65)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# B (66)
|
||||||
|
[[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F]],
|
||||||
|
# C (67)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# D (68)
|
||||||
|
[[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F]],
|
||||||
|
# E (69)
|
||||||
|
[[T,T,T,T,T],[T,F,F,F,F],[T,F,F,F,F],[T,T,T,T,F],[T,F,F,F,F],[T,F,F,F,F],[T,T,T,T,T]],
|
||||||
|
# F (70)
|
||||||
|
[[T,T,T,T,T],[T,F,F,F,F],[T,F,F,F,F],[T,T,T,T,F],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F]],
|
||||||
|
# G (71)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,F],[T,F,T,T,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,T]],
|
||||||
|
# H (72)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# I (73)
|
||||||
|
[[T,T,T,T,T],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[T,T,T,T,T]],
|
||||||
|
# J (74)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,F,F,T,F],[F,F,F,F,F],[F,F,F,T,F],[F,T,F,T,F],[F,T,T,T,F]],
|
||||||
|
# K (75)
|
||||||
|
[[T,F,F,F,T],[T,F,F,T,F],[T,F,T,F,F],[T,T,F,F,F],[T,F,T,F,F],[T,F,F,T,F],[T,F,F,F,T]],
|
||||||
|
# L (76)
|
||||||
|
[[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F],[T,T,T,T,T]],
|
||||||
|
# M (77)
|
||||||
|
[[T,F,F,F,T],[T,T,F,T,T],[T,F,T,F,T],[T,F,T,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# N (78)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[T,T,F,F,T],[T,F,T,F,T],[T,F,F,T,T],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# O (79)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# P (80)
|
||||||
|
[[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F]],
|
||||||
|
# Q (81)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,T,F,T],[T,F,F,T,F],[F,T,T,F,T]],
|
||||||
|
# R (82)
|
||||||
|
[[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F],[T,F,T,F,F],[T,F,F,T,F],[T,F,F,F,T]],
|
||||||
|
# S (83)
|
||||||
|
[[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,F],[F,T,T,T,F],[F,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# T (84)
|
||||||
|
[[T,T,T,T,T],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F]],
|
||||||
|
# U (85)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# V (86)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,F,T,F],[F,F,T,F,F]],
|
||||||
|
# W (87)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,T,F,T],[T,F,T,F,T],[T,T,F,T,T],[T,F,F,F,T]],
|
||||||
|
# X (88)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[F,T,F,T,F],[F,F,T,F,F],[F,T,F,T,F],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# Y (89)
|
||||||
|
[[T,F,F,F,T],[T,F,F,F,T],[F,T,F,T,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F]],
|
||||||
|
# Z (90)
|
||||||
|
[[T,T,T,T,T],[F,F,F,F,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[T,F,F,F,F],[T,T,T,T,T]],
|
||||||
|
# [ (91)
|
||||||
|
[[F,T,T,T,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,T,T,F]],
|
||||||
|
# \ (92)
|
||||||
|
[[T,F,F,F,F],[T,F,F,F,F],[F,T,F,F,F],[F,F,T,F,F],[F,F,F,T,F],[F,F,F,F,T],[F,F,F,F,T]],
|
||||||
|
# ] (93)
|
||||||
|
[[F,T,T,T,F],[F,F,F,T,F],[F,F,F,T,F],[F,F,F,T,F],[F,F,F,T,F],[F,F,F,T,F],[F,T,T,T,F]],
|
||||||
|
# ^ (94)
|
||||||
|
[[F,F,T,F,F],[F,T,F,T,F],[T,F,F,F,T],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# _ (95)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[T,T,T,T,T]],
|
||||||
|
# ` (96)
|
||||||
|
[[F,T,T,F,F],[F,F,T,F,F],[F,F,F,T,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
# a (97)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,T,T,T,F],[F,F,F,F,T],[F,T,T,T,T],[T,F,F,F,T],[F,T,T,T,T]],
|
||||||
|
# b (98)
|
||||||
|
[[T,F,F,F,F],[T,F,F,F,F],[T,F,T,T,F],[T,T,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F]],
|
||||||
|
# c (99)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,F],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# d (100)
|
||||||
|
[[F,F,F,F,T],[F,F,F,F,T],[F,T,T,F,T],[T,F,F,T,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,T]],
|
||||||
|
# e (101)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,T,T,T,F],[T,F,F,F,T],[T,T,T,T,T],[T,F,F,F,F],[F,T,T,T,F]],
|
||||||
|
# f (102)
|
||||||
|
[[F,F,T,T,F],[F,T,F,F,T],[F,T,F,F,F],[T,T,T,F,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,F]],
|
||||||
|
# g (103)
|
||||||
|
[[F,F,F,F,F],[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,T],[F,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# h (104)
|
||||||
|
[[T,F,F,F,F],[T,F,F,F,F],[T,F,T,T,F],[T,T,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# i (105)
|
||||||
|
[[F,F,T,F,F],[F,F,F,F,F],[F,T,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,T,T,T,F]],
|
||||||
|
# j (106)
|
||||||
|
[[F,F,F,T,F],[F,F,F,F,F],[F,F,T,T,F],[F,F,F,T,F],[F,F,F,T,F],[T,F,F,T,F],[F,T,T,F,F]],
|
||||||
|
# k (107)
|
||||||
|
[[T,F,F,F,F],[T,F,F,F,F],[T,F,F,T,F],[T,F,T,F,F],[T,T,F,F,F],[T,F,T,F,F],[T,F,F,T,F]],
|
||||||
|
# l (108)
|
||||||
|
[[F,T,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,T,T,T,F]],
|
||||||
|
# m (109)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,T,F,T,F],[T,F,T,F,T],[T,F,T,F,T],[T,F,T,F,T],[T,F,T,F,T]],
|
||||||
|
# n (110)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,T,T,F],[T,T,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T]],
|
||||||
|
# o (111)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# p (112)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,T,T,T,F],[T,F,F,F,T],[T,F,F,F,T],[T,T,T,T,F],[T,F,F,F,F]],
|
||||||
|
# q (113)
|
||||||
|
[[F,F,F,F,F],[F,T,T,T,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,T],[F,F,F,F,T],[F,F,F,F,T]],
|
||||||
|
# r (114)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,T,T,F],[T,T,F,F,T],[T,F,F,F,F],[T,F,F,F,F],[T,F,F,F,F]],
|
||||||
|
# s (115)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[F,T,T,T,F],[T,F,F,F,F],[F,T,T,T,F],[F,F,F,F,T],[T,T,T,T,F]],
|
||||||
|
# t (116)
|
||||||
|
[[F,T,F,F,F],[F,T,F,F,F],[T,T,T,T,F],[F,T,F,F,F],[F,T,F,F,F],[F,T,F,F,T],[F,F,T,T,F]],
|
||||||
|
# u (117)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,T,T],[F,T,T,F,T]],
|
||||||
|
# v (118)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,F,F,T],[F,T,F,T,F],[F,F,T,F,F]],
|
||||||
|
# w (119)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,F,F,T],[T,F,F,F,T],[T,F,T,F,T],[T,F,T,F,T],[F,T,F,T,F]],
|
||||||
|
# x (120)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,F,F,T],[F,T,F,T,F],[F,F,T,F,F],[F,T,F,T,F],[T,F,F,F,T]],
|
||||||
|
# y (121)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,F,F,F,T],[T,F,F,F,T],[F,T,T,T,F],[F,F,F,F,T],[F,T,T,T,F]],
|
||||||
|
# z (122)
|
||||||
|
[[F,F,F,F,F],[F,F,F,F,F],[T,T,T,T,T],[F,F,F,T,F],[F,F,T,F,F],[F,T,F,F,F],[T,T,T,T,T]],
|
||||||
|
# { (123)
|
||||||
|
[[F,F,F,T,F],[F,F,T,F,F],[F,F,T,F,F],[F,T,F,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,F,T,F]],
|
||||||
|
# | (124)
|
||||||
|
[[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,T,F,F]],
|
||||||
|
# } (125)
|
||||||
|
[[F,T,F,F,F],[F,F,T,F,F],[F,F,T,F,F],[F,F,F,T,F],[F,F,T,F,F],[F,F,T,F,F],[F,T,F,F,F]],
|
||||||
|
# ~ (126)
|
||||||
|
[[F,F,F,F,F],[F,T,F,F,F],[T,F,T,F,T],[F,F,F,T,F],[F,F,F,F,F],[F,F,F,F,F],[F,F,F,F,F]],
|
||||||
|
]
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
from rpi_ws281x import Color
|
||||||
|
from config import PANEL_PIXEL_COUNT, DISPLAY_MAX_X, DISPLAY_MAX_Y
|
||||||
|
from fonts import FONT_7X5
|
||||||
|
|
||||||
|
# Set by main.py after strip is created
|
||||||
|
strip = None
|
||||||
|
|
||||||
|
|
||||||
|
def _pixel_index(x, y):
|
||||||
|
if x % 2 == 1:
|
||||||
|
y = PANEL_PIXEL_COUNT - 1 - y
|
||||||
|
return y + (x * PANEL_PIXEL_COUNT)
|
||||||
|
|
||||||
|
|
||||||
|
def set_pixel(x, y, r, g, b):
|
||||||
|
if x < 0 or y < 0 or x > DISPLAY_MAX_X or y > DISPLAY_MAX_Y:
|
||||||
|
return
|
||||||
|
strip.setPixelColor(_pixel_index(x, y), Color(r, g, b))
|
||||||
|
|
||||||
|
|
||||||
|
def get_pixel_color(x, y):
|
||||||
|
"""Returns (r, g, b) tuple."""
|
||||||
|
if x < 0 or y < 0 or x > DISPLAY_MAX_X or y > DISPLAY_MAX_Y:
|
||||||
|
return (0, 0, 0)
|
||||||
|
c = strip.getPixelColor(_pixel_index(x, y))
|
||||||
|
return ((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF)
|
||||||
|
|
||||||
|
|
||||||
|
def fill_pixels(x1, y1, x2, y2, r, g, b):
|
||||||
|
if x1 > x2:
|
||||||
|
x1, x2 = x2, x1
|
||||||
|
if y1 > y2:
|
||||||
|
y1, y2 = y2, y1
|
||||||
|
for y in range(y1, y2 + 1):
|
||||||
|
for x in range(x1, x2 + 1):
|
||||||
|
set_pixel(x, y, r, g, b)
|
||||||
|
|
||||||
|
|
||||||
|
def shift_rectangle_left(x1, y1, x2, y2, shift_by):
|
||||||
|
if not shift_by:
|
||||||
|
return
|
||||||
|
if x1 > x2:
|
||||||
|
x1, x2 = x2, x1
|
||||||
|
if y1 > y2:
|
||||||
|
y1, y2 = y2, y1
|
||||||
|
for y in range(y1, y2 + 1):
|
||||||
|
if y < 0 or y > DISPLAY_MAX_Y:
|
||||||
|
continue
|
||||||
|
for x in range(x1, x2 + 1):
|
||||||
|
src_x = x1 + (x - x1)
|
||||||
|
dest_x = src_x - shift_by
|
||||||
|
color = (0, 0, 0)
|
||||||
|
if 0 <= src_x <= DISPLAY_MAX_X:
|
||||||
|
color = get_pixel_color(src_x, y)
|
||||||
|
if 0 <= dest_x <= DISPLAY_MAX_X:
|
||||||
|
set_pixel(dest_x, y, *color)
|
||||||
|
for y in range(y1, y2 + 1):
|
||||||
|
if y < 0 or y > DISPLAY_MAX_Y:
|
||||||
|
continue
|
||||||
|
for x in range(x2 - shift_by + 1, x2 + 1):
|
||||||
|
if 0 <= x <= DISPLAY_MAX_X:
|
||||||
|
set_pixel(x, y, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_character_part(row_data, width, r, g, b, start_x, start_y):
|
||||||
|
for col in range(width):
|
||||||
|
if row_data[col]:
|
||||||
|
px = col + start_x
|
||||||
|
py = start_y
|
||||||
|
if 0 <= px <= DISPLAY_MAX_X and 0 <= py <= DISPLAY_MAX_Y:
|
||||||
|
set_pixel(px, py, r, g, b)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_character(char_data, height, width, r, g, b, cursor):
|
||||||
|
"""Draws one character at cursor position, advances cursor.x by width+1."""
|
||||||
|
for row in range(height):
|
||||||
|
draw_character_part(char_data[row], width, r, g, b, cursor['x'], row + cursor['y'])
|
||||||
|
cursor['x'] += width + 1
|
||||||
+431
@@ -0,0 +1,431 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PTI LEDY - Raspberry Pi 4 port
|
||||||
|
Requires: sudo pip3 install rpi_ws281x flask
|
||||||
|
Run with: sudo python3 main.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from rpi_ws281x import PixelStrip, ws
|
||||||
|
|
||||||
|
import config
|
||||||
|
import low_level
|
||||||
|
import server
|
||||||
|
from fonts import FONT_7X5
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Strip init
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
strip = PixelStrip(
|
||||||
|
config.NUMPIXELS,
|
||||||
|
config.PIN,
|
||||||
|
freq_hz=800000,
|
||||||
|
dma=10,
|
||||||
|
invert=False,
|
||||||
|
brightness=255,
|
||||||
|
channel=0,
|
||||||
|
strip_type=ws.WS2811_STRIP_GRB,
|
||||||
|
)
|
||||||
|
strip.begin()
|
||||||
|
low_level.strip = strip
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Global state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
global_node_id = 0
|
||||||
|
|
||||||
|
text_nodes = [None] * config.MAX_TEXT_NODES_COUNT
|
||||||
|
multi_color_text_nodes = [None] * config.MAX_TEXT_NODES_COUNT
|
||||||
|
|
||||||
|
for i in range(config.MAX_TEXT_NODES_COUNT):
|
||||||
|
text_nodes[i] = {
|
||||||
|
'global_id': 0, 'content': '', 'color': (0, 0, 0),
|
||||||
|
'char_height': config.MEDIUM_TEXT_HEIGHT, 'char_width': config.MEDIUM_TEXT_WIDTH,
|
||||||
|
'pos_x': 0, 'pos_y': 0, 'character_count': 0,
|
||||||
|
'scroll_slowness': 0, 'scroll_progress': 0,
|
||||||
|
'disappear_time': 0, 'is_scrolling': True, 'is_repeating': False,
|
||||||
|
}
|
||||||
|
multi_color_text_nodes[i] = {
|
||||||
|
'global_id': 0, 'content': '', 'colors': [], # list of (r,g,b,start_index)
|
||||||
|
'char_height': config.MEDIUM_TEXT_HEIGHT, 'char_width': config.MEDIUM_TEXT_WIDTH,
|
||||||
|
'pos_x': 0, 'pos_y': 0, 'character_count': 0, 'color_count': 0,
|
||||||
|
'scroll_slowness': 0, 'scroll_progress': 0,
|
||||||
|
'disappear_time': 0, 'is_scrolling': True, 'is_repeating': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
main_animation_started = False
|
||||||
|
bottom_text_state = 0
|
||||||
|
scroll_node_global_id = 0
|
||||||
|
scroll_node_active = False
|
||||||
|
|
||||||
|
program2_active = False
|
||||||
|
bottom_text_iteration_count = 0
|
||||||
|
program2_vehicles_spawned = 0
|
||||||
|
program2_drain_counter = 0
|
||||||
|
program2_done = False
|
||||||
|
program2_scroll_progress = 0
|
||||||
|
program2_spacing_counter = 0
|
||||||
|
|
||||||
|
VEHICLES = [
|
||||||
|
[[0,0,0,0,0,0,0,0],[0,0,1,1,1,1,1,0],[1,1,1,1,1,1,1,1],[0,1,0,0,0,0,1,0]],
|
||||||
|
[[0,0,1,1,0,0,0,0],[0,1,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],[0,1,0,0,0,0,1,0]],
|
||||||
|
[[0,0,1,1,0,1,1,1],[0,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[0,1,0,0,0,0,1,0]],
|
||||||
|
[[0,0,0,0,1,1,0,0],[0,0,1,1,1,1,1,0],[0,1,1,1,1,1,1,1],[0,0,1,0,0,0,1,0]],
|
||||||
|
[[0,0,0,0,0,0,0,0],[0,0,0,1,1,1,1,1],[0,0,1,1,1,1,1,1],[0,0,0,1,0,0,1,0]],
|
||||||
|
[[1,1,1,1,1,1,1,1],[0,1,0,1,0,1,0,1],[0,1,1,1,1,1,1,1],[0,0,1,0,0,0,1,0]],
|
||||||
|
[[0,0,0,0,0,0,0,0],[0,0,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],[0,1,0,0,0,0,1,0]],
|
||||||
|
[[1,1,0,1,1,1,1,1],[1,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1],[0,1,0,0,0,0,1,0]],
|
||||||
|
[[0,0,0,1,1,1,1,1],[0,0,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[0,1,0,0,0,0,1,0]],
|
||||||
|
[[0,0,0,0,0,0,0,0],[0,1,1,1,0,0,0,1],[1,1,1,1,1,1,1,0],[0,1,0,0,0,1,0,0]],
|
||||||
|
[[0,1,0,1,1,1,1,1],[1,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1],[0,0,1,0,0,0,1,0]],
|
||||||
|
[[0,0,0,0,0,0,0,0],[0,0,0,0,0,1,1,0],[0,0,0,1,1,1,1,1],[0,0,0,0,1,0,1,0]],
|
||||||
|
]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _get_text_node_x2(node):
|
||||||
|
if node['character_count'] == 0:
|
||||||
|
return node['pos_x']
|
||||||
|
return node['pos_x'] + (node['char_width'] * node['character_count']) + (node['character_count'] - 1) - 1
|
||||||
|
|
||||||
|
def _get_text_node_y2(node):
|
||||||
|
return node['pos_y'] + node['char_height'] - 1
|
||||||
|
|
||||||
|
def _color_for_char(r, g, b):
|
||||||
|
return (r, g, b)
|
||||||
|
|
||||||
|
def _add_text_node(content, color, pos_x, pos_y, scroll_slowness, is_scrolling, is_small, disappear_time, is_repeating):
|
||||||
|
global global_node_id
|
||||||
|
if not content:
|
||||||
|
return None
|
||||||
|
h = config.SMALL_TEXT_HEIGHT if is_small else config.MEDIUM_TEXT_HEIGHT
|
||||||
|
w = config.SMALL_TEXT_WIDTH if is_small else config.MEDIUM_TEXT_WIDTH
|
||||||
|
for node in text_nodes:
|
||||||
|
if node['disappear_time'] == 0:
|
||||||
|
node['content'] = content
|
||||||
|
node['color'] = color
|
||||||
|
node['pos_x'] = pos_x
|
||||||
|
node['pos_y'] = pos_y
|
||||||
|
node['char_height'] = h
|
||||||
|
node['char_width'] = w
|
||||||
|
node['character_count'] = len(content)
|
||||||
|
node['scroll_slowness'] = scroll_slowness
|
||||||
|
node['scroll_progress'] = 0
|
||||||
|
node['is_scrolling'] = is_scrolling
|
||||||
|
node['is_repeating'] = is_repeating
|
||||||
|
node['disappear_time'] = disappear_time
|
||||||
|
node['global_id'] = global_node_id
|
||||||
|
global_node_id += 1
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _add_multi_color_node(content, colors, pos_x, pos_y, scroll_slowness, is_scrolling, is_small, disappear_time, is_repeating):
|
||||||
|
global global_node_id
|
||||||
|
if not content:
|
||||||
|
return None
|
||||||
|
h = config.SMALL_TEXT_HEIGHT if is_small else config.MEDIUM_TEXT_HEIGHT
|
||||||
|
w = config.SMALL_TEXT_WIDTH if is_small else config.MEDIUM_TEXT_WIDTH
|
||||||
|
for node in multi_color_text_nodes:
|
||||||
|
if node['disappear_time'] == 0:
|
||||||
|
node['content'] = content
|
||||||
|
node['colors'] = list(colors)
|
||||||
|
node['color_count'] = len(colors)
|
||||||
|
node['pos_x'] = pos_x
|
||||||
|
node['pos_y'] = pos_y
|
||||||
|
node['char_height'] = h
|
||||||
|
node['char_width'] = w
|
||||||
|
node['character_count'] = len(content)
|
||||||
|
node['scroll_slowness'] = scroll_slowness
|
||||||
|
node['scroll_progress'] = 0
|
||||||
|
node['is_scrolling'] = is_scrolling
|
||||||
|
node['is_repeating'] = is_repeating
|
||||||
|
node['disappear_time'] = disappear_time
|
||||||
|
node['global_id'] = global_node_id
|
||||||
|
global_node_id += 1
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _is_node_existing(gid):
|
||||||
|
for node in text_nodes:
|
||||||
|
if node['global_id'] == gid and node['disappear_time'] != 0:
|
||||||
|
return True
|
||||||
|
for node in multi_color_text_nodes:
|
||||||
|
if node['global_id'] == gid and node['disappear_time'] != 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _reset_global_id():
|
||||||
|
global global_node_id
|
||||||
|
global_node_id = 2
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Drawing
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _draw_char(ch, height, width, r, g, b, cursor):
|
||||||
|
if ord(ch) < ord('!') or ord(ch) > ord('~'):
|
||||||
|
ch = ' '
|
||||||
|
glyph = FONT_7X5[ord(ch) - ord(' ')]
|
||||||
|
low_level.draw_character(glyph, height, width, r, g, b, cursor)
|
||||||
|
|
||||||
|
def scroll_all_texts(split_scroll_mode=False):
|
||||||
|
for node in text_nodes:
|
||||||
|
if node['disappear_time'] == 0:
|
||||||
|
continue
|
||||||
|
if node['is_scrolling']:
|
||||||
|
if node['scroll_slowness'] > node['scroll_progress']:
|
||||||
|
node['scroll_progress'] += 1
|
||||||
|
else:
|
||||||
|
node['scroll_progress'] = 0
|
||||||
|
x2 = _get_text_node_x2(node)
|
||||||
|
if split_scroll_mode or node['pos_y'] >= 7:
|
||||||
|
if x2 < 0:
|
||||||
|
if node['is_repeating']:
|
||||||
|
node['pos_x'] = config.DISPLAY_MAX_X
|
||||||
|
else:
|
||||||
|
node['disappear_time'] = 1
|
||||||
|
continue
|
||||||
|
node['pos_x'] -= 1
|
||||||
|
else:
|
||||||
|
if node['pos_x'] > config.DISPLAY_MAX_X:
|
||||||
|
if node['is_repeating']:
|
||||||
|
node['pos_x'] = -x2 + node['pos_x']
|
||||||
|
else:
|
||||||
|
node['disappear_time'] = 1
|
||||||
|
continue
|
||||||
|
node['pos_x'] += 1
|
||||||
|
|
||||||
|
cursor = {'x': node['pos_x'], 'y': node['pos_y']}
|
||||||
|
r, g, b = node['color']
|
||||||
|
for ch in node['content']:
|
||||||
|
_draw_char(ch, node['char_height'], node['char_width'], r, g, b, cursor)
|
||||||
|
|
||||||
|
def scroll_all_multi_color_texts(split_scroll_mode=False):
|
||||||
|
for node in multi_color_text_nodes:
|
||||||
|
if node['disappear_time'] == 0:
|
||||||
|
continue
|
||||||
|
if node['is_scrolling']:
|
||||||
|
if node['scroll_slowness'] > node['scroll_progress']:
|
||||||
|
node['scroll_progress'] += 1
|
||||||
|
else:
|
||||||
|
node['scroll_progress'] = 0
|
||||||
|
x1 = node['pos_x']
|
||||||
|
x2 = x1 + (node['char_width'] * node['character_count']) + (node['character_count'] - 1) - 1
|
||||||
|
if split_scroll_mode or node['pos_y'] >= 7:
|
||||||
|
if x2 < 0:
|
||||||
|
if node['is_repeating']:
|
||||||
|
node['pos_x'] = config.DISPLAY_MAX_X
|
||||||
|
else:
|
||||||
|
node['disappear_time'] = 1
|
||||||
|
continue
|
||||||
|
node['pos_x'] -= 1
|
||||||
|
else:
|
||||||
|
if x1 > config.DISPLAY_MAX_X:
|
||||||
|
if node['is_repeating']:
|
||||||
|
node['pos_x'] = -x2 + x1
|
||||||
|
else:
|
||||||
|
node['disappear_time'] = 1
|
||||||
|
continue
|
||||||
|
node['pos_x'] += 1
|
||||||
|
|
||||||
|
cursor = {'x': node['pos_x'], 'y': node['pos_y']}
|
||||||
|
color_index = 0
|
||||||
|
r, g, b, _ = node['colors'][0]
|
||||||
|
for j, ch in enumerate(node['content']):
|
||||||
|
if color_index < node['color_count'] - 1 and node['colors'][color_index + 1][3] == j:
|
||||||
|
color_index += 1
|
||||||
|
r, g, b, _ = node['colors'][color_index]
|
||||||
|
_draw_char(ch, node['char_height'], node['char_width'], r, g, b, cursor)
|
||||||
|
|
||||||
|
def handle_disappear_timers():
|
||||||
|
for node in text_nodes:
|
||||||
|
if node['disappear_time'] > 0:
|
||||||
|
node['disappear_time'] -= 1
|
||||||
|
if node['disappear_time'] == 0:
|
||||||
|
low_level.fill_pixels(
|
||||||
|
node['pos_x'], node['pos_y'],
|
||||||
|
_get_text_node_x2(node), _get_text_node_y2(node),
|
||||||
|
0, 0, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_multi_color_disappear_timers():
|
||||||
|
for node in multi_color_text_nodes:
|
||||||
|
if node['disappear_time'] > 0:
|
||||||
|
node['disappear_time'] -= 1
|
||||||
|
if node['disappear_time'] == 0:
|
||||||
|
x2 = node['pos_x'] + (node['char_width'] * node['character_count']) + (node['character_count'] - 1) - 1
|
||||||
|
low_level.fill_pixels(
|
||||||
|
node['pos_x'], node['pos_y'],
|
||||||
|
x2, node['pos_y'] + node['char_height'] - 1,
|
||||||
|
0, 0, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Programs
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def handle_program1():
|
||||||
|
global main_animation_started, scroll_node_active, scroll_node_global_id
|
||||||
|
global bottom_text_state, bottom_text_iteration_count, program2_active
|
||||||
|
|
||||||
|
s = server.state
|
||||||
|
|
||||||
|
if not s['program_top_text_enabled']:
|
||||||
|
for node in multi_color_text_nodes:
|
||||||
|
if node['disappear_time'] != 0:
|
||||||
|
node['disappear_time'] = 1
|
||||||
|
main_animation_started = False
|
||||||
|
elif not main_animation_started:
|
||||||
|
if not _is_node_existing(0) and not _is_node_existing(1):
|
||||||
|
main_animation_started = True
|
||||||
|
_add_multi_color_node(
|
||||||
|
"Witamy w PTI",
|
||||||
|
[(40, 40, 40, 0), (0, 0, 50, 9)],
|
||||||
|
0, 0, 3, True, True, -1, True
|
||||||
|
)
|
||||||
|
|
||||||
|
if not s['program_bottom_text_enabled']:
|
||||||
|
if scroll_node_active:
|
||||||
|
for node in text_nodes:
|
||||||
|
if node['disappear_time'] != 0:
|
||||||
|
node['disappear_time'] = 1
|
||||||
|
scroll_node_active = False
|
||||||
|
if program2_active:
|
||||||
|
program2_active = False
|
||||||
|
return
|
||||||
|
|
||||||
|
if program2_active:
|
||||||
|
return
|
||||||
|
|
||||||
|
if (main_animation_started or not s['program_top_text_enabled']) and \
|
||||||
|
(not scroll_node_active or not _is_node_existing(scroll_node_global_id)):
|
||||||
|
|
||||||
|
if scroll_node_active:
|
||||||
|
bottom_text_iteration_count += 1
|
||||||
|
scroll_node_active = False
|
||||||
|
if s['program_vehicles_enabled'] and bottom_text_iteration_count >= config.PROGRAM2_TEXT_ITERATIONS:
|
||||||
|
bottom_text_iteration_count = 0
|
||||||
|
if random.randint(0, 99) < config.PROGRAM2_TRIGGER_CHANCE:
|
||||||
|
program2_active = True
|
||||||
|
reset_program2()
|
||||||
|
low_level.fill_pixels(0, 9, config.DISPLAY_MAX_X, config.DISPLAY_MAX_Y, 0, 0, 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
texts = [
|
||||||
|
("Technik informatyk + ai, robotyka", (0, 0, 50)),
|
||||||
|
("Technik programista", (50, 40, 0)),
|
||||||
|
("Technik poligrafii i grafiki komputerowej", (0, 45, 0)),
|
||||||
|
("Technik reklamy", (60, 20, 0)),
|
||||||
|
]
|
||||||
|
text, color = texts[bottom_text_state]
|
||||||
|
node = _add_text_node(text, color, config.DISPLAY_MAX_X, 9, 1, True, True, -1, False)
|
||||||
|
if node is not None:
|
||||||
|
scroll_node_global_id = node['global_id']
|
||||||
|
scroll_node_active = True
|
||||||
|
bottom_text_state = (bottom_text_state + 1) % 4
|
||||||
|
|
||||||
|
|
||||||
|
def draw_vehicle(index, pos_x, pos_y):
|
||||||
|
vr, vg, vb = config.VEHICLE_COLOR
|
||||||
|
for row in range(4):
|
||||||
|
for col in range(8):
|
||||||
|
if VEHICLES[index][row][col]:
|
||||||
|
low_level.set_pixel(pos_x + col, pos_y + row, vr, vg, vb)
|
||||||
|
|
||||||
|
|
||||||
|
def reset_program2():
|
||||||
|
global program2_scroll_progress, program2_spacing_counter
|
||||||
|
global program2_vehicles_spawned, program2_drain_counter, program2_done
|
||||||
|
program2_scroll_progress = 0
|
||||||
|
program2_spacing_counter = 0
|
||||||
|
program2_vehicles_spawned = 0
|
||||||
|
program2_drain_counter = 0
|
||||||
|
program2_done = False
|
||||||
|
|
||||||
|
|
||||||
|
def handle_program2(spacing=4, scroll_slowness=1, y=0, max_vehicles=0):
|
||||||
|
global program2_scroll_progress, program2_spacing_counter
|
||||||
|
global program2_vehicles_spawned, program2_drain_counter, program2_done
|
||||||
|
|
||||||
|
if scroll_slowness > program2_scroll_progress:
|
||||||
|
program2_scroll_progress += 1
|
||||||
|
return
|
||||||
|
program2_scroll_progress = 0
|
||||||
|
|
||||||
|
low_level.shift_rectangle_left(0, y, config.DISPLAY_MAX_X, y + 3, 1)
|
||||||
|
|
||||||
|
if max_vehicles > 0 and program2_vehicles_spawned >= max_vehicles:
|
||||||
|
program2_drain_counter += 1
|
||||||
|
if program2_drain_counter >= config.DISPLAY_MAX_X + 1:
|
||||||
|
program2_done = True
|
||||||
|
return
|
||||||
|
|
||||||
|
program2_spacing_counter -= 1
|
||||||
|
if program2_spacing_counter <= 0:
|
||||||
|
draw_vehicle(random.randint(0, 11), config.DISPLAY_MAX_X - 7, y)
|
||||||
|
program2_spacing_counter = 8 + spacing
|
||||||
|
if max_vehicles > 0:
|
||||||
|
program2_vehicles_spawned += 1
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main loop
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
strip.setBrightness(255)
|
||||||
|
for i in range(config.NUMPIXELS):
|
||||||
|
strip.setPixelColor(i, 0)
|
||||||
|
strip.show()
|
||||||
|
server.start_server(port=80)
|
||||||
|
print("Server started. Running display loop.")
|
||||||
|
|
||||||
|
|
||||||
|
def loop():
|
||||||
|
global program2_active
|
||||||
|
|
||||||
|
s = server.state
|
||||||
|
|
||||||
|
low_level.fill_pixels(0, 0, config.DISPLAY_MAX_X, 7, 0, 0, 0)
|
||||||
|
handle_program1()
|
||||||
|
|
||||||
|
if s['program_top_text_enabled']:
|
||||||
|
scroll_all_multi_color_texts(split_scroll_mode=True)
|
||||||
|
handle_multi_color_disappear_timers()
|
||||||
|
|
||||||
|
if not s['program_bottom_text_enabled']:
|
||||||
|
if program2_active:
|
||||||
|
program2_active = False
|
||||||
|
low_level.fill_pixels(0, 9, config.DISPLAY_MAX_X, config.DISPLAY_MAX_Y, 0, 0, 0)
|
||||||
|
elif program2_active and not s['program_vehicles_enabled']:
|
||||||
|
program2_active = False
|
||||||
|
low_level.fill_pixels(0, 9, config.DISPLAY_MAX_X, config.DISPLAY_MAX_Y, 0, 0, 0)
|
||||||
|
elif program2_active:
|
||||||
|
handle_program2(spacing=4, scroll_slowness=1, y=12, max_vehicles=config.PROGRAM2_VEHICLE_COUNT)
|
||||||
|
if program2_done:
|
||||||
|
program2_active = False
|
||||||
|
low_level.fill_pixels(0, 9, config.DISPLAY_MAX_X, config.DISPLAY_MAX_Y, 0, 0, 0)
|
||||||
|
else:
|
||||||
|
low_level.fill_pixels(0, 9, config.DISPLAY_MAX_X, config.DISPLAY_MAX_Y, 0, 0, 0)
|
||||||
|
scroll_all_texts(split_scroll_mode=True)
|
||||||
|
handle_disappear_timers()
|
||||||
|
|
||||||
|
strip.show()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setup()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
loop()
|
||||||
|
time.sleep(0.02) # ~50 fps
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
for i in range(config.NUMPIXELS):
|
||||||
|
strip.setPixelColor(i, 0)
|
||||||
|
strip.show()
|
||||||
|
print("\nStopped.")
|
||||||
|
sys.exit(0)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
rpi_ws281x
|
||||||
|
flask
|
||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
from flask import Flask, request, jsonify, Response
|
||||||
|
import threading
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Shared state — written by Flask thread, read by main loop
|
||||||
|
state = {
|
||||||
|
'brightness': 100,
|
||||||
|
'program_top_text_enabled': True,
|
||||||
|
'program_bottom_text_enabled': True,
|
||||||
|
'program_vehicles_enabled': True,
|
||||||
|
}
|
||||||
|
_lock = threading.Lock()
|
||||||
|
|
||||||
|
INDEX_HTML = """<!DOCTYPE HTML><html>
|
||||||
|
<head>
|
||||||
|
<title>LED Panel Control</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body { font-family: Arial, sans-serif; background: #1a1a2e; color: #eee; padding: 20px; }
|
||||||
|
h1 { text-align: center; margin-bottom: 30px; font-size: 1.5em; }
|
||||||
|
.container { max-width: 400px; margin: 0 auto; }
|
||||||
|
.card { background: #16213e; border-radius: 12px; padding: 16px 20px; margin-bottom: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
.card-label { font-size: 1em; }
|
||||||
|
.card-label small { display: block; color: #888; font-size: 0.75em; margin-top: 2px; }
|
||||||
|
.toggle { position: relative; width: 52px; height: 28px; flex-shrink: 0; }
|
||||||
|
.toggle input { opacity: 0; width: 0; height: 0; }
|
||||||
|
.toggle .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background: #444; border-radius: 28px; transition: 0.3s; }
|
||||||
|
.toggle .slider:before { content: ""; position: absolute; height: 22px; width: 22px;
|
||||||
|
left: 3px; bottom: 3px; background: #fff; border-radius: 50%; transition: 0.3s; }
|
||||||
|
.toggle input:checked + .slider { background: #4CAF50; }
|
||||||
|
.toggle input:checked + .slider:before { transform: translateX(24px); }
|
||||||
|
.brightness-card { background: #16213e; border-radius: 12px; padding: 16px 20px; margin-bottom: 12px; }
|
||||||
|
.brightness-card label { font-size: 1em; display: block; margin-bottom: 8px; }
|
||||||
|
.brightness-row { display: flex; align-items: center; gap: 12px; }
|
||||||
|
.brightness-row input[type=range] { flex: 1; accent-color: #4CAF50; }
|
||||||
|
.brightness-row span { min-width: 32px; text-align: right; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>LED Panel Control</h1>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-label">Top Text<small>Witamy w PTI</small></div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="top_text" checked onchange="toggle('top_text', this.checked)"><span class="slider"></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-label">Bottom Text<small>School programs</small></div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="bottom_text" checked onchange="toggle('bottom_text', this.checked)"><span class="slider"></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-label">Vehicles<small>Vehicle interruptions</small></div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="vehicles" checked onchange="toggle('vehicles', this.checked)"><span class="slider"></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="brightness-card">
|
||||||
|
<label>Brightness</label>
|
||||||
|
<div class="brightness-row">
|
||||||
|
<input type="range" id="brightness" min="0" max="100" value="100" oninput="updateBrightnessLabel()" onchange="sendBrightness()">
|
||||||
|
<span id="brightnessValue">100</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function toggle(program, enabled) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "/toggle-program", true);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
xhr.send("program=" + program + "&enabled=" + (enabled ? "true" : "false"));
|
||||||
|
}
|
||||||
|
function updateBrightnessLabel() {
|
||||||
|
document.getElementById("brightnessValue").innerText = document.getElementById("brightness").value;
|
||||||
|
}
|
||||||
|
function sendBrightness() {
|
||||||
|
var val = document.getElementById("brightness").value;
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "/brightness", true);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
xhr.send("value=" + val);
|
||||||
|
}
|
||||||
|
function fetchStatus() {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/program-status", true);
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
var s = JSON.parse(xhr.responseText);
|
||||||
|
document.getElementById("top_text").checked = s.top_text;
|
||||||
|
document.getElementById("bottom_text").checked = s.bottom_text;
|
||||||
|
document.getElementById("vehicles").checked = s.vehicles;
|
||||||
|
document.getElementById("brightness").value = s.brightness;
|
||||||
|
document.getElementById("brightnessValue").innerText = s.brightness;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
window.onload = fetchStatus;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return Response(INDEX_HTML, mimetype='text/html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/brightness', methods=['POST'])
|
||||||
|
def handle_brightness():
|
||||||
|
value = request.form.get('value')
|
||||||
|
if value is not None:
|
||||||
|
with _lock:
|
||||||
|
state['brightness'] = max(0, min(100, int(value)))
|
||||||
|
return 'OK'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/toggle-program', methods=['POST'])
|
||||||
|
def handle_toggle():
|
||||||
|
program = request.form.get('program')
|
||||||
|
enabled_str = request.form.get('enabled')
|
||||||
|
if not program or enabled_str is None:
|
||||||
|
return 'Missing program or enabled parameter', 400
|
||||||
|
enabled = enabled_str.lower() == 'true'
|
||||||
|
key_map = {
|
||||||
|
'top_text': 'program_top_text_enabled',
|
||||||
|
'bottom_text': 'program_bottom_text_enabled',
|
||||||
|
'vehicles': 'program_vehicles_enabled',
|
||||||
|
}
|
||||||
|
if program not in key_map:
|
||||||
|
return 'Unknown program', 400
|
||||||
|
with _lock:
|
||||||
|
state[key_map[program]] = enabled
|
||||||
|
return 'OK'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/program-status', methods=['GET'])
|
||||||
|
def handle_status():
|
||||||
|
with _lock:
|
||||||
|
s = dict(state)
|
||||||
|
return jsonify({
|
||||||
|
'top_text': s['program_top_text_enabled'],
|
||||||
|
'bottom_text': s['program_bottom_text_enabled'],
|
||||||
|
'vehicles': s['program_vehicles_enabled'],
|
||||||
|
'brightness': s['brightness'],
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def start_server(host='0.0.0.0', port=80):
|
||||||
|
t = threading.Thread(target=lambda: app.run(host=host, port=port, use_reloader=False), daemon=True)
|
||||||
|
t.start()
|
||||||
Reference in New Issue
Block a user