From 6f6e0fd66579cfa403190b7b6989ac24fc06bcb2 Mon Sep 17 00:00:00 2001 From: Kai Vogelgesang Date: Tue, 22 Mar 2022 21:35:16 +0100 Subject: [PATCH] Implement DMX serial output --- pult/backend/main.py | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/pult/backend/main.py b/pult/backend/main.py index 5bf0d8f..369a33e 100644 --- a/pult/backend/main.py +++ b/pult/backend/main.py @@ -1,5 +1,8 @@ import asyncio from typing import * +from queue import Queue +from threading import Thread, Lock +import time from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request from fastapi.staticfiles import StaticFiles @@ -7,6 +10,7 @@ from fastapi.responses import FileResponse from pydantic import BaseModel, Field, ValidationError +import serial app = FastAPI() @@ -156,7 +160,80 @@ async def ws_handler(ws: WebSocket): app.mount("/", StaticFiles(directory="frontend", html=True)) +dmx_data_lock = Lock() +dmx_data = [0 for _ in range(len(dmx_state))] + +async def dmx_watcher(): + while True: + with dmx_data_lock: + for (i, slider) in enumerate(dmx_state): + dmx_data[i] = slider.value + await asyncio.sleep(1/50) + +class DmxWriter(Thread): + def __init__(self): + super().__init__() + self.running = True + + def run(self): + + FPS = 50 + FRAME_TIME = 1 / FPS + + + with serial.Serial("/dev/ttyUSB0", 500_000) as ser: + payload = bytearray(512) + + def sync(): + # wait for sync + while True: + b = ser.readline() + if b.strip() == b"Sync.": + return + + sync() + + print("initial sync.") + + while self.running: + + loop_start = time.time() + + with dmx_data_lock: + for (i, value) in enumerate(dmx_data): + payload[i] = value + + ser.write(payload) + ser.flush() + + response = ser.readline() + if response.strip() != b"Ack.": + print(f"received bad response: {response!r}") + sync() + continue + + loop_time = time.time() - loop_start + if loop_time < FRAME_TIME: + time.sleep(FRAME_TIME - loop_time) + else: + print("loop took too long!") + + print(f"loop time: {1000 * loop_time:0.2f}ms busy, {1000 * (time.time() - loop_start):0.2f}ms total") + + def stop(self): + self.running = False + +dmx_writer = DmxWriter() @app.on_event("startup") async def on_startup(): asyncio.create_task(socket_manager.watch_auto_release()) + asyncio.create_task(dmx_watcher()) + dmx_writer.start() + + +@app.on_event("shutdown") +async def on_shutdown(): + print("shutdown") + dmx_writer.stop() + dmx_writer.join() \ No newline at end of file