diff --git a/Controllers/DMXController/DMXControllerDetect.cpp b/Controllers/DMXController/DMXControllerDetect.cpp new file mode 100644 index 00000000..0ecba081 --- /dev/null +++ b/Controllers/DMXController/DMXControllerDetect.cpp @@ -0,0 +1,142 @@ +#include "Detector.h" +#include "RGBController.h" +#include "RGBController_DMX.h" +#include "SettingsManager.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +/******************************************************************************************\ +* * +* DetectDMXControllers * +* * +* Detect devices supported by the DMX driver * +* * +\******************************************************************************************/ + +void DetectDMXControllers() +{ + json dmx_settings; + + std::vector> device_lists; + DMXDevice dev; + + /*-------------------------------------------------*\ + | Get DMX settings from settings manager | + \*-------------------------------------------------*/ + dmx_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DMXDevices"); + + /*-------------------------------------------------*\ + | If the DMX settings contains devices, process | + \*-------------------------------------------------*/ + if(dmx_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < dmx_settings["devices"].size(); device_idx++) + { + /*-------------------------------------------------*\ + | Clear DMX device data | + \*-------------------------------------------------*/ + dev.name = ""; + dev.keepalive_time = 0; + + if(dmx_settings["devices"][device_idx].contains("name")) + { + dev.name = dmx_settings["devices"][device_idx]["name"]; + } + + if(dmx_settings["devices"][device_idx].contains("port")) + { + dev.port = dmx_settings["devices"][device_idx]["port"]; + } + + if(dmx_settings["devices"][device_idx].contains("keepalive_time")) + { + dev.keepalive_time = dmx_settings["devices"][device_idx]["keepalive_time"]; + } + + if(dmx_settings["devices"][device_idx].contains("red_channel")) + { + dev.red_channel = dmx_settings["devices"][device_idx]["red_channel"]; + } + + if(dmx_settings["devices"][device_idx].contains("green_channel")) + { + dev.green_channel = dmx_settings["devices"][device_idx]["green_channel"]; + } + + if(dmx_settings["devices"][device_idx].contains("blue_channel")) + { + dev.blue_channel = dmx_settings["devices"][device_idx]["blue_channel"]; + } + + if(dmx_settings["devices"][device_idx].contains("brightness_channel")) + { + dev.brightness_channel = dmx_settings["devices"][device_idx]["brightness_channel"]; + } + + /*---------------------------------------------------------*\ + | Determine whether to create a new list or add this device | + | to an existing list. A device is added to an existing | + | list if both devices share one or more universes for the | + | same output destination | + \*---------------------------------------------------------*/ + bool device_added_to_existing_list = false; + + /*---------------------------------------------------------*\ + | Track grouping for all controllers. | + \*---------------------------------------------------------*/ + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + for(unsigned int device_idx = 0; device_idx < device_lists[list_idx].size(); device_idx++) + { + /*---------------------------------------------------------*\ + | Check if the port used by this new device is the same as | + | in the existing device. If so, add the new device to the | + | existing list. | + \*---------------------------------------------------------*/ + if(1) + { + device_lists[list_idx].push_back(dev); + device_added_to_existing_list = true; + break; + } + } + + if(device_added_to_existing_list) + { + break; + } + } + + /*---------------------------------------------------------*\ + | If the device did not overlap with existing devices, | + | create a new list for it | + \*---------------------------------------------------------*/ + if(!device_added_to_existing_list) + { + std::vector new_list; + + new_list.push_back(dev); + + device_lists.push_back(new_list); + } + } + + + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + RGBController_DMX* rgb_controller; + rgb_controller = new RGBController_DMX(device_lists[list_idx]); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectDMXControllers() */ + +REGISTER_DETECTOR("DMX", DetectDMXControllers); diff --git a/Controllers/DMXController/RGBController_DMX.cpp b/Controllers/DMXController/RGBController_DMX.cpp new file mode 100644 index 00000000..c5f63f40 --- /dev/null +++ b/Controllers/DMXController/RGBController_DMX.cpp @@ -0,0 +1,233 @@ +/*-----------------------------------------*\ +| RGBController_DMX.cpp | +| | +| Generic RGB Interface for OpenAuraSDK | +| DMX interface | +| | +| Adam Honse (CalcProgrammer1) 4/30/2023 | +\*-----------------------------------------*/ + +#include "RGBController_DMX.h" +#include + +#include "LogManager.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name DMX Devices + @category LEDStrip + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectDMXControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DMX::RGBController_DMX(std::vector device_list) +{ + devices = device_list; + + name = "DMX Device Group"; + type = DEVICE_TYPE_LEDSTRIP; + description = "DMX Device"; + location = "DMX: " + devices[0].port; + + /*-----------------------------------------*\ + | If this controller only represents a | + | single device, use the device name for the| + | controller name | + \*-----------------------------------------*/ + if(devices.size() == 1) + { + name = devices[0].name; + } + + /*-----------------------------------------*\ + | Open OpenDMX port | + \*-----------------------------------------*/ + port = new serial_port(devices[0].port.c_str(), 250000, SERIAL_PORT_PARITY_NONE, SERIAL_PORT_SIZE_8, SERIAL_PORT_STOP_BITS_2, false); + + /*-----------------------------------------*\ + | Clear the RTS signal, which enables the | + | OpenDMX RS-485 drive enable | + \*-----------------------------------------*/ + port->serial_set_rts(false); + + /*-----------------------------------------*\ + | Set up modes | + \*-----------------------------------------*/ + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness = 255; + Direct.brightness_min = 0; + Direct.brightness_max = 255; + modes.push_back(Direct); + + keepalive_delay = 0ms; + + SetupZones(); + + for (std::size_t device_idx = 0; device_idx < devices.size(); device_idx++) + { + /*-----------------------------------------*\ + | Update keepalive delay | + \*-----------------------------------------*/ + if(devices[device_idx].keepalive_time > 0) + { + if(keepalive_delay.count() == 0 || keepalive_delay.count() > devices[device_idx].keepalive_time) + { + keepalive_delay = std::chrono::milliseconds(devices[device_idx].keepalive_time); + } + } + } + + if(keepalive_delay.count() > 0) + { + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_DMX::KeepaliveThreadFunction, this); + } + else + { + keepalive_thread_run = 0; + keepalive_thread = nullptr; + } +} + +RGBController_DMX::~RGBController_DMX() +{ + if(keepalive_thread != nullptr) + { + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + } + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + if(zones[zone_index].matrix_map->map != NULL) + { + delete zones[zone_index].matrix_map->map; + } + + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_DMX::SetupZones() +{ + /*-----------------------------------------*\ + | Add Zones | + \*-----------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < devices.size(); zone_idx++) + { + zone led_zone; + led_zone.name = devices[zone_idx].name; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + + zones.push_back(led_zone); + } + + /*-----------------------------------------*\ + | Add LEDs | + \*-----------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name + " LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_DMX::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_DMX::DeviceUpdateLEDs() +{ + int color_idx = 0; + + last_update_time = std::chrono::steady_clock::now(); + + unsigned char dmx_data[513]; + + memset(dmx_data, 0, sizeof(dmx_data)); + + for(unsigned int device_idx = 0; device_idx < devices.size(); device_idx++) + { + if(devices[device_idx].brightness_channel > 0) + { + dmx_data[devices[device_idx].brightness_channel] = modes[0].brightness; + } + + if(devices[device_idx].red_channel > 0) + { + dmx_data[devices[device_idx].red_channel] = RGBGetRValue(colors[device_idx]); + } + + if(devices[device_idx].green_channel > 0) + { + dmx_data[devices[device_idx].green_channel] = RGBGetGValue(colors[device_idx]); + } + + if(devices[device_idx].blue_channel > 0) + { + dmx_data[devices[device_idx].blue_channel] = RGBGetBValue(colors[device_idx]); + } + } + + port->serial_break(); + port->serial_write((char*)&dmx_data, sizeof(dmx_data)); +} + +void RGBController_DMX::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DMX::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DMX::DeviceUpdateMode() +{ + +} + +void RGBController_DMX::KeepaliveThreadFunction() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > ( keepalive_delay * 0.95f ) ) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(keepalive_delay / 2); + } +} diff --git a/Controllers/DMXController/RGBController_DMX.h b/Controllers/DMXController/RGBController_DMX.h new file mode 100644 index 00000000..ab38b4a3 --- /dev/null +++ b/Controllers/DMXController/RGBController_DMX.h @@ -0,0 +1,52 @@ +/*-----------------------------------------*\ +| RGBController_DMX.h | +| | +| Generic RGB Interface for OpenAuraSDK | +| DMX interface | +| | +| Adam Honse (CalcProgrammer1) 4/30/2023 | +\*-----------------------------------------*/ + +#pragma once +#include "RGBController.h" +#include "serial_port.h" +#include +#include + +struct DMXDevice +{ + std::string name; + std::string port; + unsigned int keepalive_time; + unsigned int red_channel; + unsigned int green_channel; + unsigned int blue_channel; + unsigned int brightness_channel; +}; + +class RGBController_DMX : public RGBController +{ +public: + RGBController_DMX(std::vector device_list); + ~RGBController_DMX(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + std::vector devices; + serial_port * port; + std::thread * keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::milliseconds keepalive_delay; + std::chrono::time_point last_update_time; +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index 92360e67..5e0035b4 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -108,6 +108,7 @@ INCLUDEPATH += Controllers/CryorigH7QuadLumiController/ \ Controllers/DasKeyboardController/ \ Controllers/DebugController/ \ + Controllers/DMXController/ \ Controllers/DuckyKeyboardController/ \ Controllers/DygmaRaiseController/ \ Controllers/E131Controller/ \ @@ -240,6 +241,8 @@ HEADERS += qt/OpenRGBSystemInfoPage.h \ qt/OpenRGBThemeManager.h \ qt/OpenRGBZoneResizeDialog.h \ + qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.h \ + qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.h \ qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsEntry.h \ qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.h \ qt/OpenRGBLIFXSettingsPage/OpenRGBLIFXSettingsEntry.h \ @@ -393,6 +396,7 @@ HEADERS += Controllers/DarkProject/RGBController_DarkProjectKeyboard.h \ Controllers/DasKeyboardController/DasKeyboardController.h \ Controllers/DasKeyboardController/RGBController_DasKeyboard.h \ + Controllers/DMXController/RGBController_DMX.h \ Controllers/DuckyKeyboardController/DuckyKeyboardController.h \ Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.h \ Controllers/DygmaRaiseController/DygmaRaiseController.h \ @@ -799,6 +803,8 @@ SOURCES += qt/QTooltipedSlider.cpp \ qt/TabLabel.cpp \ qt/hsv.cpp \ + qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.cpp \ + qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.cpp \ qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsEntry.cpp \ qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.cpp \ qt/OpenRGBLIFXSettingsPage/OpenRGBLIFXSettingsEntry.cpp \ @@ -981,6 +987,8 @@ SOURCES += Controllers/DasKeyboardController/DasKeyboardController.cpp \ Controllers/DasKeyboardController/DasKeyboardControllerDetect.cpp \ Controllers/DasKeyboardController/RGBController_DasKeyboard.cpp \ + Controllers/DMXController/DMXControllerDetect.cpp \ + Controllers/DMXController/RGBController_DMX.cpp \ Controllers/DuckyKeyboardController/DuckyKeyboardController.cpp \ Controllers/DuckyKeyboardController/DuckyKeyboardControllerDetect.cpp \ Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.cpp \ @@ -1426,6 +1434,8 @@ FORMS += qt/OpenRGBSupportedDevicesPage.ui \ qt/OpenRGBSystemInfoPage.ui \ qt/OpenRGBZoneResizeDialog.ui \ + qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.ui \ + qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.ui \ qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsEntry.ui \ qt/OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.ui \ qt/OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsEntry.ui \ diff --git a/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.cpp b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.cpp new file mode 100644 index 00000000..5ab8ad5e --- /dev/null +++ b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.cpp @@ -0,0 +1,29 @@ +#include "OpenRGBDMXSettingsEntry.h" +#include "ui_OpenRGBDMXSettingsEntry.h" + +using namespace Ui; + +OpenRGBDMXSettingsEntry::OpenRGBDMXSettingsEntry(QWidget *parent) : + QWidget(parent), + ui(new Ui::OpenRGBDMXSettingsEntryUi) +{ + ui->setupUi(this); +} + +OpenRGBDMXSettingsEntry::~OpenRGBDMXSettingsEntry() +{ + delete ui; +} + +void OpenRGBDMXSettingsEntry::changeEvent(QEvent *event) +{ + if(event->type() == QEvent::LanguageChange) + { + ui->retranslateUi(this); + } +} + +void Ui::OpenRGBDMXSettingsEntry::on_TypeComboBox_currentIndexChanged(int index) +{ + +} diff --git a/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.h b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.h new file mode 100644 index 00000000..75c82a1b --- /dev/null +++ b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.h @@ -0,0 +1,25 @@ +#ifndef OPENRGBDMXSETTINGSENTRY_H +#define OPENRGBDMXSETTINGSENTRY_H + +#include "ui_OpenRGBDMXSettingsEntry.h" +#include + +namespace Ui { +class OpenRGBDMXSettingsEntry; +} + +class Ui::OpenRGBDMXSettingsEntry : public QWidget +{ + Q_OBJECT + +public: + explicit OpenRGBDMXSettingsEntry(QWidget *parent = nullptr); + ~OpenRGBDMXSettingsEntry(); + Ui::OpenRGBDMXSettingsEntryUi *ui; + +private slots: + void changeEvent(QEvent *event); + void on_TypeComboBox_currentIndexChanged(int index); +}; + +#endif // OPENRGBDMXSETTINGSENTRY_H diff --git a/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.ui b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.ui new file mode 100644 index 00000000..87334c8b --- /dev/null +++ b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsEntry.ui @@ -0,0 +1,109 @@ + + + OpenRGBDMXSettingsEntryUi + + + + 0 + 0 + 531 + 199 + + + + + 0 + 0 + + + + E131 settings entry + + + + + + + + + + + + Brightness Channel: + + + + + + + + + + Blue Channel: + + + + + + + + + + + + + Name: + + + + + + + Green Channel: + + + + + + + Red Channel: + + + + + + + + + + + + + Keepalive Time: + + + + + + + + + + Port: + + + + + + + + + + + + + NameEdit + + + + diff --git a/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.cpp b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.cpp new file mode 100644 index 00000000..2a20cd6e --- /dev/null +++ b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.cpp @@ -0,0 +1,160 @@ +#include "OpenRGBDMXSettingsPage.h" +#include "ui_OpenRGBDMXSettingsPage.h" +#include "ResourceManager.h" + +using namespace Ui; + +OpenRGBDMXSettingsPage::OpenRGBDMXSettingsPage(QWidget *parent) : + QWidget(parent), + ui(new Ui::OpenRGBDMXSettingsPageUi) +{ + ui->setupUi(this); + + json dmx_settings; + + /*-------------------------------------------------*\ + | Get DMX settings from settings manager | + \*-------------------------------------------------*/ + dmx_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DMXDevices"); + + /*-------------------------------------------------*\ + | If the DMX settings contains devices, process | + \*-------------------------------------------------*/ + if(dmx_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < dmx_settings["devices"].size(); device_idx++) + { + OpenRGBDMXSettingsEntry* entry = new OpenRGBDMXSettingsEntry; + + if(dmx_settings["devices"][device_idx].contains("name")) + { + entry->ui->NameEdit->setText(QString::fromStdString(dmx_settings["devices"][device_idx]["name"])); + } + + if(dmx_settings["devices"][device_idx].contains("port")) + { + entry->ui->PortEdit->setText(QString::fromStdString(dmx_settings["devices"][device_idx]["port"])); + } + + if(dmx_settings["devices"][device_idx].contains("red_channel")) + { + entry->ui->RedEdit->setText(QString::number((int)dmx_settings["devices"][device_idx]["red_channel"])); + } + + if(dmx_settings["devices"][device_idx].contains("green_channel")) + { + entry->ui->GreenEdit->setText(QString::number((int)dmx_settings["devices"][device_idx]["green_channel"])); + } + + if(dmx_settings["devices"][device_idx].contains("blue_channel")) + { + entry->ui->BlueEdit->setText(QString::number((int)dmx_settings["devices"][device_idx]["blue_channel"])); + } + + if(dmx_settings["devices"][device_idx].contains("brightness_channel")) + { + entry->ui->BrightnessEdit->setText(QString::number((int)dmx_settings["devices"][device_idx]["brightness_channel"])); + } + + if(dmx_settings["devices"][device_idx].contains("keepalive_time")) + { + entry->ui->KeepaliveTimeEdit->setText(QString::number((int)dmx_settings["devices"][device_idx]["keepalive_time"])); + } + + entries.push_back(entry); + + QListWidgetItem* item = new QListWidgetItem; + + item->setSizeHint(entry->sizeHint()); + + ui->DMXDeviceList->addItem(item); + ui->DMXDeviceList->setItemWidget(item, entry); + ui->DMXDeviceList->show(); + } + } +} + +OpenRGBDMXSettingsPage::~OpenRGBDMXSettingsPage() +{ + delete ui; +} + +void OpenRGBDMXSettingsPage::changeEvent(QEvent *event) +{ + if(event->type() == QEvent::LanguageChange) + { + ui->retranslateUi(this); + } +} + +void Ui::OpenRGBDMXSettingsPage::on_AddDMXDeviceButton_clicked() +{ + OpenRGBDMXSettingsEntry* entry = new OpenRGBDMXSettingsEntry; + entries.push_back(entry); + + QListWidgetItem* item = new QListWidgetItem; + + item->setSizeHint(entry->sizeHint()); + + ui->DMXDeviceList->addItem(item); + ui->DMXDeviceList->setItemWidget(item, entry); + ui->DMXDeviceList->show(); +} + +void Ui::OpenRGBDMXSettingsPage::on_RemoveDMXDeviceButton_clicked() +{ + int cur_row = ui->DMXDeviceList->currentRow(); + + if(cur_row < 0) + { + return; + } + + QListWidgetItem* item = ui->DMXDeviceList->takeItem(cur_row); + + ui->DMXDeviceList->removeItemWidget(item); + delete item; + + delete entries[cur_row]; + entries.erase(entries.begin() + cur_row); +} + +void Ui::OpenRGBDMXSettingsPage::on_SaveDMXConfigurationButton_clicked() +{ + json dmx_settings; + + /*-------------------------------------------------*\ + | Get DMX settings from settings manager | + \*-------------------------------------------------*/ + dmx_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DMXDevices"); + + dmx_settings["devices"].clear(); + + for(unsigned int device_idx = 0; device_idx < entries.size(); device_idx++) + { + /*-------------------------------------------------*\ + | Required parameters | + \*-------------------------------------------------*/ + dmx_settings["devices"][device_idx]["name"] = entries[device_idx]->ui->NameEdit->text().toStdString(); + dmx_settings["devices"][device_idx]["port"] = entries[device_idx]->ui->PortEdit->text().toStdString(); + dmx_settings["devices"][device_idx]["red_channel"] = entries[device_idx]->ui->RedEdit->text().toUInt(); + dmx_settings["devices"][device_idx]["green_channel"] = entries[device_idx]->ui->GreenEdit->text().toUInt(); + dmx_settings["devices"][device_idx]["blue_channel"] = entries[device_idx]->ui->BlueEdit->text().toUInt(); + + /*-------------------------------------------------*\ + | Optional parameters | + \*-------------------------------------------------*/ + if(entries[device_idx]->ui->BrightnessEdit->text() != "") + { + dmx_settings["devices"][device_idx]["brightness_channel"] = entries[device_idx]->ui->BrightnessEdit->text().toUInt(); + } + + if(entries[device_idx]->ui->KeepaliveTimeEdit->text() != "") + { + dmx_settings["devices"][device_idx]["keepalive_time"] = entries[device_idx]->ui->KeepaliveTimeEdit->text().toUInt(); + } + } + + ResourceManager::get()->GetSettingsManager()->SetSettings("DMXDevices", dmx_settings); + ResourceManager::get()->GetSettingsManager()->SaveSettings(); +} diff --git a/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.h b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.h new file mode 100644 index 00000000..5427b4a9 --- /dev/null +++ b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.h @@ -0,0 +1,35 @@ +#ifndef OPENRGBDMXSETTINGSPAGE_H +#define OPENRGBDMXSETTINGSPAGE_H + +#include "ui_OpenRGBDMXSettingsPage.h" +#include + +#include "OpenRGBDMXSettingsEntry.h" + +namespace Ui { +class OpenRGBDMXSettingsPage; +} + +class Ui::OpenRGBDMXSettingsPage : public QWidget +{ + Q_OBJECT + +public: + explicit OpenRGBDMXSettingsPage(QWidget *parent = nullptr); + ~OpenRGBDMXSettingsPage(); + +private slots: + void changeEvent(QEvent *event); + void on_AddDMXDeviceButton_clicked(); + + void on_RemoveDMXDeviceButton_clicked(); + + void on_SaveDMXConfigurationButton_clicked(); + +private: + Ui::OpenRGBDMXSettingsPageUi* ui; + std::vector entries; + +}; + +#endif // OPENRGBDMXSETTINGSPAGE_H diff --git a/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.ui b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.ui new file mode 100644 index 00000000..85019182 --- /dev/null +++ b/qt/OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.ui @@ -0,0 +1,49 @@ + + + OpenRGBDMXSettingsPageUi + + + + 0 + 0 + 400 + 300 + + + + E131 settings page + + + + + + Add + + + + + + + Remove + + + + + + + Save + + + + + + + QAbstractItemView::ScrollPerPixel + + + + + + + + diff --git a/qt/OpenRGBDialog2.cpp b/qt/OpenRGBDialog2.cpp index 444107f0..4c2e9a63 100644 --- a/qt/OpenRGBDialog2.cpp +++ b/qt/OpenRGBDialog2.cpp @@ -432,6 +432,11 @@ OpenRGBDialog2::OpenRGBDialog2(QWidget *parent) : QMainWindow(parent), ui(new Op \*-----------------------------------------------------*/ AddPluginsPage(); + /*-----------------------------------------------------*\ + | Add the DMX settings page | + \*-----------------------------------------------------*/ + AddDMXSettingsPage(); + /*-----------------------------------------------------*\ | Add the E1.31 settings page | \*-----------------------------------------------------*/ @@ -757,6 +762,34 @@ void OpenRGBDialog2::AddSettingsPage() connect(this, SIGNAL(ProfileListChanged()), SettingsPage, SLOT(UpdateProfiles())); } +void OpenRGBDialog2::AddDMXSettingsPage() +{ + /*-----------------------------------------------------*\ + | Create the Settings page | + \*-----------------------------------------------------*/ + DMXSettingsPage = new OpenRGBDMXSettingsPage(); + + ui->SettingsTabBar->addTab(DMXSettingsPage, ""); + + QString SettingsLabelString; + + if(OpenRGBThemeManager::IsDarkTheme()) + { + SettingsLabelString = "serial_dark.png"; + } + else + { + SettingsLabelString = "serial.png"; + } + + /*-----------------------------------------------------*\ + | Create the tab label | + \*-----------------------------------------------------*/ + TabLabel* SettingsTabLabel = new TabLabel(SettingsLabelString, tr("DMX Devices"), (char *)"DMX Devices", (char *)context); + + ui->SettingsTabBar->tabBar()->setTabButton(ui->SettingsTabBar->tabBar()->count() - 1, QTabBar::LeftSide, SettingsTabLabel); +} + void OpenRGBDialog2::AddE131SettingsPage() { /*-----------------------------------------------------*\ diff --git a/qt/OpenRGBDialog2.h b/qt/OpenRGBDialog2.h index 54bab9f0..907cb465 100644 --- a/qt/OpenRGBDialog2.h +++ b/qt/OpenRGBDialog2.h @@ -9,6 +9,7 @@ #include "OpenRGBSystemInfoPage.h" #include "OpenRGBSupportedDevicesPage.h" #include "OpenRGBSettingsPage.h" +#include "OpenRGBDMXSettingsPage/OpenRGBDMXSettingsPage.h" #include "OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.h" #include "OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsPage.h" #include "OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.h" @@ -84,6 +85,7 @@ private: OpenRGBSoftwareInfoPage *SoftInfoPage; OpenRGBSupportedDevicesPage *SupportedPage; OpenRGBSettingsPage *SettingsPage; + OpenRGBDMXSettingsPage *DMXSettingsPage; OpenRGBE131SettingsPage *E131SettingsPage; OpenRGBElgatoKeyLightSettingsPage *ElgatoKeyLightSettingsPage; OpenRGBKasaSmartSettingsPage *KasaSmartSettingsPage; @@ -112,6 +114,7 @@ private: void AddSoftwareInfoPage(); void AddSupportedDevicesPage(); void AddSettingsPage(); + void AddDMXSettingsPage(); void AddE131SettingsPage(); void AddElgatoKeyLightSettingsPage(); void AddKasaSmartSettingsPage(); diff --git a/serial_port/serial_port.cpp b/serial_port/serial_port.cpp index 62efab3e..b8d60310 100644 --- a/serial_port/serial_port.cpp +++ b/serial_port/serial_port.cpp @@ -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 at baud rate | +| 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 +} diff --git a/serial_port/serial_port.h b/serial_port/serial_port.h index 2da9a49e..0d60b162 100644 --- a/serial_port/serial_port.h +++ b/serial_port/serial_port.h @@ -58,9 +58,37 @@ #include #include #include +#include #endif /* __APPLE__ */ +/*-------------------------------------------------------------------------*\ +| Serial Port Enums | +\*-------------------------------------------------------------------------*/ +typedef unsigned int serial_port_parity; +enum +{ + SERIAL_PORT_PARITY_NONE = 0, /* No parity */ + SERIAL_PORT_PARITY_ODD = 1, /* Odd parity */ + SERIAL_PORT_PARITY_EVEN = 2, /* Even parity */ +}; + +typedef unsigned int serial_port_size; +enum +{ + SERIAL_PORT_SIZE_8 = 0, /* 8 bits per byte */ + SERIAL_PORT_SIZE_7 = 1, /* 7 bits per byte */ + SERIAL_PORT_SIZE_6 = 2, /* 6 bits per byte */ + SERIAL_PORT_SIZE_5 = 3, /* 5 bits per byte */ +}; + +typedef unsigned int serial_port_stop_bits; +enum +{ + SERIAL_PORT_STOP_BITS_1 = 0, /* 1 stop bit */ + SERIAL_PORT_STOP_BITS_2 = 1, /* 2 stop bits */ +}; + /*-------------------------------------------------------------------------*\ | Serial Port Class | | The reason for this class is that serial ports are treated differently | @@ -72,6 +100,12 @@ class serial_port public: serial_port(); serial_port(const char * name, unsigned int baud); + 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); ~serial_port(); @@ -90,12 +124,19 @@ public: void serial_flush_rx(); void serial_flush_tx(); + void serial_break(); + + void serial_set_rts(bool rts); int serial_available(); private: - char port_name[1024]; - unsigned int baud_rate; + char port_name[1024]; + unsigned int baud_rate; + serial_port_parity parity; + serial_port_size size; + serial_port_stop_bits stop_bits; + bool flow_control; #ifdef _WIN32 HANDLE file_descriptor;