Open tannewt opened 6 years ago
If this ever gets done a great project would be an Artemis "red alert" siren:
http://artemiswiki.pbworks.com/w/page/68387358/DMX%20Interface
I found these which might be useful reference https://github.com/jaydcarlson/microcontroller-test-code/tree/master/SAMD10/dmx https://github.com/claudeheintz/LXSAMD21DMX
it is also used in theater, music-shows and other stage / event things . also in architecture (storefront and similar ambient-lighting)
some more references i found on my search that could be helpfull:
I'm also very interested in there being a way to send and receive DMX-512 data with CircuitPython.
Can this be done with UART at the lowest level? Do we just need a Python library for it?
What is the least expensive testing setup?
Hey @tannewt ,
A Python library would be the easiest way to do it. But it might need doing at the hardware level of the SAMD21/51. I read in the datasheet that these chips should support RS485 at the hardware level.
I know only one python library that does this well at the moment. https://github.com/mathertel/DMXSerial
At the heart of DMX:
After extensive reading from the adafruit forums from 2018 ... please ignore my comments about direct hardware support.
The SAMD51 has some support for RS485 but external hardware is still required. The SAMD21 has no built in support.
The only way is to use a transceiver module (MAX3485 is most popular) and normal UART protocols with a packet decoding layer (presumably this is what everyone in this issue is looking for).
I'd imagine anyone who wants this would do something similar to the below:
class DMX512:
def __init__(self, uart):
self._device = uart
def read(self):
# recieve 513 packets from DMX protocol
data = self._device.read(513)
# do something with the data
# maybe remove first packet
recv = data[1:]
return recv
def write(self, values):
# send 512 bytes via DMX protocol
# check data first
if len(values) < 512:
raise Exception("data too short")
# maybe convert values to bytes
# send data to devices
self._device.write(values)
I have been looking into this for about 7 years and been doing stage lighting for 10+ years - that's why I'm interested 😄 .
@tannewt least expensive could be some 'amazon dmx led spot' (you never know if they are conform to the specifications...) -
the better setup: a scope or digital-logger/analyzer thing... ;-)
or just some already 'proven to work' arduino library..
all the implementation can be tested in the 3.3V / 5V TTL levels...
as @wallarug has written - to get to the actuall DMX512 HW-Layer ('just' RS485) some differential driver /receiver is needed..
here would be the challenge to check and find one that is 3.3V compatible..
out of my head i don't know one..
but there are lots of options form different manufactures - example TI -
and than its just a question of 'do you want to be specification conformant' -
if true you have to think about isolation...
if not - just ignore isolation - in most small setups it works ;-)
theoretically you could do it with just an UART implementation.. but this could get hacky - as you need to fastly switch baudrates to generate the stop bit timming.. (if i remember correctly) lower level - maybe the 'USART' support in the chip can handle this thing already in a smoother way... DMX is in the basic version only Master → Receiver → Receiver→ Receiver. so uni-directional.
if someone wants to implement RDM (remote device managment) it gets more complicated and timing-critical - as now the master has to listen between sending normal frames for the responses from the devices. so its half-duplex - for this you normally need to toggle a pin to tell the driver if it is in send or receive mode..
@wallarug your example is only partly correct: DMX is not specified to transmit all 512 channels. you are allowed to send less! and sometimes that is usefull! in my classic arduino projects i used this to reduce performance /memory problems ;-) (only sending the ~12 channels the device needed...)
Thanks for the info. To summarize: I don't think there is any core work needed for this. On the drivers side we'd likely need a community library for the external transmitter IC and then a library for the DMX protocol on top of that.
I don't have any experience with DMX so someone else will be a better person to do it. I'm happy to consult on the CircuitPython side of things but won't be picking this up directly myself.
the external IC is only 'levelshifting' so no lib there.. maybe we need some special edge cases implemented in the UART Core... in total you are right - best is if someone with deep detail understanding of the DMX-Protocoll finds the time to implement it -and then we will know where the limits are.. i think i could do it - but i don't know if and when i find spare time for it - so if there is someone else willing i could support/help with some protocoll information and testing with some hardware-setups.
U can start with this fragment:
import board
import busio
import digitalio
import time
from array import array
import gc
class DMX():
def __init__(self, max_channels):
# First byte is always 0, 512 after that is the 512 channels
self.dmx_message = array('B', [0] * (max_channels+1))
self.dmx_uart = digitalio.DigitalInOut(board.TX)
self.dmx_uart.direction = digitalio.Direction.OUTPUT
self.dmx_uart.value = 1
def set_channels(self, message):
"""
a dict and writes them to the array
format {channel:value}
"""
for ch in message:
self.dmx_message[ch] = message[ch]
def write_frame(self):
"""
Send a DMX frame
"""
self.dmx_uart.value = 0
time.sleep(88E-6)
self.dmx_uart.value = 1
self.dmx_uart.deinit()
time.sleep(8E-6)
self.dmx_uart = busio.UART(tx=board.TX,rx=None, baudrate=250000, bits=8, parity=None, stop=2)
self.dmx_uart.write(self.dmx_message)
self.dmx_uart.deinit()
self.dmx_uart = digitalio.DigitalInOut(board.TX)
self.dmx_uart.direction = digitalio.Direction.OUTPUT
self.dmx_uart.value = 1
channel = 10
dmx = DMX(channel)
#dmx.set_channels({1:0})
for i in range(1,channel):
print(i)
dmx.set_channels({i:255})
dmx.write_frame()
time.sleep(2)
It has been stale for over 2y. Is there any news on this?
currently i have no use-cases for dmx... otherwise i would try the fragment from @Tasm-Devil. seems like a valid first starting point...
It has been stale for over 2y. Is there any news on this?
Just to confirm, is all that people want is a CircuitPython Library that does DMX control? Nothing more that this?
@wallarug I would like to see a library which sends DMX packets periodically (min. 0.8 Frames/s, as per DMX Standard).
It has been stale for over 2y. Is there any news on this?
Just to confirm, is all that people want is a CircuitPython Library that does DMX control? Nothing more that this?
if i think about it for a moment - i can imagine three simple scenarios:
as told - just a quick brainstorming... and i think the sketch from Tasm-Devil is a good starting point for 2.
I’m working on two libraries, one for DMX sending, and the other for DMX receiving. Both use RP2040’s PIO. They both work. I’m now doing some clean-up and hope I can get them into the Community Bundle.
I’m working on two libraries, one for DMX sending, and the other for DMX receiving. Both use RP2040’s PIO. They both work. I’m now doing some clean-up and hope I can get them into the Community Bundle.
Awesome! Can you provide some GitHub links when you are ready?
I’m working on two libraries, one for DMX sending, and the other for DMX receiving. Both use RP2040’s PIO. They both work. I’m now doing some clean-up and hope I can get them into the Community Bundle.
We are really pleased to hear this!
https://github.com/mydana/CircuitPython_DMX_Transmitter Would love feedback.
https://github.com/mydana/CircuitPython_DMX_Transmitter Would love feedback.
That looks awesome. I'll take another look soon and comment in the issues,
https://github.com/mydana/CircuitPython_DMX_Transmitter Would love feedback.
Just wanted to say thanks @mydana, I just found your library from this thread and was able to set it up and get it working without any issues using an RP2040 and CircuitPython 9.0.0 beta 2. Your wiring diagram in the README was also very helpful. Thank you! I hope that it can be added to the community bundle soon.
https://github.com/mydana/CircuitPython_DMX_Receiver
It's still a bit buggy, but I'd love feedback.
I independently came up with something very similar to https://github.com/adafruit/circuitpython/issues/673#issuecomment-616831705 from @Tasm-Devil switching the transmit pin between digital out (for Break + Mark-after-Break) and uart (for start condition + data)
That works on the ESP32s3 I use.
Notable difference is that time.sleep
has a minimum increment of 1ms. So asking it to sleep 8uS for the Mark-after-break ends up being really 1ms. But initializing the uart alone takes more than the minimum MAB time, so I just set the pin high and don't sleep. (maybe dangerous is future micro python runs much faster. I wish there were a sleep_us(int)
)
I also experimented with using SPI to send out the bitstream. That allows very precise time control, but didn't really make any difference in my testing.
https://github.com/mydana/CircuitPython_DMX_Transmitter Would love feedback.
Why won't circup see your package? @mydana
dmx_transmitter is not a known CircuitPython library.
:(
I'm on a pico plus 2 running CircuitPython 9.
Why won't circup see your package? @mydana
I needs to be added to the community bundle for circup to see it: https://github.com/adafruit/CircuitPython_Community_Bundle/tree/main/libraries/helpers
This is used by DJs to control lighting.