2023-04-02 22:47:50 -04:00
|
|
|
/*
|
|
|
|
widget.cpp
|
|
|
|
|
|
|
|
Copyright (c) 2023 Kevin Matz (kevin.matz@gmail.com)
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include "widget.h"
|
2023-04-26 10:05:16 -04:00
|
|
|
#include <rdm.h>
|
2023-04-02 22:47:50 -04:00
|
|
|
|
2023-04-03 09:58:26 -04:00
|
|
|
namespace ENTTEC {
|
2023-04-02 22:47:50 -04:00
|
|
|
|
|
|
|
Widget::Widget()
|
2023-04-03 16:43:37 -04:00
|
|
|
: serial_number(0) // SN# 0 for emulated devices
|
|
|
|
, firmware_version(2<<8) // emulated devices support RDM
|
2023-04-05 12:39:12 -04:00
|
|
|
, tx_break_intervals(17) // 181.4 us by default
|
|
|
|
, tx_mab_intervals(10) // 106.7 us by default
|
|
|
|
, tx_rate(40) // 40 packets/s, by default
|
2023-04-09 19:31:56 -04:00
|
|
|
, usb_mode(USBunknown)
|
2023-04-06 09:39:29 -04:00
|
|
|
, rx_update_mode_(Pro::RxNotifyAlways)
|
2023-04-06 08:34:35 -04:00
|
|
|
, token_data_changed_(nullptr)
|
2023-04-25 16:48:24 -04:00
|
|
|
, rdm_controller_(nullptr)
|
|
|
|
, rdm_responder_(nullptr)
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
2023-05-03 16:10:52 -04:00
|
|
|
// receive RDM alt-start-code'd messages from the universe
|
|
|
|
token_rdm_receive_ = onRxData(RDM::SC_RDM, std::bind(&Widget::receiveRDM,
|
|
|
|
this, std::placeholders::_1));
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Widget::~Widget()
|
|
|
|
{
|
2023-04-09 19:31:56 -04:00
|
|
|
switch (usb_mode) {
|
2023-04-02 22:47:50 -04:00
|
|
|
case USBdevice:
|
|
|
|
Widget::halt();
|
|
|
|
break;
|
|
|
|
case USBhost:
|
2023-04-05 08:10:59 -04:00
|
|
|
Widget::close();
|
2023-04-02 22:47:50 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2023-04-25 16:48:24 -04:00
|
|
|
if (rdm_controller_)
|
|
|
|
delete rdm_controller_;
|
|
|
|
if (rdm_responder_)
|
|
|
|
delete rdm_responder_;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Begin operating as a USB Device
|
2023-04-03 16:43:37 -04:00
|
|
|
*
|
|
|
|
* The base class method should be called from all inheriting overrides.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
|
|
|
void Widget::init()
|
|
|
|
{
|
2023-04-14 10:10:10 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
usb_mode = USBdevice;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Stop operating as a USB Device
|
2023-04-03 16:43:37 -04:00
|
|
|
*
|
|
|
|
* The base class method should be called from all inheriting overrides.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
|
|
|
void Widget::halt()
|
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-09 19:31:56 -04:00
|
|
|
usb_mode = USBunknown;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Begin operating on the USB Host
|
2023-04-03 16:43:37 -04:00
|
|
|
*
|
|
|
|
* The base class method should be called from all inheriting overrides.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
2023-04-05 08:10:59 -04:00
|
|
|
void Widget::open()
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
2023-04-14 10:10:10 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
usb_mode = USBhost;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Stop operating on the USB Host
|
2023-04-03 16:43:37 -04:00
|
|
|
*
|
|
|
|
* The base class method should be called from all inheriting overrides.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
2023-04-05 08:10:59 -04:00
|
|
|
void Widget::close()
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-09 19:31:56 -04:00
|
|
|
usb_mode = USBunknown;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 12:41:13 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::setModeController
|
|
|
|
*/
|
|
|
|
void Widget::setModeController()
|
|
|
|
{
|
|
|
|
sendDmx();
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
token_data_changed_ = onDataChange([this](DMX::Universe*){sendDmx();});
|
2023-04-17 08:17:31 -04:00
|
|
|
device_class_ = DMX::CONTROLLER;
|
2023-04-25 16:48:24 -04:00
|
|
|
if (rdm_responder_)
|
|
|
|
delete rdm_responder_;
|
|
|
|
if (featureRDM())
|
|
|
|
rdm_controller_ = new RDM::Controller();
|
2023-04-05 12:41:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::setModeBridge
|
|
|
|
* @param mode
|
|
|
|
*/
|
|
|
|
void Widget::setModeBridge(Pro::DMX_RX_MODE mode)
|
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
token_data_changed_ = nullptr;
|
|
|
|
rx_update_mode_ = mode;
|
2023-04-25 16:48:24 -04:00
|
|
|
if (rdm_controller_)
|
|
|
|
delete rdm_controller_;
|
2023-04-17 08:17:31 -04:00
|
|
|
if (featureRDM())
|
2023-04-25 16:48:24 -04:00
|
|
|
{
|
2023-04-28 20:19:15 -04:00
|
|
|
RDM::UID id(Pro::DecimalToBCD(serial_number), 0x454E); // Use the ENTTEC manufacturer ID?
|
2023-04-25 16:48:24 -04:00
|
|
|
rdm_responder_ = new RDM::Responder(id);
|
2023-04-27 09:26:05 -04:00
|
|
|
rdm_configure_responder_();
|
2023-04-25 16:48:24 -04:00
|
|
|
device_class_ = DMX::RESPONDER;
|
|
|
|
}
|
2023-04-17 08:17:31 -04:00
|
|
|
else
|
2023-04-25 16:48:24 -04:00
|
|
|
device_class_ = DMX::RECEIVER;
|
2023-04-06 08:33:57 -04:00
|
|
|
}
|
2023-04-05 12:41:13 -04:00
|
|
|
auto msg = std::make_shared<Pro::MsgRecieveDMXOnChange>();
|
|
|
|
msg->mode = mode;
|
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-06 08:33:57 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::serialNumber
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
uint32_t Widget::serialNumber() const
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
return serial_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::firmwareVersion
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
uint16_t Widget::firmwareVersion() const
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
return firmware_version;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::txBreakTime
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
double Widget::txBreakTime() const
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
return tx_break_intervals * Pro::DMX_BREAK_INTERVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 09:19:16 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::setTxBreakTime
|
|
|
|
* @param time
|
2023-04-14 09:53:30 -04:00
|
|
|
* @return
|
2023-04-05 09:19:16 -04:00
|
|
|
*/
|
2023-04-14 09:53:30 -04:00
|
|
|
double Widget::setTxBreakTime(double time)
|
2023-04-05 09:19:16 -04:00
|
|
|
{
|
|
|
|
setTxBreakIntervals(time / Pro::DMX_BREAK_INTERVAL);
|
2023-04-14 09:53:30 -04:00
|
|
|
return txBreakTime();
|
2023-04-05 09:19:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::setTxBreakIntervals
|
|
|
|
* @param count
|
|
|
|
*/
|
|
|
|
void Widget::setTxBreakIntervals(uint8_t count)
|
|
|
|
{
|
|
|
|
count = std::max(count, Pro::DMX_BREAK_MIN);
|
|
|
|
count = std::min(count, Pro::DMX_BREAK_MAX);
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-05 09:19:16 -04:00
|
|
|
tx_break_intervals = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-06 08:33:57 -04:00
|
|
|
/**
|
|
|
|
* @brief txMabTime
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
double Widget::txMabTime() const
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
return tx_mab_intervals * Pro::DMX_MAB_INTERVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 09:19:16 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::setTxMabTime
|
|
|
|
* @param time
|
2023-04-14 09:53:30 -04:00
|
|
|
* @return
|
2023-04-05 09:19:16 -04:00
|
|
|
*/
|
2023-04-14 09:53:30 -04:00
|
|
|
double Widget::setTxMabTime(double time)
|
2023-04-05 09:19:16 -04:00
|
|
|
{
|
|
|
|
setTxMabIntervals(time / Pro::DMX_MAB_INTERVAL);
|
2023-04-14 09:53:30 -04:00
|
|
|
return txMabTime();
|
2023-04-05 09:19:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::setTxMabIntervals
|
|
|
|
* @param count
|
|
|
|
*/
|
|
|
|
void Widget::setTxMabIntervals(uint8_t count)
|
|
|
|
{
|
|
|
|
count = std::max(count, Pro::DMX_MAB_MIN);
|
|
|
|
count = std::min(count, Pro::DMX_MAB_MAX);
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-05 09:19:16 -04:00
|
|
|
tx_mab_intervals = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-06 08:33:57 -04:00
|
|
|
/**
|
|
|
|
* @brief txRate
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
uint8_t Widget::txRate() const
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
return tx_rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 09:19:16 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::setTxRate
|
|
|
|
* @param rate
|
|
|
|
*/
|
|
|
|
void Widget::setTxRate(uint8_t rate)
|
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-05 09:19:16 -04:00
|
|
|
tx_rate = std::min(rate, Pro::DMX_RATE_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-14 09:55:10 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::userData
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
const std::vector<uint8_t> & Widget::userData() const
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
return user_configuration;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::setUserData
|
|
|
|
* @param data
|
|
|
|
*/
|
|
|
|
void Widget::setUserData(std::vector<uint8_t> data)
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
user_configuration = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-14 10:09:26 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::getParameters
|
|
|
|
* @param user_length
|
|
|
|
*/
|
|
|
|
void Widget::getParameters(size_t user_length)
|
|
|
|
{
|
|
|
|
auto msg = std::make_shared<Pro::MsgGetWidgetParametersRequest>();
|
|
|
|
msg->size = std::min(user_length, Pro::USER_CONFIGURATION_MAX);
|
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Widget::setParameters
|
|
|
|
* @param user_length
|
|
|
|
*/
|
|
|
|
void Widget::setParameters(size_t user_length) const
|
|
|
|
{
|
|
|
|
auto msg = std::make_shared<Pro::MsgSetWidgetParametersRequest>();
|
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
msg->break_time = tx_break_intervals;
|
|
|
|
msg->mab_time = tx_mab_intervals;
|
|
|
|
msg->rate = tx_rate;
|
|
|
|
msg->user_data = user_configuration;
|
|
|
|
}
|
|
|
|
size_t length = user_length < 0 ? user_configuration.size() : user_length;
|
|
|
|
length = std::min(length, Pro::USER_CONFIGURATION_MAX);
|
|
|
|
msg->user_data.resize(length, 0xff); // resized data padded with 0xFF (matching OEM)
|
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-06 10:43:42 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::writeFirmware
|
|
|
|
* @param data
|
|
|
|
* @param size
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool Widget::writeFirmware(const uint8_t *data, const size_t size)
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* While the API documents the message formats, there is little discussion of the
|
|
|
|
* implimentation for updating firmware. As such, do nothing and return false.
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// reboot the device into the fw loader
|
|
|
|
rebootBootloader();
|
|
|
|
|
|
|
|
auto msg = std::make_shared<Pro::MsgProgramFlashPageRequest>();
|
2023-04-14 10:07:58 -04:00
|
|
|
bool success = false;
|
2023-04-06 10:43:42 -04:00
|
|
|
for (uint i = 0; i < size / sizeof(msg->page);)
|
|
|
|
{
|
|
|
|
std::copy(data + (i*sizeof(msg->page)), data + ((i+1)*sizeof(msg->page)), msg->page);
|
|
|
|
sendMessage(msg);
|
|
|
|
if (success)
|
|
|
|
i++; // page write was successful, do the next.
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-09 19:43:54 -04:00
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief Create ENTTEC API messages.
|
|
|
|
* @param label Opcode of the message.
|
|
|
|
* @param mode USB role.
|
2023-04-09 19:43:54 -04:00
|
|
|
* @return
|
2023-04-26 09:58:03 -04:00
|
|
|
*
|
|
|
|
* Some opcodes have dual meaning, depending on which side of USB this driver is operating.
|
2023-04-09 19:43:54 -04:00
|
|
|
*/
|
|
|
|
std::shared_ptr<Pro::MessageData> Widget::MessageDataFactory(Pro::MESSAGE_LABEL label,
|
|
|
|
OperatingMode mode)
|
|
|
|
{
|
|
|
|
switch (label) {
|
|
|
|
case Pro::OpReprogramFirmware:
|
|
|
|
return std::make_shared<Pro::MsgReprogramFirmware>();
|
|
|
|
case Pro::OpProgramFlashPage:
|
|
|
|
switch (mode) {
|
|
|
|
case USBdevice:
|
|
|
|
return std::make_shared<Pro::MsgProgramFlashPageReply>();
|
|
|
|
case USBhost:
|
|
|
|
return std::make_shared<Pro::MsgProgramFlashPageRequest>();
|
|
|
|
default:
|
|
|
|
return std::make_shared<Pro::MsgNoop>();
|
|
|
|
}
|
|
|
|
case Pro::OpGetWidgetParameters:
|
|
|
|
switch (mode) {
|
|
|
|
case USBdevice:
|
|
|
|
return std::make_shared<Pro::MsgGetWidgetParametersReply>();
|
|
|
|
case USBhost:
|
|
|
|
return std::make_shared<Pro::MsgGetWidgetParametersRequest>();
|
|
|
|
default:
|
|
|
|
return std::make_shared<Pro::MsgNoop>();
|
|
|
|
}
|
|
|
|
case Pro::OpSetWidgetParameters:
|
|
|
|
return std::make_shared<Pro::MsgSetWidgetParametersRequest>();
|
|
|
|
case Pro::OpRecievedDmxPacket:
|
|
|
|
return std::make_shared<Pro::MsgRecievedDmxPacket>();
|
|
|
|
case Pro::OpOutputOnlySendDMX:
|
|
|
|
return std::make_shared<Pro::MsgOutputOnlySendDMX>();
|
|
|
|
case Pro::OpSendRDMData:
|
|
|
|
return std::make_shared<Pro::MsgSendRDMData>();
|
|
|
|
case Pro::OpRecieveDMXOnChange:
|
|
|
|
return std::make_shared<Pro::MsgRecieveDMXOnChange>();
|
|
|
|
case Pro::OpRecievedDMXChanged:
|
|
|
|
return std::make_shared<Pro::MsgRecievedDMXChanged>();
|
|
|
|
case Pro::OpGetWidgetSerial:
|
|
|
|
switch (mode) {
|
|
|
|
case USBdevice:
|
|
|
|
return std::make_shared<Pro::MsgGetWidgetSerialReply>();
|
|
|
|
case USBhost:
|
|
|
|
return std::make_shared<Pro::MsgGetWidgetSerialRequest>();
|
|
|
|
default:
|
|
|
|
return std::make_shared<Pro::MsgNoop>();
|
|
|
|
}
|
|
|
|
case Pro::OpSendRDMDiscovery:
|
|
|
|
return std::make_shared<Pro::MsgSendRDMDiscovery>();
|
|
|
|
default:
|
|
|
|
return std::make_shared<Pro::MsgNoop>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-02 22:47:50 -04:00
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief Route recieved Enttec API messages.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
2023-04-26 09:58:03 -04:00
|
|
|
*
|
|
|
|
* Some opcodes have dual meaning, depending on which side of USB this driver is operating.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
2023-04-03 16:42:48 -04:00
|
|
|
void Widget::routeRxMessage(std::shared_ptr<Pro::MessageData> msg)
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
2023-04-06 12:22:26 -04:00
|
|
|
switch (msg->label)
|
|
|
|
{
|
|
|
|
case Pro::OpNoop:
|
2023-04-09 19:51:01 -04:00
|
|
|
rxMsgHello();
|
2023-04-06 12:22:26 -04:00
|
|
|
break;
|
2023-04-02 22:47:50 -04:00
|
|
|
case Pro::OpReprogramFirmware:
|
2023-04-09 19:51:01 -04:00
|
|
|
rxMsgReprogramFirmware();
|
2023-04-02 22:47:50 -04:00
|
|
|
break;
|
|
|
|
case Pro::OpProgramFlashPage:
|
|
|
|
{
|
2023-04-09 19:31:56 -04:00
|
|
|
switch (usb_mode) {
|
2023-04-02 22:47:50 -04:00
|
|
|
case USBdevice:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgProgramFlashPageRequest>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgProgramFlashPageRequest(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USBhost:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgProgramFlashPageReply>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgProgramFlashPageReply(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpGetWidgetParameters:
|
|
|
|
{
|
2023-04-09 19:31:56 -04:00
|
|
|
switch (usb_mode) {
|
2023-04-02 22:47:50 -04:00
|
|
|
case USBdevice:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgGetWidgetParametersRequest>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgGetWidgetParametersRequest(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USBhost:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgGetWidgetParametersReply>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgGetWidgetParametersReply(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpSetWidgetParameters:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgSetWidgetParametersRequest>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgSetWidgetParametersRequest(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpRecievedDmxPacket:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgRecievedDmxPacket>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgRecievedDmxPacket(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpOutputOnlySendDMX:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgOutputOnlySendDMX>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgOutputOnlySendDMX(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpSendRDMData:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgSendRDMData>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgSendRDMData(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpRecieveDMXOnChange:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgRecieveDMXOnChange>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgRecieveDMXOnChange(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpRecievedDMXChanged:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgRecievedDMXChanged>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgRecievedDMXChanged(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpGetWidgetSerial:
|
|
|
|
{
|
2023-04-09 19:31:56 -04:00
|
|
|
switch (usb_mode) {
|
2023-04-02 22:47:50 -04:00
|
|
|
case USBdevice:
|
2023-04-09 19:51:01 -04:00
|
|
|
rxMsgGetWidgetSerialRequest();
|
2023-04-02 22:47:50 -04:00
|
|
|
break;
|
|
|
|
case USBhost:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgGetWidgetSerialReply>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgGetWidgetSerialReply(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pro::OpSendRDMDiscovery:
|
|
|
|
{
|
2023-04-03 16:42:48 -04:00
|
|
|
auto data = std::static_pointer_cast<Pro::MsgSendRDMDiscovery>(msg);
|
2023-04-02 22:47:50 -04:00
|
|
|
rxMsgSendRDMDiscovery(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief Send a message to the widget.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
2023-04-03 16:43:37 -04:00
|
|
|
*
|
2023-04-26 09:58:03 -04:00
|
|
|
* \note The base class method is to discard the message.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
2023-04-03 16:42:48 -04:00
|
|
|
void Widget::sendMessage(std::shared_ptr<Pro::MessageData> msg) const
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
|
|
|
(void)msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 08:54:43 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::rebootBootloader
|
2023-04-06 10:42:39 -04:00
|
|
|
*
|
|
|
|
* USB devices should override this method to do something meaningful to their state.
|
2023-04-05 08:54:43 -04:00
|
|
|
*/
|
|
|
|
void Widget::rebootBootloader()
|
|
|
|
{
|
2023-04-09 19:31:56 -04:00
|
|
|
switch (usb_mode) {
|
2023-04-06 10:42:39 -04:00
|
|
|
case USBhost:
|
|
|
|
{
|
|
|
|
auto msg = std::make_shared<Pro::MsgReprogramFirmware>();
|
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2023-04-26 09:58:03 -04:00
|
|
|
/// \note This method is remotely triggered by USB Hosts prior to sending a firemware upload.
|
2023-04-06 10:42:39 -04:00
|
|
|
break;
|
|
|
|
}
|
2023-04-05 08:54:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 08:42:50 -04:00
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief Request that the USB Device reply with it's serial number.
|
2023-04-05 08:42:50 -04:00
|
|
|
*/
|
2023-04-14 10:07:58 -04:00
|
|
|
void Widget::getSerialNumber()
|
2023-04-05 08:42:50 -04:00
|
|
|
{
|
2023-04-06 10:11:33 -04:00
|
|
|
auto msg = std::make_shared<Pro::MsgGetWidgetSerialRequest>();
|
2023-04-05 08:42:50 -04:00
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-05 12:41:13 -04:00
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief Request that the USB Device output the current dimmer data.
|
|
|
|
* @param trimmed Remove trailing null slots.
|
2023-04-05 12:41:13 -04:00
|
|
|
*/
|
|
|
|
void Widget::sendDmx(bool trimmed) const
|
|
|
|
{
|
|
|
|
auto msg = std::make_shared<Pro::MsgOutputOnlySendDMX>();
|
|
|
|
uint16_t l = null_start_data.size() - 1;
|
|
|
|
if (trimmed)
|
|
|
|
for (l = null_start_data.size() - 1; l > 0 && null_start_data[l] == 0; --l) {};
|
2023-04-09 20:42:05 -04:00
|
|
|
msg->data = std::vector<uint8_t>(null_start_data.begin(), null_start_data.begin() + l + 1);
|
2023-04-05 12:41:13 -04:00
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-26 10:05:16 -04:00
|
|
|
/**
|
|
|
|
* @brief Request that the USB Device send RDM data.
|
|
|
|
* @param data
|
|
|
|
*/
|
2023-04-28 20:19:15 -04:00
|
|
|
void Widget::sendRDMdata(const std::vector<uint8_t> &data) const
|
2023-04-26 10:05:16 -04:00
|
|
|
{
|
|
|
|
auto msg = std::make_shared<Pro::MsgSendRDMData>();
|
|
|
|
msg->data = data;
|
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Request that the USB Device send RDM discovery data.
|
|
|
|
* @param data
|
|
|
|
*/
|
2023-04-28 20:19:15 -04:00
|
|
|
void Widget::sendRDMdiscovery(const std::vector<uint8_t> &data) const
|
2023-04-26 10:05:16 -04:00
|
|
|
{
|
|
|
|
auto msg = std::make_shared<Pro::MsgSendRDMDiscovery>();
|
|
|
|
(void)data;
|
|
|
|
/// \todo copy data to discovery message
|
|
|
|
sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-05-03 16:10:52 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::receiveRDM
|
|
|
|
* @param data
|
|
|
|
*/
|
|
|
|
void Widget::receiveRDM(const std::vector<uint8_t> &data)
|
|
|
|
{
|
|
|
|
if (!rdm_responder_)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (data.size() > RDM::MESSAGE_MINIMUM_LENGTH)
|
|
|
|
rdm_responder_->receive(data);
|
|
|
|
else
|
|
|
|
/// \todo The format for receiving DISC_UNIQUE_BRANCH messages is special.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-06 12:22:26 -04:00
|
|
|
/**
|
|
|
|
* @brief Widget::rxMsgHello
|
|
|
|
*/
|
2023-04-09 19:51:01 -04:00
|
|
|
void Widget::rxMsgHello()
|
2023-04-06 12:22:26 -04:00
|
|
|
{
|
2023-04-09 19:31:56 -04:00
|
|
|
switch (usb_mode) {
|
2023-04-06 12:22:26 -04:00
|
|
|
case USBdevice:
|
2023-04-09 19:51:01 -04:00
|
|
|
sendMessage(std::make_shared<Pro::MsgNoop>()); // mirror the message back to the host
|
2023-04-06 12:22:26 -04:00
|
|
|
break;
|
|
|
|
case USBhost:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-02 22:47:50 -04:00
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host intents to begin sending a firmware upload.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
2023-04-09 19:51:01 -04:00
|
|
|
void Widget::rxMsgReprogramFirmware()
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
2023-04-06 10:42:39 -04:00
|
|
|
rebootBootloader();
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host has sent a page of a new firmware.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgProgramFlashPageRequest(std::shared_ptr<Pro::MsgProgramFlashPageRequest> msg)
|
|
|
|
{
|
2023-04-03 20:09:55 -04:00
|
|
|
auto reply = std::make_shared<Pro::MsgProgramFlashPageReply>();
|
2023-04-05 06:56:23 -04:00
|
|
|
reply->success = writeFwPage(msg->page);
|
2023-04-03 20:09:55 -04:00
|
|
|
sendMessage(reply);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Device is reporting the status of the previous firmware page write.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgProgramFlashPageReply(std::shared_ptr<Pro::MsgProgramFlashPageReply> msg)
|
|
|
|
{
|
2023-04-14 10:07:58 -04:00
|
|
|
(void)msg;
|
2023-04-26 09:58:03 -04:00
|
|
|
/// \todo Send next firmware page.
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host is request this widgets tx parameters.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgGetWidgetParametersRequest(std::shared_ptr<Pro::MsgGetWidgetParametersRequest> msg)
|
|
|
|
{
|
2023-04-03 20:12:01 -04:00
|
|
|
auto reply = std::make_shared<Pro::MsgGetWidgetParametersReply>();
|
2023-04-06 08:33:57 -04:00
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
reply->break_time = tx_break_intervals;
|
|
|
|
reply->mab_time = tx_mab_intervals;
|
|
|
|
reply->rate = tx_rate;
|
|
|
|
reply->user_data = std::vector<uint8_t>(user_configuration);
|
|
|
|
}
|
2023-04-03 20:12:01 -04:00
|
|
|
reply->user_data.resize(msg->size, 0);
|
|
|
|
|
|
|
|
sendMessage(reply);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Device has reported it's tx parameters.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
2023-04-05 09:19:16 -04:00
|
|
|
*
|
|
|
|
* No sanity checking is performed on the values in the reply.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
|
|
|
void Widget::rxMsgGetWidgetParametersReply(std::shared_ptr<Pro::MsgGetWidgetParametersReply> msg)
|
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-03 20:12:01 -04:00
|
|
|
firmware_version = msg->version;
|
|
|
|
tx_break_intervals = msg->break_time;
|
|
|
|
tx_mab_intervals = msg->mab_time;
|
|
|
|
tx_rate = msg->rate;
|
|
|
|
user_configuration = std::vector<uint8_t>(msg->user_data);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host has requested an update to the widget's tx parameters.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgSetWidgetParametersRequest(std::shared_ptr<Pro::MsgSetWidgetParametersRequest> msg)
|
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-05 09:19:16 -04:00
|
|
|
setTxBreakIntervals(msg->break_time);
|
|
|
|
setTxMabIntervals(msg->mab_time);
|
|
|
|
setTxRate(msg->rate);
|
2023-04-03 20:12:01 -04:00
|
|
|
user_configuration = std::vector<uint8_t>(msg->user_data);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Device has recieved a frame of DMX
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgRecievedDmxPacket(std::shared_ptr<Pro::MsgRecievedDmxPacket> msg)
|
|
|
|
{
|
2023-04-26 10:04:06 -04:00
|
|
|
setData(msg->data);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host has send a frame of DMX for the widget to transmit.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgOutputOnlySendDMX(std::shared_ptr<Pro::MsgOutputOnlySendDMX> msg)
|
|
|
|
{
|
2023-04-26 10:04:06 -04:00
|
|
|
setData(msg->data);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB host is requesting a half-duplex data write.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgSendRDMData(std::shared_ptr<Pro::MsgSendRDMData> msg)
|
|
|
|
{
|
|
|
|
(void)msg;
|
2023-04-26 09:58:03 -04:00
|
|
|
/// \todo Impliment USB Device side of sending half-duplex RDM data.
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host has requested to only updated about recv'd data if it has changed.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgRecieveDMXOnChange(std::shared_ptr<Pro::MsgRecieveDMXOnChange> msg)
|
|
|
|
{
|
2023-04-05 12:41:13 -04:00
|
|
|
setData(std::vector<uint8_t>(DMX::E111_LAST_SLOT+1, 0)); // clear dimmer data
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-05 12:41:13 -04:00
|
|
|
rx_update_mode_ = msg->mode;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Device has send the portion of DMX data that has changed.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgRecievedDMXChanged(std::shared_ptr<Pro::MsgRecievedDMXChanged> msg)
|
|
|
|
{
|
|
|
|
(void)msg;
|
2023-04-26 09:58:03 -04:00
|
|
|
/// \todo Merge changed slots into the universe dimmer data.
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host has reqested this widget's serial number.
|
2023-04-02 22:47:50 -04:00
|
|
|
*/
|
2023-04-09 19:51:01 -04:00
|
|
|
void Widget::rxMsgGetWidgetSerialRequest()
|
2023-04-02 22:47:50 -04:00
|
|
|
{
|
2023-04-03 20:11:25 -04:00
|
|
|
auto reply = std::make_shared<Pro::MsgGetWidgetSerialReply>();
|
2023-04-06 08:33:57 -04:00
|
|
|
{
|
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
|
|
|
reply->serial = serial_number;
|
|
|
|
}
|
2023-04-03 20:11:25 -04:00
|
|
|
sendMessage(reply);
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Device has sent it's serial number.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgGetWidgetSerialReply(std::shared_ptr<Pro::MsgGetWidgetSerialReply> msg)
|
|
|
|
{
|
2023-04-06 08:33:57 -04:00
|
|
|
std::scoped_lock lock(mtx_metadata_);
|
2023-04-03 20:11:25 -04:00
|
|
|
serial_number = msg->serial;
|
2023-04-16 21:13:11 -04:00
|
|
|
reply_serial = true;
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-26 09:58:03 -04:00
|
|
|
* @brief The USB Host is requesting the sending of a RDM Discovery message.
|
2023-04-02 22:47:50 -04:00
|
|
|
* @param msg
|
|
|
|
*/
|
|
|
|
void Widget::rxMsgSendRDMDiscovery(std::shared_ptr<Pro::MsgSendRDMDiscovery> msg)
|
|
|
|
{
|
|
|
|
(void)msg;
|
2023-04-26 09:58:03 -04:00
|
|
|
/// \todo Impliment USB Device side of sending half-duplex RDM discovery data.
|
2023-04-02 22:47:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-27 09:26:05 -04:00
|
|
|
void Widget::rdm_configure_responder_()
|
|
|
|
{
|
|
|
|
if (!rdm_responder_)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// send responders RDM messages
|
|
|
|
token_rdm_send_ = rdm_responder_->setSender(std::bind(&Widget::sendRDMdata,
|
|
|
|
this, std::placeholders::_1));
|
|
|
|
|
|
|
|
rdm_responder_->deviceManufacturerLabel = "ENTTEC";
|
|
|
|
rdm_responder_->deviceModelDescription = "DMX USB Pro";
|
|
|
|
rdm_responder_->deviceModelID = (uint16_t)70304; // manufacturer SKU
|
|
|
|
rdm_responder_->deviceProductCategory = RDM::PRODUCT_CATEGORY_DATA;
|
2023-05-03 16:10:52 -04:00
|
|
|
rdm_responder_->addProductDetail(RDM::PRODUCT_DETAIL_PROTOCOL_CONVERTOR);
|
2023-04-27 09:26:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-03 09:58:26 -04:00
|
|
|
} // namespace ENTTEC
|