feat(07-02): wire animation rendering into firmware/main.py
- Add import for create_animation from animations module - Add current_anim_instance global for animation object - Create animation instance in udp_listener_task on anim_cmd receipt - Implement render loop in render_task using ticks_diff for timing
This commit is contained in:
105
firmware/main.py
Normal file
105
firmware/main.py
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import asyncio
|
||||||
|
import socket
|
||||||
|
import select
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
|
from protocol import classify_packet, parse_animation_cmd
|
||||||
|
from led_driver import create_strip
|
||||||
|
from animations import create_animation
|
||||||
|
|
||||||
|
# --- Global state ---
|
||||||
|
config = None
|
||||||
|
strip = None
|
||||||
|
current_mode = 'idle' # 'idle', 'animation', 'frame'
|
||||||
|
current_animation = None # dict from parse_animation_cmd
|
||||||
|
current_anim_instance = None # animation object with render()
|
||||||
|
animation_start_ms = 0
|
||||||
|
frame_pixels = None # set by frame packets
|
||||||
|
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
try:
|
||||||
|
with open('config.json') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except OSError:
|
||||||
|
return {'led_count': 300, 'strip_type': 'sk6812', 'led_pin': 5,
|
||||||
|
'spi_id': 1, 'udp_port': 21324}
|
||||||
|
|
||||||
|
|
||||||
|
async def udp_listener_task(port):
|
||||||
|
"""Non-blocking UDP listener using select.poll(0) inside asyncio."""
|
||||||
|
global current_mode, current_animation, current_anim_instance
|
||||||
|
global animation_start_ms, frame_pixels
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.bind(('0.0.0.0', port))
|
||||||
|
sock.setblocking(False)
|
||||||
|
poller = select.poll()
|
||||||
|
poller.register(sock, select.POLLIN)
|
||||||
|
|
||||||
|
print('[udp] Listening on port', port)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
events = poller.poll(0)
|
||||||
|
if events:
|
||||||
|
try:
|
||||||
|
data, addr = sock.recvfrom(1500)
|
||||||
|
ptype = classify_packet(data)
|
||||||
|
|
||||||
|
if ptype == 'anim_cmd':
|
||||||
|
cmd = parse_animation_cmd(data)
|
||||||
|
if cmd:
|
||||||
|
current_animation = cmd
|
||||||
|
current_anim_instance = create_animation(cmd)
|
||||||
|
current_mode = 'animation'
|
||||||
|
animation_start_ms = time.ticks_ms()
|
||||||
|
print('[udp] Animation:', cmd['animation'])
|
||||||
|
|
||||||
|
elif ptype in ('drgb', 'drgbw', 'dnrgb'):
|
||||||
|
# Frame handling added by Plan 07-03
|
||||||
|
current_mode = 'frame'
|
||||||
|
print('[udp] Frame packet:', ptype, len(data), 'bytes')
|
||||||
|
|
||||||
|
else:
|
||||||
|
print('[udp] Unknown packet type:', data[0] if data else '?')
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
await asyncio.sleep_ms(5)
|
||||||
|
|
||||||
|
|
||||||
|
async def render_task():
|
||||||
|
"""Animation render loop at ~60fps."""
|
||||||
|
global current_mode, frame_pixels
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if current_mode == 'animation' and current_anim_instance:
|
||||||
|
t = time.ticks_diff(time.ticks_ms(), animation_start_ms) / 1000.0
|
||||||
|
pixels = current_anim_instance.render(t, config['led_count'])
|
||||||
|
strip.write_rgb(pixels)
|
||||||
|
elif current_mode == 'frame' and frame_pixels:
|
||||||
|
# Plan 07-03 adds frame write logic here
|
||||||
|
pass
|
||||||
|
|
||||||
|
await asyncio.sleep_ms(16) # ~60 fps
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
global config, strip
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
strip = create_strip(config)
|
||||||
|
strip.clear()
|
||||||
|
|
||||||
|
print('[main] LightSync firmware ready')
|
||||||
|
print('[main] Strip:', config['strip_type'], '/', config['led_count'], 'LEDs')
|
||||||
|
|
||||||
|
asyncio.create_task(udp_listener_task(config['udp_port']))
|
||||||
|
asyncio.create_task(render_task())
|
||||||
|
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
Reference in New Issue
Block a user