Initial DMX (Enttec OpenDMX USB) support and serial_port improvements

* Support DMX RGB lights (PAR lights, spotlights, wash lights, etc)
  * Configurable R/G/B channel and Brightness/Master channel
  * Add configurable parameters to serial_port needed to configure a port for DMX
  * Add DMX tab to settings
This commit is contained in:
Adam Honse 2023-05-06 08:06:19 +00:00
parent 8b4b2bacbc
commit 81aaf67ff0
14 changed files with 1318 additions and 48 deletions

View file

@ -17,9 +17,13 @@
serial_port::serial_port()
{
/*-----------------------------------------------------*\
| Set a default baud rate |
| Set default port configuration but do not open |
\*-----------------------------------------------------*/
baud_rate = 9600;
baud_rate = 9600;
parity = SERIAL_PORT_PARITY_NONE;
size = SERIAL_PORT_SIZE_8;
stop_bits = SERIAL_PORT_STOP_BITS_1;
flow_control = true;
}
/*---------------------------------------------------------*\
@ -29,7 +33,44 @@ serial_port::serial_port()
\*---------------------------------------------------------*/
serial_port::serial_port(const char * name, unsigned int baud)
{
serial_open(name, baud);
/*-----------------------------------------------------*\
| Set default port configuration and open |
\*-----------------------------------------------------*/
baud_rate = baud;
parity = SERIAL_PORT_PARITY_NONE;
size = SERIAL_PORT_SIZE_8;
stop_bits = SERIAL_PORT_STOP_BITS_1;
flow_control = true;
serial_open(name);
}
/*---------------------------------------------------------*\
| serial_port (constructor) |
| When created with port information, the constructor |
| will automatically open port <name> at baud rate <baud>|
| with the given port configuration |
\*---------------------------------------------------------*/
serial_port::serial_port
(
const char * name,
unsigned int baud,
serial_port_parity parity,
serial_port_size size,
serial_port_stop_bits stop_bits,
bool flow_control
)
{
/*-----------------------------------------------------*\
| Set default port configuration and open |
\*-----------------------------------------------------*/
this->baud_rate = baud;
this->parity = parity;
this->size = size;
this->stop_bits = stop_bits;
this->flow_control = flow_control;
serial_open(name);
}
/*---------------------------------------------------------*\
@ -58,6 +99,9 @@ bool serial_port::serial_open()
char full_path[100];
snprintf(full_path, sizeof(full_path), "\\\\.\\%s", port_name);
/*-----------------------------------------*\
| Open the port read/write |
\*-----------------------------------------*/
file_descriptor = CreateFile(full_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if((int)file_descriptor < 0)
@ -65,29 +109,106 @@ bool serial_port::serial_open()
return false;
}
/*-----------------------------------------*\
| Get the port configuration options |
\*-----------------------------------------*/
SetupComm(file_descriptor, 1, 128);
GetCommState(file_descriptor, &dcb);
dcb.BaudRate = baud_rate; //Set baud rate
dcb.ByteSize = 8; //8 data bits
dcb.Parity = NOPARITY; //Parity = none
dcb.StopBits = ONESTOPBIT; //One stop bit
dcb.fAbortOnError = TRUE; //Abort on error
/*-----------------------------------------*\
| Configure baud rate |
\*-----------------------------------------*/
dcb.BaudRate = baud_rate;
/*-----------------------------------------*\
| Configure parity |
\*-----------------------------------------*/
switch(parity)
{
case SERIAL_PORT_PARITY_NONE:
dcb.Parity = NOPARITY;
break;
case SERIAL_PORT_PARITY_ODD:
dcb.Parity = ODDPARITY;
break;
case SERIAL_PORT_PARITY_EVEN:
dcb.Parity = EVENPARITY;
break;
}
/*-----------------------------------------*\
| Configure size |
\*-----------------------------------------*/
switch(size)
{
case SERIAL_PORT_SIZE_8:
dcb.ByteSize = 8;
break;
case SERIAL_PORT_SIZE_7:
dcb.ByteSize = 7;
break;
case SERIAL_PORT_SIZE_6:
dcb.ByteSize = 6;
break;
case SERIAL_PORT_SIZE_5:
dcb.ByteSize = 5;
break;
}
/*-----------------------------------------*\
| Configure stop bits |
\*-----------------------------------------*/
if(stop_bits == SERIAL_PORT_STOP_BITS_2)
{
dcb.StopBits = TWOSTOPBITS;
}
else
{
dcb.StopBits = ONESTOPBIT;
}
/*-----------------------------------------*\
| Configure flow control |
\*-----------------------------------------*/
if(flow_control)
{
dcb.fRtsControl = RTS_CONTROL_ENABLE;
}
else
{
dcb.fRtsControl = RTS_CONTROL_DISABLE;
}
/*-----------------------------------------*\
| Configure additional parameters |
\*-----------------------------------------*/
dcb.fAbortOnError = FALSE; //Abort on error
dcb.fOutX = FALSE; //XON/XOFF off for transmit
dcb.fInX = FALSE; //XON/XOFF off for receive
dcb.fOutxCtsFlow = FALSE; //Turn off CTS flow control
dcb.fRtsControl = RTS_CONTROL_DISABLE; //Options DISABLE, ENABLE, HANDSHAKE
dcb.fOutxDsrFlow = FALSE; //Turn off DSR flow control
dcb.fDtrControl = DTR_CONTROL_DISABLE; //Disable DTR control
/*-----------------------------------------*\
| Set the port configuration options |
\*-----------------------------------------*/
SetCommState(file_descriptor, &dcb);
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant=50;
timeouts.ReadTotalTimeoutMultiplier=10;
timeouts.WriteTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=10;
/*-----------------------------------------*\
| Set the port timeouts |
\*-----------------------------------------*/
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(file_descriptor, &timeouts);
#endif
@ -95,6 +216,9 @@ bool serial_port::serial_open()
| Linux-specific code path for serial port opening |
\*-----------------------------------------------------*/
#ifdef __linux__
/*-----------------------------------------*\
| Open the port read/write with no delay |
\*-----------------------------------------*/
file_descriptor = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
if(file_descriptor < 0)
@ -102,19 +226,105 @@ bool serial_port::serial_open()
return false;
}
/*-----------------------------------------*\
| Get the port configuration options |
\*-----------------------------------------*/
struct termios2 options;
ioctl(file_descriptor, TCGETS2, &options);
/*-----------------------------------------*\
| Configure baud rate |
\*-----------------------------------------*/
options.c_cflag &= ~CBAUD;
options.c_cflag |= BOTHER;
options.c_cflag |= CBAUDEX;
options.c_ispeed = baud_rate;
options.c_ospeed = baud_rate;
/*-----------------------------------------*\
| Configure parity |
\*-----------------------------------------*/
switch(parity)
{
case SERIAL_PORT_PARITY_NONE:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~PARODD;
break;
case SERIAL_PORT_PARITY_ODD:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
case SERIAL_PORT_PARITY_EVEN:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
break;
}
/*-----------------------------------------*\
| Configure size |
\*-----------------------------------------*/
options.c_cflag &= ~CSIZE;
switch(size)
{
case SERIAL_PORT_SIZE_8:
options.c_cflag |= CS8;
break;
case SERIAL_PORT_SIZE_7:
options.c_cflag |= CS7;
break;
case SERIAL_PORT_SIZE_6:
options.c_cflag |= CS6;
break;
case SERIAL_PORT_SIZE_5:
options.c_cflag |= CS5;
break;
}
/*-----------------------------------------*\
| Configure stop bits |
\*-----------------------------------------*/
if(stop_bits == SERIAL_PORT_STOP_BITS_2)
{
options.c_cflag |= CSTOPB;
}
else
{
options.c_cflag &= ~CSTOPB;
}
/*-----------------------------------------*\
| Configure flow control |
\*-----------------------------------------*/
if(flow_control)
{
options.c_cflag |= CRTSCTS;
}
else
{
options.c_cflag &= ~CRTSCTS;
}
/*-----------------------------------------*\
| Configure additional parameters |
\*-----------------------------------------*/
options.c_lflag &= ~ICANON;
options.c_lflag &= ~ECHO; // Disable echo
options.c_lflag &= ~ECHOE; // Disable erasure
options.c_lflag &= ~ECHONL; // Disable new-line echo
options.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
options.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
options.c_ispeed = baud_rate;
options.c_ospeed = baud_rate;
options.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
/*-----------------------------------------*\
| Set the port configuration options |
\*-----------------------------------------*/
ioctl(file_descriptor, TCSETS2, &options);
#endif
@ -122,6 +332,9 @@ bool serial_port::serial_open()
| MacOS-specific code path for serial port opening |
\*-----------------------------------------------------*/
#ifdef __APPLE__
/*-----------------------------------------*\
| Open the port read/write with no delay |
\*-----------------------------------------*/
file_descriptor = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
if(file_descriptor < 0)
@ -129,53 +342,107 @@ bool serial_port::serial_open()
return false;
}
/*-----------------------------------------*\
| Get the port configuration options |
\*-----------------------------------------*/
struct termios options;
tcgetattr(file_descriptor, &options);
switch(baud_rate)
/*-----------------------------------------*\
| Configure parity |
\*-----------------------------------------*/
switch(parity)
{
case 9600:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
case SERIAL_PORT_PARITY_NONE:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~PARODD;
break;
case 19200:
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
case SERIAL_PORT_PARITY_ODD:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
case 115200:
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
break;
case 38400:
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
break;
case 57600:
cfsetispeed(&options, B57600);
cfsetospeed(&options, B57600);
break;
default:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
case SERIAL_PORT_PARITY_EVEN:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
break;
}
/*-----------------------------------------------------*\
| Configure other settings |
\*-----------------------------------------------------*/
/*-----------------------------------------*\
| Configure size |
\*-----------------------------------------*/
options.c_cflag &= ~CSIZE;
switch(size)
{
case SERIAL_PORT_SIZE_8:
options.c_cflag |= CS8;
break;
case SERIAL_PORT_SIZE_7:
options.c_cflag |= CS7;
break;
case SERIAL_PORT_SIZE_6:
options.c_cflag |= CS6;
break;
case SERIAL_PORT_SIZE_5:
options.c_cflag |= CS5;
break;
}
/*-----------------------------------------*\
| Configure stop bits |
\*-----------------------------------------*/
if(stop_bits == SERIAL_PORT_STOP_BITS_2)
{
options.c_cflag |= CSTOPB;
}
else
{
options.c_cflag &= ~CSTOPB;
}
/*-----------------------------------------*\
| Configure flow control |
\*-----------------------------------------*/
if(flow_control)
{
options.c_cflag |= CRTSCTS;
}
else
{
options.c_cflag &= ~CRTSCTS;
}
/*-----------------------------------------*\
| Configure additional parameters |
\*-----------------------------------------*/
options.c_lflag &= ~(ICANON | ISIG | ECHO);
options.c_iflag &= ~(INLCR | ICRNL);
options.c_iflag |= IGNPAR | IGNBRK;
options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
options.c_cflag &= ~(PARENB | PARODD | CSTOPB | CSIZE | CRTSCTS);
options.c_cflag |= CLOCAL | CREAD | CS8;
options.c_lflag &= ~(ICANON | ISIG | ECHO);
options.c_cc[VTIME] = 1;
options.c_cc[VMIN] = 0;
/*-----------------------------------------*\
| Set the port configuration options |
\*-----------------------------------------*/
if(tcsetattr(file_descriptor, TCSANOW, &options) < 0)
{
close(file_descriptor);
return false;
}
/*-----------------------------------------*\
| Configure baud rate |
\*-----------------------------------------*/
ioctl(file_descriptor, IOSSIOSPEED, &baud_rate);
#endif
/*-----------------------------------------------------*\
@ -357,3 +624,85 @@ void serial_port::serial_flush_tx()
tcflush(file_descriptor, TCOFLUSH);
#endif
}
/*---------------------------------------------------------*\
| serial_break |
\*---------------------------------------------------------*/
void serial_port::serial_break()
{
/*-----------------------------------------------------*\
| Windows-specific code path for serial break |
\*-----------------------------------------------------*/
#ifdef _WIN32
SetCommBreak(file_descriptor);
Sleep(1);
ClearCommBreak(file_descriptor);
#endif
/*-----------------------------------------------------*\
| Linux-specific code path for serial break |
\*-----------------------------------------------------*/
#ifdef __linux__
//Send break for at least 1 ms
ioctl(file_descriptor, TIOCSBRK);
usleep(1000);
ioctl(file_descriptor, TIOCCBRK);
#endif
/*-----------------------------------------------------*\
| MacOS-specific code path for serial break |
\*-----------------------------------------------------*/
#ifdef __APPLE__
//Send break for at least 1 ms
ioctl(file_descriptor, TIOCSBRK);
usleep(1000);
ioctl(file_descriptor, TIOCCBRK);
#endif
}
void serial_port::serial_set_rts(bool rts)
{
/*-----------------------------------------------------*\
| Windows-specific code path for serial set RTS |
\*-----------------------------------------------------*/
#ifdef _WIN32
if(rts)
{
EscapeCommFunction(file_descriptor, SETRTS);
}
else
{
EscapeCommFunction(file_descriptor, CLRRTS);
}
#endif
/*-----------------------------------------------------*\
| Linux-specific code path for serial set RTS |
\*-----------------------------------------------------*/
#ifdef __linux__
const int RTSFLAG = TIOCM_RTS;
if(rts)
{
ioctl(file_descriptor, TIOCMBIS, &RTSFLAG);
}
else
{
ioctl(file_descriptor, TIOCMBIC, &RTSFLAG);
}
#endif
/*-----------------------------------------------------*\
| MacOS-specific code path for serial set RTS |
\*-----------------------------------------------------*/
#ifdef __APPLE__
const int RTSFLAG = TIOCM_RTS;
if(rts)
{
ioctl(file_descriptor, TIOCMBIS, &RTSFLAG);
}
else
{
ioctl(file_descriptor, TIOCMBIC, &RTSFLAG);
}
#endif
}