initial commit
This commit is contained in:
parent
80571fa781
commit
179e543fee
14
README.md
14
README.md
|
@ -1,3 +1,15 @@
|
|||
# esp-artnet-http-to-dmx
|
||||
|
||||
ESP Arpnet + HTTP to DMX bridge
|
||||
ESP Arpnet + HTTP to DMX bridge
|
||||
|
||||
dmx.py handels outgoing dmx
|
||||
dmx_dummy.py is a dummy version of dmx.py for local testing without requiering the esp UART interface
|
||||
|
||||
artnetserver.py handels incoming artnet
|
||||
|
||||
httpserver.py handels incoming http requests
|
||||
|
||||
fade.py handels color fades
|
||||
|
||||
|
||||
testfiles for httpserver and artnetserver are in the test directory
|
|
@ -0,0 +1,14 @@
|
|||
class ArtNetServer():
|
||||
def __init__(self, ip, port):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
|
||||
import socket
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(('', 6454))
|
||||
s.listen()
|
||||
|
||||
while True:
|
||||
conn, addr = s.accept()
|
||||
print('Got a connection from %s' % str(addr))
|
|
@ -0,0 +1,38 @@
|
|||
try:
|
||||
import usocket as socket
|
||||
except:
|
||||
import socket
|
||||
|
||||
class ArtNetServer():
|
||||
def __init__(self, ip, port, universe=0):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
self.universe = universe
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self.socket.setblocking(False)
|
||||
self.socket.bind((ip, port))
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
self.socket.close()
|
||||
|
||||
def checkdata(self, dmx):
|
||||
try:
|
||||
data, address = self.socket.recvfrom(1024)
|
||||
except OSError as e:
|
||||
err = e.args[0]
|
||||
if err != 11:
|
||||
raise e
|
||||
else:
|
||||
if self.validate_header(data):
|
||||
universe = int.from_bytes(data[14:16], 'little')
|
||||
if universe != self.universe:
|
||||
return -1
|
||||
dmx.set_channels(list(data)[18:])
|
||||
return 0
|
||||
|
||||
def validate_header(self, header):
|
||||
return header[:12] == b'Art-Net\x00\x00P\x00\x0e'
|
|
@ -0,0 +1,34 @@
|
|||
from machine import UART, Pin
|
||||
import time
|
||||
from array import array
|
||||
|
||||
tx_pins = [1, 17]
|
||||
|
||||
class universe():
|
||||
def __init__(self, uartport, dirselectport):
|
||||
self.uartport = uartport
|
||||
self.dirselect = Pin(dirselectport, Pin.OUT)
|
||||
dmx_uart = uart = UART(uartport)
|
||||
del(dmx_uart)
|
||||
|
||||
self.dmx_values = array('B', [0] * 513)
|
||||
|
||||
def get_rgb(self, address):
|
||||
return {'r': dmx.dmx_values[address], 'g': dmx.dmx_values[address+1], 'b': dmx.dmx_values[address+2]}
|
||||
|
||||
def set_channels(self, frame):
|
||||
|
||||
for ch in frame:
|
||||
self.dmx_values[ch] = frame[ch]
|
||||
|
||||
def write_frame(self):
|
||||
self.dirselect.value(1)
|
||||
dmx_uart = Pin(tx_pins[self.uartport], Pin.OUT)
|
||||
dmx_uart.value(0)
|
||||
time.sleep_us(74)
|
||||
dmx_uart.value(1)
|
||||
|
||||
dmx_uart = UART(self.uartport)
|
||||
dmx_uart.init(115200, bits=8, parity=None, stop=2)
|
||||
dmx_uart.write(self.dmx_values)
|
||||
del(dmx_uart)
|
|
@ -0,0 +1,12 @@
|
|||
from array import array
|
||||
|
||||
class universe():
|
||||
def __init__(self):
|
||||
self.dmx_values = array('B', [0] * 513)
|
||||
|
||||
def set_channels(self, frame):
|
||||
for ch in message:
|
||||
self.dmx_values[ch] = frame[ch]
|
||||
|
||||
def write_frame(self):
|
||||
print(self.dmx_values)
|
|
@ -0,0 +1,34 @@
|
|||
import time
|
||||
class FadeHandler():
|
||||
def __init__(self):
|
||||
self.fades = []
|
||||
|
||||
|
||||
class RGBFade():
|
||||
def __init__(self, dmx, address, target, time):
|
||||
self.address = address
|
||||
self.target = target
|
||||
self.before = dmx.get_rgb(address)
|
||||
self.time = time
|
||||
self.start = time.ticks_ms()
|
||||
self.finished = False
|
||||
|
||||
def step(self, dmx):
|
||||
if dmx.get_rgb(address) == target:
|
||||
self.finished = True
|
||||
return
|
||||
fadeprogress = time.ticks_diff(time.ticks_ms(), self.start)/self.time
|
||||
dmx.dmx_values[address] = self.target['r'] - self.before['r'] * fadeprogress
|
||||
dmx.dmx_values[address+1] = self.target['g'] - self.before['g'] * fadeprogress
|
||||
dmx.dmx_values[address+2] = self.target['b'] - self.before['b'] * fadeprogress
|
||||
|
||||
def addrgbfade(self, dmx, address, target, time):
|
||||
self.fades.append(self.RGBFade(dmx, address, target, time))
|
||||
|
||||
def checkfades(self, dmx):
|
||||
for fadei in self.fades:
|
||||
if self.fades[fadei].finished:
|
||||
self.fades = self.fades.pop(fadei)
|
||||
continue
|
||||
self.fades[fadei].step(dmx)
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
try:
|
||||
import usocket as socket
|
||||
except:
|
||||
import socket
|
||||
import json
|
||||
configpage = '<html><head><title>Configseite</title></head><body><h1>huhu</h1></body></html>'
|
||||
|
||||
class HTTPServer():
|
||||
def __init__(self, ip, port):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.setblocking(False)
|
||||
self.socket.bind((ip, port))
|
||||
self.socket.listen()
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
self.socket.close()
|
||||
|
||||
|
||||
def checkdata(self, dmx, f):
|
||||
try:
|
||||
conn, address = self.socket.accept()
|
||||
except OSError as e:
|
||||
err = e.args[0]
|
||||
if err != 11:
|
||||
raise e
|
||||
else:
|
||||
request = conn.recv(1024).decode()
|
||||
print(request)
|
||||
requestline = request.split('\n')[0]
|
||||
headers = dict(headerpair.split(': ') for headerpair in request.split('\n')[1:])
|
||||
method = requestline.split()[0]
|
||||
requestpath = requestline.split()[1].split("?", 1)
|
||||
query = dict()
|
||||
if len(requestpath) > 1:
|
||||
query = dict(querypair.split('=') for querypair in requestpath[1].split('&'))
|
||||
if method == 'POST':
|
||||
if 'Content-Length' in headers and 'Content-Type' in headers:
|
||||
if headers['Content-Type'] == 'application/json':
|
||||
try:
|
||||
body = json.loads(request[len(request)-headers['Content-Length']:])
|
||||
except ValueError:
|
||||
self.sendresponse(conn, 'HTTP/1.0 415 Unsupported Media Type\n\nUnsupported Media Type')
|
||||
return -1
|
||||
else:
|
||||
self.sendresponse(conn, 'HTTP/1.0 415 Unsupported Media Type\n\nUnsupported Media Type')
|
||||
return -1
|
||||
else:
|
||||
self.sendresponse(conn, 'HTTP/1.0 411 Length Required\n\nLength Required')
|
||||
return -1
|
||||
if requestpath[0] == '/' and method == 'GET':
|
||||
self.sendresponse(conn, 'HTTP/1.0 200 OK\nConnection: close\nContent-Length: %s\nContent-Type: text/html; charset=UTF-8\n\n%s'%(len(configpage), configpage))
|
||||
return 0
|
||||
elif requestpath[0] == '/dmx' and method == 'POST':
|
||||
dmx.set_channels(body['dmx'])
|
||||
self.sendresponse(conn, 'HTTP/1.0 200 OK\n\n')
|
||||
return 0
|
||||
elif requestpath[0] == '/rgb' and method == 'GET':
|
||||
dmx.dmx_values[int(query['address'])] = query['r']
|
||||
dmx.dmx_values[int(query['address'])+1] = query['g']
|
||||
dmx.dmx_values[int(query['address'])+2] = query['b']
|
||||
self.sendresponse(conn, 'HTTP/1.0 200 OK\n\n')
|
||||
return 0
|
||||
elif requestpath[0] == '/fadeto' and method == 'GET':
|
||||
f.addrgbfade(dmx, int(query['address']), {'r': query['r'], 'g': query['g'], 'b': query['b']}, query['time'])
|
||||
self.sendresponse(conn, 'HTTP/1.0 200 OK\n\n')
|
||||
return 0
|
||||
else:
|
||||
self.sendresponse(conn, 'HTTP/1.0 404 NOT FOUND\n\nFile Not Found')
|
||||
return -1
|
||||
|
||||
def sendresponse(self, conn, response):
|
||||
conn.sendall(response.encode())
|
||||
conn.close()
|
||||
return 0
|
|
@ -0,0 +1,34 @@
|
|||
# main.py -- put your code here!
|
||||
import dmx
|
||||
import artnetserver
|
||||
import httpserver
|
||||
import fade
|
||||
import time
|
||||
|
||||
|
||||
print("starting artnet server")
|
||||
# create a artnetserver object
|
||||
a = artnetserver.ArtNetServer('127.0.0.1', 6454, 0)
|
||||
|
||||
print("starting http server")
|
||||
# create a httpserver object
|
||||
h = httpserver.HTTPServer("127.0.0.1", 8080)
|
||||
|
||||
print("creating fadehandler")
|
||||
# create a fadehandler object
|
||||
f = fade.FadeHandler()
|
||||
|
||||
# create a dmx device on UART port 1 with port 21 as direction
|
||||
dmx0 = dmx.universe(1, 21)
|
||||
|
||||
while 1:
|
||||
# get incoming artnet data
|
||||
a.checkdata(dmx)
|
||||
# get incoming http data
|
||||
h.checkdata(dmx, f)
|
||||
# checkfaders for completion and do next step
|
||||
f.checkfades(dmx)
|
||||
|
||||
print("writing dmx")
|
||||
# writing dmx data
|
||||
dmx0.write_frame()
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "ESP Arpnet + HTTP to DMX bridge"
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import asyncio
|
||||
from pyartnet import ArtNetNode
|
||||
|
||||
async def main():
|
||||
# Run this code in your async function
|
||||
node = ArtNetNode('127.0.0.1', 6454)
|
||||
|
||||
# Create universe 0
|
||||
universe = node.add_universe(0)
|
||||
|
||||
# Add a channel to the universe which consists of 3 values
|
||||
# Default size of a value is 8Bit (0..255) so this would fill
|
||||
# the DMX values 1..3 of the universe
|
||||
channel = universe.add_channel(start=12, width=3)
|
||||
channel2 = universe.add_channel(start=15, width=3)
|
||||
|
||||
|
||||
# Fade channel to 255,0,0 in 5s
|
||||
# The fade will automatically run in the background
|
||||
channel.add_fade([3,2,1], 500)
|
||||
channel2.add_fade([1,2,3], 500)
|
||||
|
||||
#channel.set_values([255,0,127])
|
||||
|
||||
# this can be used to wait till the fade is complete
|
||||
await channel
|
||||
|
||||
asyncio.run(main())
|
|
@ -0,0 +1,7 @@
|
|||
import requests
|
||||
|
||||
json_data = {'dmx': [3,7,1,0,3,4]}
|
||||
response = requests.post('http://127.0.0.1:8081/dmx', json = json_data)
|
||||
|
||||
if response.status_code == 200:
|
||||
print('Success!')
|
Loading…
Reference in New Issue