# HG changeset patch # User drewp@bigasterisk.com # Date 2019-06-08 07:36:34 # Node ID d4a07ad96aade7bee1a8c43747557fcb0562444b # Parent b26a1e7fcfbee50b4cdffa50f86b56a0edb679ab attempt a pyftdi output driver using code from https://github.com/jlbrogdon/dmx_controller/blob/master/OpenDmxUsb/__init__.py (gplv2) Ignore-this: 5a4dbbc0493dfa0bf62cc9c5b79b96d7 diff --git a/light9/collector/dmx_controller_output.py b/light9/collector/dmx_controller_output.py new file mode 100644 --- /dev/null +++ b/light9/collector/dmx_controller_output.py @@ -0,0 +1,58 @@ +####################################################### +# DMX Controller +# See +# Copyright (C) Jonathan Brogdon +# This program is published under a GPLv2 license +# +# This code implements a DMX controller with UI provided +# by LCDproc +# +####################################################### +from pyftdi import ftdi + +#FTDI device info +vendor=0x0403 +product=0x6001 + +##################### +# DMX USB controller +##################### +class OpenDmxUsb(): + def __init__(self): + self.baud_rate = 250000 + self.data_bits = 8 + self.stop_bits = 2 + self.parity = 'N' + self.flow_ctrl = '' + self.rts_state = 0 + self._init_dmx() + + #Initialize the controller + def _init_dmx(self): + self.ftdi=ftdi.Ftdi() + self.ftdi.open(vendor,product,0) + self.ftdi.set_baudrate(self.baud_rate) + self.ftdi.set_line_property(self.data_bits,self.stop_bits,self.parity,break_=0) + self.ftdi.set_flowctrl(self.flow_ctrl) + self.ftdi.purge_rx_buffer() + self.ftdi.purge_tx_buffer() + self.ftdi.set_rts(self.rts_state) + + #Send DMX data + def send_dmx(self,channelVals): + assert self.ftdi.write_data(channelVals) == 513 + # Need to generate two bits for break + self.ftdi.set_line_property(self.data_bits,self.stop_bits,self.parity,break_=1) + self.ftdi.set_line_property(self.data_bits,self.stop_bits,self.parity,break_=1) + self.ftdi.set_line_property(self.data_bits,self.stop_bits,self.parity,break_=0) + +if __name__=="__main__": + dmxUsb=OpenDmxUsb() + + channelVals=bytearray([0]*513) + channelVals[0]=0 # dummy channel 0 + while(True): + for x in range(1,468+1): + channelVals[x] = 255 + + dmxUsb.send_dmx(channelVals) diff --git a/light9/collector/output.py b/light9/collector/output.py --- a/light9/collector/output.py +++ b/light9/collector/output.py @@ -102,6 +102,34 @@ class BackgroundLoopOutput(Output): d = threads.deferToThread(self._write, sendingBuffer) d.addCallbacks(done, err) +class FtdiDmx(BackgroundLoopOutput): + def __init__(self, uri, lastDmxChannel, rate=22): + super().__init__(uri) + self.lastDmxChannel = lastDmxChannel + from .dmx_controller_output import OpenDmxUsb + self.dmx = OpenDmxUsb() + + def _write(self, buf): + self._writeStats.fps.mark() + with self._writeStats.call.time(): + if not buf: + logAllDmx.debug('%s: empty buf- no output', + self.shortId()) + return + + # ok to truncate the last channels if they just went + # to 0? No it is not. DMX receivers don't add implicit + # zeros there. + buf = bytes([0]) + buf[:self.lastDmxChannel] + + if logAllDmx.isEnabledFor(logging.DEBUG): + # for testing fps, smooth fades, etc + logAllDmx.debug( + '%s: %s...' % + (self.shortId(), ' '.join(map(str, buf[:32])))) + + self.dmx.send_dmx(buf) + class Udmx(BackgroundLoopOutput): _reconnections = scales.IntStat('reconnections') diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -46,3 +46,4 @@ https://github.com/drewp/cyclone/archive cycloneerr==0.3.0 rdfdb==0.20.0 standardservice==0.6.0 +pyftdi==0.29.4