MattIPv4 / PyDMXControl

A Python 3 module to control DMX using OpenDMX or uDMX - Featuring fixture profiles, built-in effects and a web control panel.
https://pypi.org/project/PyDMXControl/
GNU General Public License v3.0
121 stars 23 forks source link

A simple example script doesn't work with FT232 USB dongle #47

Closed pauljurczak closed 1 year ago

pauljurczak commented 1 year ago

I have a DMX512 decoder with LEDs at address 1. It is connected to the PC via USB/RS485 dongle using FT232R chip. It works fine with QLC+ app, but this PyDMXControl script:

from PyDMXControl.controllers import SerialController
from PyDMXControl.profiles.Generic import Dimmer

dmx = SerialController('/dev/ttyUSB0')
leds = dmx.add_fixture(Dimmer, name='LEDs')
leds.dim(127, 5000)

has no effect. I also tried:

from PyDMXControl.controllers import SerialController

dmx = SerialController('/dev/ttyUSB0')
dmx.all_on()

and no effect again. Can someone help me with a basic on/off control of one or more channels using the hardware I have?

pauljurczak commented 1 year ago

In the meantime, I found PyDMX and PyDMX-Drivers-FTDI combo (https://github.com/JMAlego/PyDMX), which works fine with my FT232R USB/RS485 cable.

MattIPv4 commented 1 year ago

👀 If the FTDI driver in that library is working for you, could I suggest trying the OpenDMXController here? That uses FTDI under the hood, rather than being a bare-bones serial connection.

pauljurczak commented 1 year ago

@MattIPv4 I tried:

from PyDMXControl.controllers import OpenDMXController

dmx = OpenDMXController()
dmx.all_off()

with no effect. Nothing is sent through USB cable. Is this a correct minimum script? I just need an on/off function for addresses 1 through 4.

MattIPv4 commented 1 year ago

You might want to pass ftdi_vendor_id and/or ftdi_product_id as keyword args into OpenDMXController based on the device you are using, if the defaults aren't correct?

https://github.com/MattIPv4/PyDMXControl/blob/27d41b33689eb197492a5928494a6d2f15ac391e/PyDMXControl/controllers/_OpenDMXController.py#L19-L21

pauljurczak commented 1 year ago

The defaults seem to be correct. I have:

Bus 001 Device 013: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC

What is ftdi_serial for?

MattIPv4 commented 1 year ago

It's an optional kwarg that lets you set the exact serial number of the device. I don't think you should need it.

Looking at your script, are you registering any fixtures? PyDMXControl only sends data for fixtures it knows about (so a controller with no fixtures will send DMX frames with no data in them).

pauljurczak commented 1 year ago

I have a generic DMX512 controller with 4 ports at addresses 1 through 4. What fixture should I use for it?

MattIPv4 commented 1 year ago

Registering four Generic.Dimmers might be the easiest? Or you could use the Generic.Custom fixture, and pass channels: 4 when instantiating it

pauljurczak commented 1 year ago

I tried:

from PyDMXControl.controllers import OpenDMXController
from PyDMXControl.profiles.Generic import Dimmer

dmx = OpenDMXController()
leds = dmx.add_fixture(Dimmer, name='LEDs')
dmx.all_off()

with no effect.

pauljurczak commented 1 year ago

This script:

from PyDMXControl.controllers import OpenDMXController
from PyDMXControl.profiles.Generic import Custom

dmx = OpenDMXController()
leds = dmx.add_fixture(Custom, channels=4)
dmx.all_off()

produces

PyDMXControl.utils.exceptions.ChannelNotFoundException: Channel dimmer not found for fixture #1

error.

MattIPv4 commented 1 year ago

I tried:

from PyDMXControl.controllers import OpenDMXController
from PyDMXControl.profiles.Generic import Dimmer

dmx = OpenDMXController()
leds = dmx.add_fixture(Dimmer, name='LEDs')
dmx.all_off()

with no effect.

How are you testing the output from this? This should just be outputting DMX frames with a single 0 in it essentially.

This script:

from PyDMXControl.controllers import OpenDMXController
from PyDMXControl.profiles.Generic import Custom

dmx = OpenDMXController()
leds = dmx.add_fixture(Custom, channels=4)
dmx.all_off()

produces

PyDMXControl.utils.exceptions.ChannelNotFoundException: Channel dimmer not found for > fixture #1

error.

Yeah, the custom fixture profile doesn't register channel aliases, so the controller helper functions won't work for it. You'd want to use the methods on fixture directly, so for example, leds.set_channels(0, 0, 0, 0) (hopefully that works, been a while since I used these methods).

pauljurczak commented 1 year ago

How are you testing the output from this? This should just be outputting DMX frames with a single 0 in it essentially.

My controller turns all channels on when powered up. The USB cable has indicator LED, which flashes during transmission. No data is being sent through USB.

from PyDMXControl.controllers import OpenDMXController
from PyDMXControl.profiles.Generic import Custom

dmx = OpenDMXController()
leds = dmx.add_fixture(Custom, channels=4)
leds.set_channels(0, 0, 0, 0)
dmx.close()

has no effect.

MattIPv4 commented 1 year ago

Could you try a dmx.sleep_till_interrupt() before the dmx.close(), and waiting a few seconds to see if the controller connects to your device? I would expect the FTDI driver to error out if it can't connect/send data (as some folks have encountered in #44)

MattIPv4 commented 1 year ago

Comparing https://github.com/JMAlego/PyDMX/blob/master/drivers/ftdi/dmx_drivers/ftdi/ft232r.py to https://github.com/MattIPv4/PyDMXControl/blob/master/PyDMXControl/controllers/_OpenDMXController.py, we appear to be driving the FTDI device in very similar manners (albeit I am using pyftdi where they are are using pylibftdi)

MattIPv4 commented 1 year ago

I should also check that your device that is expecting DMX control is set to address 1, as all the code you're using here is inserting the fixture at the start of the universe?

pauljurczak commented 1 year ago

Could you try a dmx.sleep_till_interrupt() before the dmx.close()

Yes, this helped. The LEDs are off, but it is sending a continuous stream of commands now, while I want to send a single one only. I tried:

from PyDMXControl.controllers import OpenDMXController
from PyDMXControl.profiles.Generic import Custom

dmx = OpenDMXController()
leds = dmx.add_fixture(Custom, channels=4)
leds.set_channels(0, 0, 0, 0)
dmx.sleep_till_interrupt()
dmx.close()
pauljurczak commented 1 year ago

time.sleep(0.5) provides enough delay. Is there a way to send just one command? It's not a showstopper, though.

pauljurczak commented 1 year ago

What do you think is a reasonable minimum sleep time I should use?

pauljurczak commented 1 year ago

Comparing https://github.com/JMAlego/PyDMX/blob/master/drivers/ftdi/dmx_drivers/ftdi/ft232r.py to https://github.com/MattIPv4/PyDMXControl/blob/master/PyDMXControl/controllers/_OpenDMXController.py, we appear to be driving the FTDI device in very similar manners (albeit I am using pyftdi where they are are using pylibftdi)

The difference I see is PyDMX send_update() sends only a single DMX packet, but PyDMXControl set_channels() sends DMX packets continuously.

MattIPv4 commented 1 year ago

Yes, PyDMXControl is meant to be an active controller, so it sends DMX frames continually at a rate that should keep most fixtures happy. It is not built to send a single frame (not all fixtures will even support being sent a single frame and holding that state forever).

MattIPv4 commented 1 year ago

The internal ticker aims to send frames at roughly 60fps. You could pass ticker_interval_millis when instantiating the controller to change that though.

pauljurczak commented 1 year ago

Thanks a lot for your help. I will close this issue now.