PyDMX icon indicating copy to clipboard operation
PyDMX copied to clipboard

Install core package and FTDI driver package

Open static75 opened this issue 2 years ago • 6 comments

Noob question: I downloaded the zip file to my Raspberry Pi and read the README file. How do I install the core package and FTDI driver package? I want to make sure I do everything exactly right for the best chance of success.

In the Core folder I see a setup.cfg file and pyproject.toml
Under Core there is a DMX folder, and under that a Drivers folder with dummy.py and debug.py. Not sure what I'm supposed to do with those.

In the Drivers folder there is Ardiuno folder and FTDI folder. I'm not using Arduino, so if I dive into the FTDI folder I find another set of setup.cfg and pyproject.toml.

I'm a little lost as to what I'm supposed to do next to get things up and running. Thanks in advance!

static75 avatar Sep 04 '22 22:09 static75

Hi there, beginner questions are usually the most enlightening as to what's missing, at least for documentation, so no problem! Anyway, on to your question.

There are two ways for you to install the packages. Either manually via the ZIP you've downloaded or from the Python package index PyPI. To install the core via PyPI you can run the command pip install PyDMX (you may alternatively need to prefix the pip command with a python version python3 -m pip install PyDMX or with a number indicating python version pip3 install PyDMX). Or if you want to install and manage it yourself you can run the command pip install . (or prefixed as above if needed) when you're in the PyDMX/core directory you downloaded.

Once you've got the core installed, you can install the driver packages. The driver packages are add-ons which let you only install the drivers you need. For instance, if you only need the FTDI drivers you can get them via pip install PyDMX-Drivers-FTDI (prefixed as needed, you get the idea). Or you can run the command pip install . when you're in the PyDMX/drivers/ftdi directory you downloaded.

If you need a bit of background on what's going on with pip etc. take a look at the docs here. And here's the page for PyDMX on PyPI.

Hopefully that sheds some light on it. Once the packages are setup, you can take a look at the few examples in this repository to get a better idea of their use.

JMAlego avatar Sep 20 '22 21:09 JMAlego

Thank you for the detailed reply!

I installed the core using: pip3 install PyDMX I then installed the FDTI driving using: pip3 install PyDMX-Drivers-FTDI

I then navigated to the examples folder and opened simple.py in Thonny. I hit "run" and I get a pile of errors:

>>> %Run simple.py
Driver name checked
Traceback (most recent call last):
   File "/media/pi/GORILLA/prototype/dmx/220904a_PyDMX-master/examples/simple.py", line 38, in <module>
     sys_exit(main())
   File "/media/pi/GORILLA/prototype/dmx/220904a_PyDMX-master/examples/simple.py", line 9, in main
     with DMXInterface("AVRDMX") as interface:
   File "/home/pi/.local/lib/python3.7/site-packages/dmx/interface.py", line 47, in __init__
     self._set_device_driver(driver_name, *args, **kwards)
   File "/home/pi/.local/lib/python3.7/site-packages/dmx/interface.py", line 56, in _set_device_driver
     raise Exception("Unknown driver")
Exception: Unknown driver

Thoughts? Where should I go from here, or what should I try next?

static75 avatar Sep 21 '22 23:09 static75

If you take a look at the error you can see, following the traceback back, the line with DMXInterface("AVRDMX") as interface: gets mentioned. AVRDMX is a different driver that you didn't install. The simple sample file tries to use it as you can see here:

https://github.com/JMAlego/PyDMX/blob/8d5ad461dac2fbfb4e6d9397cbea22d9d756bbde/examples/simple.py#L12

So a simple next step for you to try is specifying a driver you have installed, so swap that "AVRDMX" for a "FT232R". Hopefully it doesn't complain about an unknown driver then (as you've mentioned installing the FTDI driver package).

JMAlego avatar Sep 22 '22 22:09 JMAlego

Success! Making that one small change actually did something! Question: How did you know to use "FT232R" in place of "AVRDMX"?
Is there a list somewhere of different interfaces to choose from?

When I run the sample.py code now, the light fixture points straight ahead, and the light beam is on. The next step is to figure out how to control the pan, tilt, dimmer, etc. The channels for my light fixture are likely different from the light fixture you used.

I'm not sure how you have it programmed, or how to reverse engineer what you've done. It appears you build a string of 16 lights, each with 3 channels? I only have one light fixture with 6 channels. Is there any way to massively simplify this sample.py code so that everything to control the light is in one python file?

static75 avatar Sep 23 '22 20:09 static75

If you take a look at the README for the FTDI driver (or take a look at the page on PyPI) you'll see it lists the names of drivers which are installed with the package. The main FTDI driver is called FT232R because that's the only chip I've actually tested it with! Though it should work on most FTDI serial chips and their clones.

Most simple USB to DMX interfaces use a USB to Serial chip (such as the FT232R) and a Serial to RS485 converter (such as the MAX485) with some extra protection etc. around the outside. That means usually if you have a simple USB to DMX interface there's a good chance the FT232R driver will work. The FTDI driver package is named after the company that makes the FT232R, Future Technology Devices International, at some point I might rename the driver to FTDI as well as the package...

If you take a look at core/dmx/Ilight.py you'll see there's a 7 slot (channel) light defined there which you could probably use to build a 6 channel light pretty easily. Fundamentally all the different light classes provide is a way to serialize some data into a series of slot values to be sent out over the DMX interface.

As for what the simple example does, we can get an even simpler example by looking in the README at the top level, it has this example:

from dmx import Colour, DMXInterface, DMXLight3Slot, DMXUniverse

PURPLE = Colour(255, 0, 255)

# Open an interface
with DMXInterface("FT232R") as interface:
    # Create a universe
    universe = DMXUniverse()

    # Define a light
    light = DMXLight3Slot(address=8)

    # Add the light to a universe
    universe.add_light(light)

    # Update the interface's frame to be the universe's current state
    interface.set_frame(universe.serialise())

    # Send an update to the DMX network
    interface.send_update()

    # Set light to purple
    light.set_colour(PURPLE)

    # Update the interface's frame to be the universe's current state
    interface.set_frame(universe.serialise())

    # Send an update to the DMX network
    interface.send_update()

I'll break it down a bit further.

from dmx import Colour, DMXInterface, DMXLight3Slot, DMXUniverse

Here we grab the imports we'll use, I'll cover them as they get used.

PURPLE = Colour(255, 0, 255)

Next we define the colour we're going to use.

# Open an interface
with DMXInterface("FT232R") as interface:

Then we open the interface. DMXInterface performs some magic based on the driver name you give it to find the class that implements that driver from all the possible driver classes you've installed. We can then write data directly to the interface via the interface variable (as we do later).

    # Create a universe
    universe = DMXUniverse()

Next we build a universe, this is a helper that keeps track of all the lights (and other devices) attached to your DMX network. We can use it to request the state of all the lights, squashed into one.

    # Define a light
    light = DMXLight3Slot(address=8)

Now we define our light and what channel it starts at e.g. it's address. We could use different light classes here, such as the DMXLight7Slot light.

    # Add the light to a universe
    universe.add_light(light)

Then we add the light to the universe, this let's the universe keep track of it (not that useful with just one light, more useful with multiple).

    # Update the interface's frame to be the universe's current state
    interface.set_frame(universe.serialise())

Here some actual work happens, we call universe.serialise() to get the value of every slot, so all the slot values for the lights with 0 values in unused slots. We then pass that data into interface.set_frame(...) which sets the interface's frame (it won't send an update out unless we ask it to though) e.g. we're saying to the interface "this should be the state of the DMX network when you next update it".

    # Send an update to the DMX network
    interface.send_update()

This line actually makes the interface send an update to the network! So when this is called the data in the interface's buffer gets sent to the driver module you specified which then does the necessary steps to transmit it over the network (via your USB to DMX interface).

    # Set light to purple
    light.set_colour(PURPLE)

    # Update the interface's frame to be the universe's current state
    interface.set_frame(universe.serialise())

    # Send an update to the DMX network
    interface.send_update()

We then set the colour of the light to purple and do the same steps again to update the network.

You can keep changing the state of the light and sending updates as long as you want. If you want to get fancy you can have the interface.send_update() code run in a different thread, but you probably don't need that.

As for all the code to control the lights in one file, you probably could merge all the files together, but you'd need to untangle the magic code in the interface that does driver dispatch. If you just want to see the code that sends the actual data (none of the helper classes like the interface, universe, and light classes) take a look in the driver implementation for the FT232R driver. The actual code is very simple and just needs the pylibftdi package and some of the support code from the driver, it looks like this: https://github.com/JMAlego/PyDMX/blob/8d5ad461dac2fbfb4e6d9397cbea22d9d756bbde/drivers/ftdi/dmx_drivers/ftdi/ft232r.py#L133-L142 if you read it and the description of the DMX protocol from the main repo README it should make sense how it lines up and works!

JMAlego avatar Sep 24 '22 00:09 JMAlego

After some experimentation, I realized that the DMXLight7Slot example is actually using the DMXLight3Slot as a baseline and then adds rotation and opacity (thus 7 slots!). From there, I was able to spoof the meaning of each slot to control the pan, tilt, and brightness of my particular DMX light in a meaningful way. (for example, my light has pan and tilt in slots 1 and 2 respectively).

A couple of questions: In your simple.py it appears there are two lines of code that are extra/rogue:

random_colour = Colour(0x88, 0x26, 0xff)
light.set_colour(random_color)

These lines of code appear outside the For loop, and I didn't see any particular reason why. I wasn't sure if this was just a copy paste error, or if they have some specific meaning outside the loop. I commented them out and everything still seems to work fine.

Second question: Can you explain the rational behind your sleep timer? sleep(0.5 - (15.0 / 1000.0)) Why divide 15 by 1000 and then subtract that from half a second? Why not use a while True: loop for simplicity with a sleep timer of (0.1) to set the frame and send the update?

while True:
    interface.set_frame(universe.serialise())
    interface.send_update()
    sleep(0.1)

Thank you for all your help thus far!

static75 avatar Oct 04 '22 02:10 static75