Compare commits
1 Commits
master
...
feat/port-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2445ce5868 |
86
src/dmx.cpp
86
src/dmx.cpp
@ -2,62 +2,26 @@
|
||||
// Created by illyum on 8/15/2024.
|
||||
//
|
||||
|
||||
#include "dmx.h"
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "windows.h"
|
||||
|
||||
DMXEngine::DMXEngine(const char* portName, int baudRate, int dataSize)
|
||||
: running(false), buffer(dataSize, 0) {
|
||||
hSerial = CreateFileA(portName, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (hSerial == INVALID_HANDLE_VALUE) {
|
||||
std::cerr << "Error opening " << portName << " port" << std::endl;
|
||||
#include "dmx.h"
|
||||
#include "serial_port_factory.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
DMXEngine::DMXEngine(const char* portName)
|
||||
: running(false), buffer(DMX_CHANNELS, 0) {
|
||||
serialPort = SerialPortFactory::createSerialPort();
|
||||
if (!serialPort->open(portName, DMX_BAUD_RATE)) {
|
||||
std::cerr << "Failed to open serial port" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
configurePort();
|
||||
}
|
||||
|
||||
DMXEngine::~DMXEngine() {
|
||||
if (running) {
|
||||
stop();
|
||||
}
|
||||
CloseHandle(hSerial);
|
||||
}
|
||||
|
||||
void DMXEngine::configurePort() {
|
||||
DCB dcbSerialParams = { 0 };
|
||||
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
||||
if (!GetCommState(hSerial, &dcbSerialParams)) {
|
||||
std::cerr << "Error getting COM port state" << std::endl;
|
||||
CloseHandle(hSerial);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dcbSerialParams.BaudRate = 250000;
|
||||
dcbSerialParams.ByteSize = 8;
|
||||
dcbSerialParams.StopBits = TWOSTOPBITS;
|
||||
dcbSerialParams.Parity = NOPARITY;
|
||||
|
||||
if (!SetCommState(hSerial, &dcbSerialParams)) {
|
||||
std::cerr << "Error setting COM port state" << std::endl;
|
||||
CloseHandle(hSerial);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
COMMTIMEOUTS timeouts = { 0 };
|
||||
timeouts.ReadIntervalTimeout = 50;
|
||||
timeouts.ReadTotalTimeoutConstant = 50;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||
timeouts.WriteTotalTimeoutConstant = 50;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 10;
|
||||
|
||||
if (!SetCommTimeouts(hSerial, &timeouts)) {
|
||||
std::cerr << "Error setting COM port timeouts" << std::endl;
|
||||
CloseHandle(hSerial);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
delete serialPort;
|
||||
}
|
||||
|
||||
void DMXEngine::start() {
|
||||
@ -74,26 +38,21 @@ void DMXEngine::stop() {
|
||||
DMXEngine::dumpZeros();
|
||||
}
|
||||
|
||||
|
||||
void DMXEngine::setChannelValue(int channel, unsigned char value) {
|
||||
if (channel >= 0 && channel < buffer.size()) {
|
||||
if (channel >= 0 && channel < DMX_CHANNELS) {
|
||||
buffer[channel] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void DMXEngine::sendDMXData() {
|
||||
while (running) {
|
||||
// Break condition
|
||||
EscapeCommFunction(hSerial, SETBREAK);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
EscapeCommFunction(hSerial, CLRBREAK);
|
||||
serialPort->sendBreak();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
||||
|
||||
DWORD bytesWritten;
|
||||
unsigned char startCode = 0;
|
||||
WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL);
|
||||
WriteFile(hSerial, buffer.data(), buffer.size(), &bytesWritten, NULL);
|
||||
std::vector<unsigned char> data = {0}; // Start code
|
||||
data.insert(data.end(), buffer.begin(), buffer.end());
|
||||
|
||||
serialPort->writeData(data);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
}
|
||||
}
|
||||
@ -101,17 +60,16 @@ void DMXEngine::sendDMXData() {
|
||||
void DMXEngine::dumpZeros() {
|
||||
std::fill(buffer.begin(), buffer.end(), 0);
|
||||
|
||||
EscapeCommFunction(hSerial, SETBREAK);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
EscapeCommFunction(hSerial, CLRBREAK);
|
||||
serialPort->sendBreak();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
||||
|
||||
DWORD bytesWritten;
|
||||
unsigned char startCode = 0;
|
||||
WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL);
|
||||
WriteFile(hSerial, buffer.data(), buffer.size(), &bytesWritten, NULL);
|
||||
std::vector<unsigned char> data = {0}; // Start code
|
||||
data.insert(data.end(), buffer.begin(), buffer.end());
|
||||
|
||||
serialPort->writeData(data);
|
||||
}
|
||||
|
||||
void DMXEngine::dmxThreadFunc(DMXEngine* engine) {
|
||||
engine->sendDMXData();
|
||||
}
|
||||
|
||||
|
13
src/dmx.h
13
src/dmx.h
@ -9,6 +9,9 @@
|
||||
#include <tuple>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include "serial_port.h"
|
||||
|
||||
const int DMX_BAUD_RATE = 250000;
|
||||
|
||||
struct DMXFixture {
|
||||
unsigned char r, g, b;
|
||||
@ -17,7 +20,7 @@ struct DMXFixture {
|
||||
|
||||
class DMXEngine {
|
||||
public:
|
||||
explicit DMXEngine(const char* portName, int baudRate = 250000, int dataSize = 512);
|
||||
DMXEngine(const char* portName);
|
||||
~DMXEngine();
|
||||
|
||||
void start();
|
||||
@ -25,15 +28,17 @@ public:
|
||||
void setChannelValue(int channel, unsigned char value);
|
||||
|
||||
private:
|
||||
void configurePort();
|
||||
void sendDMXData();
|
||||
void dumpZeros();
|
||||
void configurePort();
|
||||
static void dmxThreadFunc(DMXEngine* engine);
|
||||
|
||||
void* hSerial;
|
||||
std::atomic<bool> running;
|
||||
SerialPort* serialPort;
|
||||
std::vector<unsigned char> buffer;
|
||||
std::thread dmxThread;
|
||||
bool running;
|
||||
|
||||
static const int DMX_CHANNELS = 512;
|
||||
};
|
||||
|
||||
#endif // DMX_H
|
||||
|
22
src/serial_port.h
Normal file
22
src/serial_port.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by illyum on 8/16/2024.
|
||||
//
|
||||
|
||||
#ifndef SERIAL_PORT_H
|
||||
#define SERIAL_PORT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SerialPort {
|
||||
public:
|
||||
virtual ~SerialPort() = default;
|
||||
|
||||
virtual bool open(const std::string& portName, int baudRate) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual bool configure(int baudRate, int dataBits, int stopBits, int parity) = 0;
|
||||
virtual bool writeData(const std::vector<unsigned char>& data) = 0;
|
||||
virtual bool sendBreak() = 0;
|
||||
};
|
||||
|
||||
#endif // SERIAL_PORT_H
|
25
src/serial_port_factory.h
Normal file
25
src/serial_port_factory.h
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by illyum on 8/16/2024.
|
||||
//
|
||||
|
||||
#ifndef SERIAL_PORT_FACTORY_H
|
||||
#define SERIAL_PORT_FACTORY_H
|
||||
|
||||
#include "serial_port.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "serial_port_windows.cpp"
|
||||
typedef WindowsSerialPort PlatformSerialPort;
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
#include "serial_port_posix.cpp"
|
||||
typedef PosixSerialPort PlatformSerialPort;
|
||||
#endif
|
||||
|
||||
class SerialPortFactory {
|
||||
public:
|
||||
static SerialPort* createSerialPort() {
|
||||
return new PlatformSerialPort();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SERIAL_PORT_FACTORY_H
|
83
src/serial_port_posix.cpp
Normal file
83
src/serial_port_posix.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// Created by illyum on 8/16/2024.
|
||||
//
|
||||
|
||||
// FIXME:
|
||||
// I don't have a mac to test it on so... I have zero idea if
|
||||
// this will work, let alone compile
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include "serial_port.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <iostream>
|
||||
|
||||
class PosixSerialPort : public SerialPort {
|
||||
public:
|
||||
PosixSerialPort() : fd(-1) {}
|
||||
|
||||
~PosixSerialPort() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool open(const std::string& portName, int baudRate) override {
|
||||
fd = ::open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (fd == -1) {
|
||||
std::cerr << "Error opening " << portName << " port" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return configure(baudRate, CS8, 1, 0);
|
||||
}
|
||||
|
||||
void close() override {
|
||||
if (fd != -1) {
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool configure(int baudRate, int dataBits, int stopBits, int parity) override {
|
||||
struct termios options;
|
||||
if (tcgetattr(fd, &options) < 0) {
|
||||
std::cerr << "Error getting COM port attributes" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
cfsetispeed(&options, baudRate);
|
||||
cfsetospeed(&options, baudRate);
|
||||
|
||||
options.c_cflag &= ~CSIZE;
|
||||
options.c_cflag |= dataBits;
|
||||
|
||||
options.c_cflag &= ~PARENB;
|
||||
if (parity) options.c_cflag |= PARENB;
|
||||
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
if (stopBits == 2) options.c_cflag |= CSTOPB;
|
||||
|
||||
options.c_cflag |= CLOCAL | CREAD;
|
||||
|
||||
options.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
options.c_oflag &= ~OPOST;
|
||||
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = 5;
|
||||
|
||||
tcflush(fd, TCIFLUSH);
|
||||
return tcsetattr(fd, TCSANOW, &options) == 0;
|
||||
}
|
||||
|
||||
bool writeData(const std::vector<unsigned char>& data) override {
|
||||
return ::write(fd, data.data(), data.size()) != -1;
|
||||
}
|
||||
|
||||
bool sendBreak() override {
|
||||
return tcsendbreak(fd, 0) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
#endif
|
78
src/serial_port_windows.cpp
Normal file
78
src/serial_port_windows.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// Created by illyum on 8/16/2024.
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "serial_port.h"
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
class WindowsSerialPort : public SerialPort {
|
||||
public:
|
||||
WindowsSerialPort() : hSerial(INVALID_HANDLE_VALUE) {}
|
||||
|
||||
~WindowsSerialPort() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool open(const std::string& portName, int baudRate) override {
|
||||
hSerial = CreateFileA(portName.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hSerial == INVALID_HANDLE_VALUE) {
|
||||
std::cerr << "Error opening " << portName << " port" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return configure(baudRate, 8, TWOSTOPBITS, NOPARITY);
|
||||
}
|
||||
|
||||
void close() override {
|
||||
if (hSerial != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hSerial);
|
||||
hSerial = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool configure(int baudRate, int dataBits, int stopBits, int parity) override {
|
||||
DCB dcbSerialParams = { 0 };
|
||||
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
||||
if (!GetCommState(hSerial, &dcbSerialParams)) {
|
||||
std::cerr << "Error getting COM port state" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
dcbSerialParams.BaudRate = baudRate;
|
||||
dcbSerialParams.ByteSize = dataBits;
|
||||
dcbSerialParams.StopBits = stopBits;
|
||||
dcbSerialParams.Parity = parity;
|
||||
|
||||
if (!SetCommState(hSerial, &dcbSerialParams)) {
|
||||
std::cerr << "Error setting COM port state" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
COMMTIMEOUTS timeouts = { 0 };
|
||||
timeouts.ReadIntervalTimeout = 50;
|
||||
timeouts.ReadTotalTimeoutConstant = 50;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||
timeouts.WriteTotalTimeoutConstant = 50;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 10;
|
||||
|
||||
return SetCommTimeouts(hSerial, &timeouts);
|
||||
}
|
||||
|
||||
bool writeData(const std::vector<unsigned char>& data) override {
|
||||
DWORD bytesWritten;
|
||||
return WriteFile(hSerial, data.data(), data.size(), &bytesWritten, nullptr);
|
||||
}
|
||||
|
||||
bool sendBreak() override {
|
||||
EscapeCommFunction(hSerial, SETBREAK);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
EscapeCommFunction(hSerial, CLRBREAK);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE hSerial;
|
||||
};
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user