Skip to content

Commit

Permalink
Adding support for the DMX USB Pro Mk2
Browse files Browse the repository at this point in the history
  • Loading branch information
Pablo Barton committed Jan 28, 2022
1 parent 6eb908a commit 5e11c27
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 22 deletions.
47 changes: 26 additions & 21 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,36 @@

from setuptools import setup

requires = ['pyserial']
requires = ["pyserial"]


def load_readme():
with open('README.md') as f:
with open("README.md") as f:
readme = f.read()
return readme


setup(name='DMXEnttecPro',
version='0.4',
description='Python control of the Enttec DMX USB Pro',
author='Paul Barton',
author_email='[email protected]',
license='GPL3',
package_dir={'': 'src'},
packages=['DMXEnttecPro'],
install_requires=requires,
long_description=load_readme(),
long_description_content_type='text/markdown',
classifiers=["Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
"Development Status :: 4 - Beta",
]
)
setup(
name="DMXEnttecPro",
version="0.4",
description="Python control of the Enttec DMX USB Pro, including Mk2",
author="Paul Barton",
author_email="[email protected]",
license="GPL3",
package_dir={"": "src"},
packages=["DMXEnttecPro"],
install_requires=requires,
long_description=load_readme(),
long_description_content_type="text/markdown",
classifiers=[
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
"Development Status :: 4 - Beta",
],
)
3 changes: 2 additions & 1 deletion src/DMXEnttecPro/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
Defines the Controller
"""
from typing import Optional

import serial
from functools import wraps
Expand Down Expand Up @@ -94,7 +95,7 @@ def set_dmx_parameters(
output_break_time: int = 9,
mab_time: int = 1,
output_rate: int = 40,
user_defined_bytes=None,
user_defined_bytes: Optional[bytearray] = None,
):
"""
Transmit a message to the Enttec DMX USB Pro to configure some
Expand Down
Empty file.
120 changes: 120 additions & 0 deletions src/DMXEnttecPro/controllers/mk2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from enum import Enum


# Message Labels <= 12 correspond to the v1 API. > 12 correspond to v2 API
from typing import Optional

import serial

from DMXEnttecPro.utils import (
least_significant_bit_for_size,
most_significant_bit_for_size,
)


class Mk2MessageLabels(int, Enum):
GET_PORT_WIDGET_PARAMETERS_REQUEST_REPLY_PORT1 = 3
GET_PORT_WIDGET_PARAMETERS_REQUEST_REPLY_PORT2 = 196
SET_PORT_WIDGET_PARAMETERS_REQUEST_PORT1 = 4
SET_PORT_WIDGET_PARAMETERS_REQUEST_PORT2 = 156
RECEIVED_DMX_PACKET_PORT1 = 5
RECEIVED_DMX_PACKET_PORT2 = 210
OUTPUT_ONLY_SEND_DMX_PACKET_REQUEST_PORT1 = 6
OUTPUT_ONLY_SEND_DMX_PACKET_REQUEST_PORT2 = 132
SEND_RDM_PACKET_REQUEST_PORT1 = 7
SEND_RDM_PACKET_REQUEST_PORT2 = 226
RECEIVE_DMX_ON_CHANGE_PORT1 = 8
RECEIVE_DMX_ON_CHANGE_PORT2 = 128
RECEIVED_DMX_CHANGE_OF_STATE_PACKET_PORT1 = 9
RECEIVED_DMX_CHANGE_OF_STATE_PACKET_PORT2 = 22
GET_WIDGET_SERIAL_NUMBER_REQUEST_REPLY = 10
SEND_RDM_DISCOVERY_REQUEST_PORT1 = 11
SEND_RDM_DISCOVERY_REQUEST_PORT2 = 208
RDM_CONTROLLER_RECEIVE_TIMEOUT_PORT1 = 12
RDM_CONTROLLER_RECEIVE_TIMEOUT_PORT2 = 209
SET_API_KEY_REQUEST = 13
QUERY_HARDWARE_VERSION_REQUEST_REPLY = 14
GET_PORT_ASSIGNMENT_REQUEST_REPLY = 220
SET_PORT_ASSIGNMENT_REQUEST = 201
RECEIVED_MIDI = 225
SEND_MIDI_REQUEST = 191
SHOW_QUERY_REQUEST_REPLY = 139
SHOW_BLOCK_ERASE_REQUEST = 129 # Distinguished by subcommand keyword "ERAS"
SHOW_SECTOR_ERASE_REQUEST = 129 # Distinguished by subcommand keyword "ERSE"
SHOW_WRITE_REQUEST = 129 # Distinguished by subcommand keyword "WRIT"
SHOW_READ_REQUEST_REPLY = 203
START_SHOW_REQUEST = 129 # Distinguished by subcommand keyword "STAR"
STOP_SHOW_REQUEST = 129 # Distinguished by subcommand keyword "STOP"


class Mk2Controller(object):
def __init__(
self,
port_string: str,
dmx_size: int = 512,
baudrate: int = 57600,
timeout: int = 1,
auto_submit: bool = False,
):
if not (24 <= dmx_size <= 512):
raise ValueError("Size of DMX channel frame must be between 24 and 512")
self.dmx_size = dmx_size
self.baudrate = baudrate
self.timeout = timeout
self.auto_submit = auto_submit

self._conn = serial.Serial(
port_string, baudrate=self.baudrate, timeout=self.timeout
)

self.channels = bytearray(self.dmx_size)
self._last_submitted_channels = bytearray(self.dmx_size)
self._signal_start = bytearray([0x7E])
self._signal_end = bytearray([0xE7])

def set_port_widget_parameters(
self,
*,
port: int = 1,
break_time: int = 9,
mab_time: int = 1,
rate: int = 40,
user_defined_bytes: Optional[bytearray] = None,
):
if user_defined_bytes is None:
user_defined_bytes = bytearray()
else:
if len(user_defined_bytes) > 512:
raise ValueError(
"Length of user_defined_bytes must not be greater than 512"
)
if port not in (1, 2):
raise ValueError("port must be 1 or 2")
if not (9 <= break_time <= 127):
raise ValueError("output_break_time must be between 9 and 127")
if not (1 <= mab_time <= 127):
raise ValueError("mab_time must be between 1 and 127")
if not (0 <= rate <= 40):
raise ValueError("output_rate must be between 0 and 40")
label = Mk2MessageLabels.SET_PORT_WIDGET_PARAMETERS_REQUEST_PORT1
if port == 2:
label = Mk2MessageLabels.SET_PORT_WIDGET_PARAMETERS_REQUEST_PORT2
udb_len = len(user_defined_bytes)
msg = (
self._signal_start
+ bytearray(
[
label.value,
least_significant_bit_for_size(udb_len + 5),
most_significant_bit_for_size(udb_len + 5),
least_significant_bit_for_size(udb_len),
most_significant_bit_for_size(udb_len),
break_time,
mab_time,
rate,
]
)
+ user_defined_bytes
+ self._signal_end
)
self._conn.write(msg)
8 changes: 8 additions & 0 deletions src/DMXEnttecPro/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ def get_port_by_product_id(product_id: int) -> str:
raise ValueError("No COM device found with product id {}".format(product_id))


def least_significant_bit_for_size(size: int) -> int:
return size & 0xFF


def most_significant_bit_for_size(size: int) -> int:
return (size >> 8) & 0xFF


def show_port_details():
"""
Print a listing of all COM ports PySerial can find. Useful for determining
Expand Down

0 comments on commit 5e11c27

Please sign in to comment.