chat py
This commit is contained in:
+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)
|
||||
Reference in New Issue
Block a user