#!/usr/bin/env python # WS server example that synchronizes state across clients import asyncio import json import logging import websockets import redis import time import serial import numpy as np import RPi.GPIO as GPIO import threading import os def is_raspberrypi(): try: with io.open('/sys/firmware/devicetree/base/model', 'r') as m: if 'raspberry pi' in m.read().lower(): return True except Exception: pass return False print(is_raspberrypi) # Colordefinition for console print class pcol: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' ################################################################################# # # # G L O B A L S # # DMX array config DMXDATA = np.zeros([513], dtype='uint8') # DMX Startcode DMXDATA[0] = 0 # 0 = Lighting Control Data # DMX Reset [µs]/float DMXRST = 1000000.0 # typical 88µs # DMX Break [µs]/float DMXBRK = 8.0 # 8µs (min) to 1s (max) # DMX Sequence sleep [ms]/float DMXSLP = 5000.0 #250.0 # 0µs (min) to 1s (max) # Buffer for RDM DMXRDM = 0 DMXRDMFLAG = False # 1 for radlines() is active to buffer rdm packages # # Websocket variables # # Set to store user-data USERS = set() # Set to store other data served by the websocket # STATE = {} # DummyFill # for i in range(1, 10): # STATE.update({str(i): 0}) ################################################################################# # # # F U N C T I O N S # # ########################### # # DMX # ########################### def sendDMX(data): #continuously sending the signal; to receive changed values: Threading global DMXRDMFLAG global DMXRDM with serial.Serial('/dev/serial0', baudrate=250000, timeout=1, stopbits=serial.STOPBITS_TWO, bytesize=8) as DMXSER:# UART configuration and definition DMXSER.cancel_read() # GPIO_18 to switch RS-485 driver (IC1) to TX-mode GPIO.output(18, GPIO.HIGH) # send reset DMXSER.send_break(DMXRST / 1000000.0) # break-time time.sleep(DMXBRK / 1000000.0) # send complete data array DMXSER.write(bytearray(DMXDATA)) # GPIO_18 to switch RS-485 driver (IC1) to RX-mode DMXSER.reset_input_buffer() GPIO.output(18, GPIO.LOW) # Sleep between TX packages, use time to get RDM packages DMXRDM = "" DMXRDMFLAG = False DMXSER.timeout(DMXSLP / 1000.0) # between 0 - 1 sec DMXRDM = DMXSER.readlines() DMXRDMFLAG = True ########################### # # THREADING # ########################### slicy = threading.Thread(target=sendDMX, args=(DMXDATA, )) slicy.start() ########################### # # Websockset # ########################### async def rdm_state(): global DMXRDM global DMXRDMFLAG if USERS and DMXRDMFLAG: message = "rdm=" + DMXRDM + ";" print("RDM EVENT " + DMXRDM) await asyncio.wait([user.send(message) for user in USERS]) # Notify_State is triggered each time a new connection is established to the websocket # and only sent to the new user, not to all! # Serving all infos once as complete string, so we only have to send delta from that time on async def notify_state(): if USERS: # asyncio.wait doesnt accept an empty list message = state_event() await asyncio.wait([user.send(message) for user in USERS]) # Generate message-text for Notify_State def state_event(): # we have to send a string rtn = "" # dont have to send DMXDATA[0], this is the start code for index in range(1, len(DMXDATA)): rtn += (str(index) + "=" + str(DMXDATA[index]) + ";") # give debug feedback print("state_event returned ", rtn) return rtn # Users-Event is triggered each time a new connection is established to the websocket # and sent to all other but not the new user async def notify_users(): if USERS: # asyncio.wait doesnt accept an empty list message = users_event() await asyncio.wait([user.send(message) for user in USERS]) # Generate message-text for Notify_Users def users_event(): # only send the new value return "users=" + str(len(USERS)) + ";" # Handling (un-)registering async def register(websocket): USERS.add(websocket) print("New user registered ") await notify_users() async def unregister(websocket): USERS.remove(websocket) print("Known user left") await notify_users() # # Sending delta-reports to all users when a value changed except user count # async def notify_delta(delta): if USERS: # asyncio.wait doesnt accept an empty list await asyncio.wait([user.send(delta) for user in USERS]) # # Websocket working thread # async def websworker(websocket, path): # register(websocket) sends user_event() to websocket await register(websocket) try: await websocket.send(state_event()) while True: try: # waiting for a new message received on the websocket message = await asyncio.wait_for(websocket.recv(), timeout=0.1) # split message from multiple commands in one string data = message.split(";") # process each command for index in range(len(data)): # act[0] will be the key # act[1] will be the value act = data[index].split("=") try: # if key is a number, we will have e change on a dmx value # otherwise, we have a string to check which will be processed in except... # poor python - there is no other chance to check if a string can be a int without errors dmxchannel = int(act[0]) dmxvalue = int(act[1]) # We have integer, but are they in a valid range? if( dmxchannel < 1 or dmxchannel > 512 or dmxvalue < 0 or dmxvalue > 255): # this must be an exception, we will continue in the exception-area anyway now dmxchannel = int("will raise") # looks valid, so take them to the buffer DMXDATA[dmxchannel] = dmxvalue # notifying our connected users # we can send the resceived command back since the syntax is matched :) await notify_delta(data[index]) # debug print("DMX value for channel " + act[0] + " changed to " + act[1]) # It does not look like we have a dmx key-value, so we have to process the command except Exception: if act[0] == "manual": sendDMX(DMXDATA) print("Manual send data: ", bytearray(DMXDATA)) elif act[0] == "sec": print("") else: print("received message, but can not handle: ", act) except asyncio.TimeoutError: await rdm_state() finally: await unregister(websocket) ########################### # # GPIO # ########################### # We have to set GPIO GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(18, GPIO.OUT) GPIO.setup(4, GPIO.OUT) # GPIO_18 to switch RS-485 driver (IC1) to RX-mode #GPIO.output(18, GPIO.LOW) # V2: will be done in send-loop for rdm # Powercycle 3V3 on HAT to ensure a cleaned up buffer GPIO.output(4, GPIO.HIGH) time.sleep(0.25) GPIO.output(4, GPIO.LOW) ########################### # # Start the websocket # ########################### asyncio.get_event_loop().run_until_complete( websockets.serve(websworker, port=6789)) asyncio.get_event_loop().run_forever() print("start") while True: sendDMX(DMXDATA)