DMX-Hat_RasPi/Python/sock.py
2022-07-27 15:24:06 +02:00

342 lines
7.5 KiB
Python

#!/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
while True:
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()