1
0
Fork 0
OpenLCP/protocol/enttec/dmx-usb-pro/pro.h

459 lines
15 KiB
C++

/*
pro.h
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.
*/
#pragma once
#include <bufferstream.h>
#include <climits>
#include <cstdint>
#include <cstring>
#include <math.h>
#include <memory>
#include <vector>
namespace ENTTEC::Pro {
struct MessageData;
const uint8_t START_DELIMITER = 0x7e; //!< Start of message delimiter
const uint8_t END_DELIMITER = 0xe7; //!< End of message delimiter
/**
* @brief Firmware Varieties
*
* \cite DMXUSBPro The Widget firmware version identifies the set of application messages that
* are supported by the firmware. The Widget will ignore any unsupported application messages.
*/
enum FIRMWARE_TYPE : uint8_t {
FwUnknown = 0,
FwDMX = 1, //!< Normal DMX firmware.
FwRDM = 2, //!< RDM firmware
FwSniffer = 3 //!< RDM Sniffer firmware
};
/**
* @brief All message labels from API 1.44
*/
enum MESSAGE_LABEL : uint8_t {
OpNoop = 0, //!< null message, out-of-spec definition. Not in API!
OpReprogramFirmware = 1, //!< Reprogram Firmware Request
OpProgramFlashPage = 2, //!< Program Flash Page Request / Reply
OpGetWidgetParameters = 3, //!< Get Widget Parameters Request / Reply
OpSetWidgetParameters = 4, //!< Set Widget Parameters Request
OpRecievedDmxPacket = 5, //!< Received DMX Packet
OpOutputOnlySendDMX = 6, //!< Output Only Send DMX Packet Request
OpSendRDMData = 7, //!< Send RDM Packet Request
OpRecieveDMXOnChange = 8, //!< Receive DMX on Change
OpRecievedDMXChanged = 9, //!< Received DMX Change Of State Packet
OpGetWidgetSerial = 10, //!< Get Widget Serial Number Request / Reply
OpSendRDMDiscovery = 11 //!< Send RDM Discovery Request
};
/**
* @brief Base type of each message data.
*/
struct MessageData
: public streamable
{
/**
* @brief Initialize MessageData with the appropriate Label
* @param l Label
*/
MessageData(uint8_t l) : label(l) {};
const uint8_t label; //!< Message ID label (OpCode)
};
/**
* @brief No Op message (Label = 0, no data)
*
* While null messages (label = 0) are not present in the API spec., allow their IO,
* perhaps to act as a "ping/echo" to detect emulated devices.
*/
struct MsgNoop
: public MessageData
{
MsgNoop() : MessageData(OpNoop) {};
virtual size_t streamSize() const override { return 0; };
virtual void iStream(std::shared_ptr<bufferstream>) override {};
virtual void oStream(std::shared_ptr<bufferstream>) const override{};
};
/**
* @brief 1. Reprogram Firmware Request (Label = 1, no data)
*
* \cite DMXUSBPro This message requests the Widget firmware to run the Widget bootstrap to
* enable reprogramming of the Widget firmware.
*/
struct MsgReprogramFirmware
: public MessageData
{
MsgReprogramFirmware() : MessageData(OpReprogramFirmware) {};
virtual size_t streamSize() const override { return 0; };
virtual void iStream(std::shared_ptr<bufferstream>) override {};
virtual void oStream(std::shared_ptr<bufferstream>) const override{};
};
/**
* @brief 2. Program Flash Page Request (Label=2)
*
* \cite DMXUSBPro This message programs one Flash page of the Widget firmware. The Flash
* pages must be programmed in order from first to last Flash page, with the contents of
* the firmware binary file.
*/
struct MsgProgramFlashPageRequest
: public MessageData
{
MsgProgramFlashPageRequest() : MessageData(OpProgramFlashPage) {};
virtual size_t streamSize() const override { return 64; };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
uint8_t page[64]; //!< One page of firmware binary file.
};
/**
* @brief 3. Program Flash Page Reply (Label=2)
*
* \cite DMXUSBPro The Widget sends this message to the PC on completion of the
* Program Flash Page request.
*/
struct MsgProgramFlashPageReply
: public MessageData
{
MsgProgramFlashPageReply() : MessageData(OpProgramFlashPage) {};
virtual size_t streamSize() const override { return 4; };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
bool success; //!< Success character array product
};
const char SUCCESS_OK[4] = {'T','R','U','E'}; //!< Firmware page write success
const char SUCCESS_FAIL[4] = {'F','A','L','S'}; //!< Firmware page write failure
/**
* @brief 4. Get Widget Parameters Request (Label=3)
*
* \cite DMXUSBPro This message requests the Widget configuration.
*/
struct MsgGetWidgetParametersRequest
: public MessageData
{
MsgGetWidgetParametersRequest() : MessageData(OpGetWidgetParameters) {};
virtual size_t streamSize() const override { return 2; };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
uint16_t size = 0; //!< user configuration size in bytes
};
const size_t USER_CONFIGURATION_MAX = 508; //!< maximum user configuration size
/**
* @brief 5. Get Widget Parameters Request (Label=3)
*
* \cite DMXUSBPro The Widget sends this message to the PC in response to the Get
* Widget Parameters request.
*/
struct MsgGetWidgetParametersReply
: public MessageData
{
MsgGetWidgetParametersReply() : MessageData(OpGetWidgetParameters) {};
virtual size_t streamSize() const override { return 5 + user_data.size(); };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
union {
uint16_t version;
struct {
uint8_t fw_rev;
uint8_t fw_type;
};
};
uint8_t break_time; //!< interval count of the DMX output BREAK
uint8_t mab_time; //!< interval count of the DMX output MARK AFTER BREAK
uint8_t rate; //!< DMX output rate in packets per second.
std::vector<uint8_t> user_data; //!< user defined configuration data
};
const float DMX_BREAK_INTERVAL = 10.67; //!< microseconds, resolution of the BREAK interval
const uint8_t DMX_BREAK_MIN = 9; //!< minimum BREAK intervals
const uint8_t DMX_BREAK_MAX = 127; //!< maximum BREAK intervals
const float DMX_MAB_INTERVAL = 10.67; //!< microseconds, resolution of the MAB interval
const uint8_t DMX_MAB_MIN = 1; //!< minimum MAB intervals
const uint8_t DMX_MAB_MAX = 127; //!< maximum MAB intervals
const uint8_t DMX_RATE_MIN = 1; //!< maximum DMX refresh rate
const uint8_t DMX_RATE_MAX = 40; //!< minimum DMX refresh rate
/**
* @brief 6. Set Widget Parameters Request (Label=4)
*
* \cite DMXUSBPro This message sets the Widget configuration.
*/
struct MsgSetWidgetParametersRequest
: public MessageData
{
MsgSetWidgetParametersRequest() : MessageData(OpSetWidgetParameters) {};
virtual size_t streamSize() const override { return 5 + user_data.size(); };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
uint8_t break_time; //!< interval count of the DMX output BREAK
uint8_t mab_time; //!< interval count of the DMX output MARK AFTER BREAK
uint8_t rate; //!< DMX output rate in packets per second.
std::vector<uint8_t> user_data; //!< user defined configuration data
};
/**
* @brief 7. Received DMX Packet (Label=5)
*
* \cite DMXUSBPro The Widget sends this message to the PC unsolicited, whenever the
* Widget receives a DMX or RDM packet from the DMX port, and the Receive DMX on Change
* mode is 'Send always'.
*/
struct MsgRecievedDmxPacket
: public MessageData
{
MsgRecievedDmxPacket() : MessageData(OpRecievedDmxPacket) {};
virtual size_t streamSize() const override { return 1 + data.size(); };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
union {
uint8_t rx_errors; //!< When this is 0, the DMX data is valid.
struct {
bool rx_queue_overflow : 1;
bool rx_overrun : 1;
};
};
std::vector<uint8_t> data; //!< Received DMX data beginning with the start code.
};
enum RX_ERROR {
NoError = 0b00000000,
QueueOverflow = 0x00000001,
Overrun = 0x00000010,
};
const size_t DMX_LAST_SLOT_MIN = 24; //!< length of the shortest supported universe
/**
* @brief 8. Output Only Send DMX Packet Request (Label=6)
*
* \cite DMXUSBPro This message requests the Widget to periodically send a DMX packet out
* of the Widget DMX port at the configured DMX output rate.
*/
struct MsgOutputOnlySendDMX
: public MessageData
{
MsgOutputOnlySendDMX() : MessageData(OpOutputOnlySendDMX) {};
virtual size_t streamSize() const override { return std::max(data.size(), DMX_LAST_SLOT_MIN + 1); };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
std::vector<uint8_t> data; //!< DMX data to send, beginning with the start code.
};
/**
* @brief 9. Send RDM Packet Request (Label=7)
*
* \cite DMXUSBPro This message requests the Widget to send an RDM packet out of
* the Widget DMX port, and then change the DMX port direction to input, so that RDM or
* DMX packets can be received.
*/
struct MsgSendRDMData
: public MessageData
{
MsgSendRDMData() : MessageData(OpSendRDMData) {};
virtual size_t streamSize() const override { return data.size(); };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
std::vector<uint8_t> data; //!< RDM data to send, beginning with the start code.
};
enum DMX_RX_MODE {
RxNotifyAlways = 0, //!< Always send RX'd over USB
RxNotifyOnChange = 1 //!< Only send changed date over USB
};
/**
* @brief 10. Receive DMX on Change (label = 8)
*
* \cite DMXUSBPro This message requests the Widget send a DMX packet to the PC only when
* the DMX values change on the input port.
*
* By default the widget will always send, if you want to send on change it must be enabled
* by sending this message.
*/
struct MsgRecieveDMXOnChange
: public MessageData
{
MsgRecieveDMXOnChange() : MessageData(OpRecieveDMXOnChange) {};
virtual size_t streamSize() const override { return 1; };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
DMX_RX_MODE mode = RxNotifyAlways; //!< Always (default), or OnChange
};
/**
* @brief 11.Received DMX Change Of State Packet (Label=9)
*
* \cite DMXUSBPro The Widget sends one or more instances of this message to the PC unsolicited,
* whenever the Widget receives a changed DMX packet from the DMX port, and the Receive DMX on
* Change mode is 'Send on data change only'.
*/
struct MsgRecievedDMXChanged
: public MessageData
{
MsgRecievedDMXChanged() : MessageData(OpRecievedDMXChanged) {};
virtual size_t streamSize() const override { return 6 + data.size(); };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
uint8_t start; //!< Start changed byte number.
uint8_t changed[5]; //!< Changed bit array.
std::vector<uint8_t> data; //!< Changed DMX data byte data.
};
/**
* @brief 12.Get Widget Serial Number Request (Label = 10, no data)
*
* \cite DMXUSBPro This message requests the Widget serial number.
*/
struct MsgGetWidgetSerialRequest
: public MessageData
{
MsgGetWidgetSerialRequest() : MessageData(OpGetWidgetSerial) {};
virtual size_t streamSize() const override { return 0; };
virtual void iStream(std::shared_ptr<bufferstream>) override {};
virtual void oStream(std::shared_ptr<bufferstream>) const override{};
};
/**
* @brief 13.Get Widget Serial Number Reply (Label = 10)
*
* \cite DMXUSBPro The Widget sends this message to the PC in response to the Get Widget
* Serial Number request.
*/
struct MsgGetWidgetSerialReply
: public MessageData
{
MsgGetWidgetSerialReply() : MessageData(OpGetWidgetSerial) {};
virtual size_t streamSize() const override { return 4; };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
uint32_t serial; //!< BCD serial number.
};
/**
* @brief 14. Send RDM Discovery Request (Label=11)
*
* \cite DMXUSBPro This message requests the Widget to send an RDM Discovery Request packet
* out of the Widget DMX port, and then receive an RDM Discovery Response.
*/
struct MsgSendRDMDiscovery
: public MessageData
{
MsgSendRDMDiscovery() : MessageData(OpSendRDMDiscovery) {};
virtual size_t streamSize() const override { return 38; };
virtual void iStream(std::shared_ptr<bufferstream>) override;
virtual void oStream(std::shared_ptr<bufferstream>) const override;
uint8_t request[38]; //!< DISC_UNIQUE_BRANCH RDM request packet to send.
};
/**
* @brief BCDtoDecimal
* @param bcd
* @return
*/
template <typename T>
T BCDtoDecimal(const T bcd)
{
static_assert(std::is_integral<T>::value, "Integer type required.");
const uint_fast8_t NIBBLE_BIT = CHAR_BIT / 2;
T decimal = bcd & 0xf;
for(size_t i = 1; i * NIBBLE_BIT < sizeof(bcd) * CHAR_BIT; i++)
decimal += ((bcd >> (i * NIBBLE_BIT)) & 0xf) * pow(10,i);
return decimal;
}
/**
* @brief DecimalToBCD
* @param decimal
* @return
*/
template <typename T>
T DecimalToBCD(const T decimal)
{
static_assert(std::is_integral<T>::value, "Integer type required.");
const uint_fast8_t NIBBLE_BIT = CHAR_BIT / 2;
T bcd = decimal & 0xf;
for(size_t i = 1; i * NIBBLE_BIT < sizeof(bcd) * CHAR_BIT; i++)
bcd |= ((decimal / (uint)pow(10,i)) % 10) << (i * NIBBLE_BIT);
return bcd;
}
} // namespace ENTTEC::Pro