/* artnet.h Copyright (c) 2022 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 /** * @brief \cite ARTNET Art-Net is an Ethernet protocol based on the TCP/IP protocol * suite. * * Its purpose is to allow transfer of large amounts of DMX512 data * over a wide area using standard networking technology. */ namespace ARTNET { /// @brief \cite ARTNET Universe Addressing /// /// > The Port-Address of each DMX512 Universe is encoded as a 15-bit number struct PortAddress { union { uint16_t value; struct { union { uint8_t subuni; struct { uint8_t universe : 4; //!< A single DMX512 frame of 512 channels is referred to as a Universe. uint8_t subnet : 4; //!< A group of 16 consecutive universes is referred to as a sub-net. }; }; struct { uint8_t net : 7; //!< A group of 16 consecutive Sub-Nets or 256 consecutive Universes is referred to as a net. There are 128 Nets in total. bool reserved : 1; }; }; }; }; /// @brief \cite ARTNET Protocol Operation /// /// > The UDP port used as both source and destination is 0x1936. static const uint16_t UDP_PORT = 6454; /// @brief Packet ID /// /// > Array of 8 characters, the final character is a null termination. /// > Value = ‘A’ ‘r’ ‘t’ ‘-‘ ‘N’ ‘e’ ‘t’ 0x00 static const uint8_t PACKET_IDENTIFIER[] = { 0x41, 0x72, 0x74, 0x2D, 0x4E, 0x65, 0x74, 0x00}; /// @brief \cite ARTNET Art-Net protocol revision number. /// /// Current value 14. Controllers should ignore communication with nodes /// using a protocol version lower than 14. static const uint16_t VERSION = 14; /// @brief \cite ARTNET Table 1 - OpCodes /// /// The OpCode defines the class of data following within the UDP packet. /// The following table details the legal OpCode values used in Art-Net packets enum OpCode : uint16_t { OpNull = 0x0000, //!< no-op. Ignore this packet. OpPoll = 0x2000, //!< an ArtPoll packet, no other data is contained in this UDP packet. OpPollReply = 0x2100, //!< an ArtPollReply Packet. It contains device status information. OpDiagData = 0x2300, //!< Diagnostics and data logging packet. OpCommand = 0x2400, //!< Used to send text based parameter commands. // OpOutput = 0x5000, //!< an ArtDmx data packet. It contains zero start code DMX512 information for a single Universe. OpDmx = 0x5000, //!< an ArtDmx data packet. It contains zero start code DMX512 information for a single Universe. OpNzs = 0x5100, //!< an ArtNzs data packet. It contains non-zero start code (except RDM) DMX512 information for a single Universe. OpSync = 0x5200, //!< an ArtSync data packet. It is used to force synchronous transfer of ArtDmx packets to a node’s output. OpAddress = 0x6000, //!< an ArtAddress packet. It contains remote programming information for a Node. OpInput = 0x7000, //!< an ArtInput packet. It contains enable – disable data for DMX inputs. OpTodRequest = 0x8000, //!< an ArtTodRequest packet. It is used to request a Table of Devices (ToD) for RDM discovery. OpTodData = 0x8100, //!< an ArtTodData packet. It is used to send a Table of Devices (ToD) for RDM discovery. OpTodControl = 0x8200, //!< an ArtTodControl packet. It is used to send RDM discovery control messages. OpRdm = 0x8300, //!< an ArtRdm packet. It is used to send all non discovery RDM messages. OpRdmSub = 0x8400, //!< an ArtRdmSub packet. It is used to send compressed, RDM Sub-Device data. OpVideoSetup = 0xA010, //!< an ArtVideoSetup packet. It contains video screen setup information for nodes that implement the extended video features. OpVideoPalette = 0xA020, //!< an ArtVideoPalette packet. It contains colour palette setup information for nodes that implement the extended video features. OpVideoData = 0xA040, //!< an ArtVideoData packet. It contains display data for nodes that implement the extended video features. OpMacMaster = 0xF000, //!< depreciated OpMacSlave = 0xF100, //!< depreciated OpFirmwareMaster = 0xF200, //!< an ArtFirmwareMaster packet. It is used to upload new firmware or firmware extensions to the Node. OpFirmwareReply = 0xF300, //!< an ArtFirmwareReply packet. It is returned by the node to acknowledge receipt of an ArtFirmwareMaster packet or ArtFileTnMaster packet. OpFileTnMaster = 0xF400, //!< Uploads user file to node. OpFileFnMaster = 0xF500, //!< Downloads user file from node. OpFileFnReply = 0xF600, //!< Server to Node acknowledge for download packets. OpIpProg = 0xF800, //!< an ArtIpProg packet. It is used to re- programme the IP, Mask and Port address of the Node. OpIpProgReply = 0xF900, //!< an ArtIpProgReply packet. It is returned by the node to acknowledge receipt of an ArtIpProg packet. OpMedia = 0x9000, //!< an ArtMedia packet. It is Unicast by a Media Server and acted upon by a Controller. OpMediaPatch = 0x9100, //!< an ArtMediaPatch packet. It is Unicast by a Controller and acted upon by a Media Server. OpMediaControl = 0x9200, //!< an ArtMediaControl packet. It is Unicast by a Controller and acted upon by a Media Server. OpMediaContrlReply = 0x9300, //!< an ArtMediaControlReply packet. It is Unicast by a Media Server and acted upon by a Controller. OpTimeCode = 0x9700, //!< an ArtTimeCode packet. It is used to transport time code over the network. OpTimeSync = 0x9800, //!< Used to synchronise real time date and clock OpTrigger = 0x9900, //!< Used to send trigger macros OpDirectory = 0x9A00, //!< Requests a node's file list OpDirectoryReply = 0x9B00 //!< Replies to OpDirectory with file list }; /// OEM is unknown or non-registered type static const uint16_t oem_unknown = 0x00ff; /// Used by ArtTrigger for general purpose codes static const uint16_t oem_gobal = 0xffff; /// @brief \cite ARTNET Table 3 - NodeReport Codes enum NodeReport : uint16_t { RcDebug = 0x0000, //!< Booted in debug mode (Only used in development) RcPowerOk = 0x0001, //!< Power On Tests successful RcPowerFail = 0x0002, //!< Hardware tests failed at Power On RcSocketWr1 = 0x0003, //!< Last UDP from Node failed due to truncated length, Most likely caused by a collision. RcParseFail = 0x0004, //!< Unable to identify last UDP transmission. Check OpCode and packet length. RcUdpFail = 0x0005, //!< Unable to open Udp Socket in last transmission attempt RcShNameOk = 0x0006, //!< Confirms that Short Name programming via ArtAddress, was successful. RcLoNameOk = 0x0007, //!< Confirms that Long Name programming via ArtAddress, was successful. RcDmxError = 0x0008, //!< DMX512 receive errors detected. RcDmxUdpFull = 0x0009, //!< Ran out of internal DMX transmit buffers. RcDmxRxFull = 0x000A, //!< Ran out of internal DMX Rx buffers. RcSwitchErr = 0x000B, //!< Rx Universe switches conflict. RcConfigErr = 0x000C, //!< Product configuration does not match firmware. RcDmxShort = 0x000D, //!< DMX output short detected. See GoodOutput field. RcFirmwareFail = 0x000E, //!< Last attempt to upload new firmware failed. RcUserFail = 0x000F, //!< User changed switch settings when address locked by remote programming. User changes ignored. RcFactoryRes = 0x0010 //!< Factory reset has occurred. }; /// \cite ARTNET Table 4 - Style Codes enum Style : uint8_t { StNode = 0x00, //!< A DMX to / from Art-Net device StController = 0x01, //!< A lighting console. StMedia = 0x02, //!< A Media Server. StRoute = 0x03, //!< A network routing device. StBackup = 0x04, //!< A backup device. StConfig = 0x05, //!< A configuration or diagnostic tool StVisual = 0x06 //!< A visualiser }; /// \cite ARTNET Table 5 - Priority Codes enum Priority : uint8_t { DpLow = 0x10, //!< Low priority message. DpMed = 0x40, //!< Medium priority message. DpHigh = 0x80, //!< High priority message. DpCritial = 0xE0, //!< Critical priority message. DpVolatile = 0xF0 //!< Volatile message. }; /// Set the communication behavior during ArtPoll struct TalkToMe { union { uint8_t value = 0; struct { bool depreciated : 1; bool reply_on_change : 1; //!< Send ArtPollReply whenever Node conditions change. bool diag_enable : 1; //!< Send me diagnostics messages. bool diag_unicast : 1; //!< Diagnostics messages are unicast. (if bit 2). bool VLC_disable : 1; //!< Disable VLC transmission. uint8_t reserved : 3; }; }; }; /** * @brief The OEM word * * Describes the equipment vendor and the feature set available. * Bit 15 high indicates extended features available. */ struct OEM { union { uint16_t value = oem_unknown; struct { uint16_t manufacturer : 15; bool extended_features : 1; }; }; }; /** * @brief The Authority enum */ enum Authority : uint8_t { AuthorityUnkown = 0b00, //!< Port-Address Programming Authority unknown. AuthorityLocal = 0b01, //!< Port-Address set by front panel controls. AuthorityRemote = 0b10, //!< All or part of Port-Address programmed by network AuthorityUnused = 0b11, //!< not used }; /** * @brief The Indicator enum */ enum Indicator : uint8_t { IndicatorUnknown = 0b00, //!< Indicator state unknown. IndicatorIdentify = 0b01, //!< Indicators in Locate / Identify Mode. IndicatorMute = 0b10, //!< Indicators in Mute Mode. IndicatorNormal = 0b11, //!< Indicators in Normal Mode. }; /** * @brief The FailoverMode enum */ enum FailoverMode : uint8_t { FailoverHoldLast = 0b00, //!< Hold last state. FailoverAllZeros = 0b01, //!< All output to zero. FailoverAllFull = 0b10, //!< All output to full. FailoverScenePlayback = 0b11, //!< Playback failover scene. }; /** * @brief The General_Status register */ struct GeneralStatus { union { uint8_t value1 = 0; struct { bool ubea_valid : 1; //!< UBEA present and not corrupt. bool rdm_capable : 1; //!< Capable of Remote Device Management (RDM). bool boot_failsafe : 1; //!< Booted from ROM bool reserved1 : 1; //!< not implimented Authority authority : 2; //!< Port-Address Programming Authority Indicator indicator : 2; //!< Indicator State }; }; union { uint8_t value2 = 0b00010000; // 15-bit port-addresses (Art-Net 3 or 4) struct { bool web_config_available : 1; //!< supports web browser configuration. bool DHCP_active : 1; //!< Node’s IP is DHCP configured. bool DHCP_capable : 1; //!< Node is DHCP capable. bool port_address_15b : 1; //!< Node supports 15 bit Port-Address bool sACN_capable : 1; //!< Node is able to switch between Art-Net and sACN. bool squawking : 1; //!< is sqawking bool io_remote_set : 1; //!> Node supports switching of output style using ArtCommand. bool remote_RDM : 1; //!> Node supports control of RDM using ArtCommand. }; }; union { uint8_t value3 = 0; struct { uint8_t reserved3 : 5; //!< Not used, set to zero bool failover_capable : 1; //!< Node supports fail-over. FailoverMode failover_mode : 2; //!< network data loss behavior. }; }; }; /** * @brief The KnownProtocols enum * * Protocols elegible to be reported in the PortTypes array */ enum KnownProtocols : uint8_t { Protocol_DMX = 0b000000, Protocol_MIDI = 0b000001, Protocol_Avab = 0b000010, Protocol_CMX = 0b000011, Protocol_ADB = 0b000100, Protocol_ArtNet = 0b000101, }; /** * @brief The PortTypes struct * * An array of 4 PortTypes are reported in the ArtPollReply packet. */ struct PortTypes { union { uint8_t value = 0; struct { KnownProtocols protocol : 6; bool can_input : 1; bool can_output : 1; }; }; }; /** * @brief The GoodInput struct * * An array of 4 GoodInput are reported in the ArtPollReply packet. */ struct GoodInput { union { uint8_t value = 0; struct { uint8_t reserved : 2; //!< Unused and transmitted as zero. bool rx_errors : 1; //!< Receive errors detected. bool disabled : 1; //!< Input is disabled. bool DMX_text : 1; //!< includes DMX512 text packets. bool DMX_SIP : 1; //!< includes DMX512 SIP’s. bool DMX_test : 1; //!< includes DMX512 test packets. bool active : 1; //!< Data received. }; }; }; /** * @brief The GoodOutput struct * * An array of 4 GoodOutput are reported in the ArtPollReply packet. */ struct GoodOutput { union { uint8_t valueA = 0; struct { bool sACN_enabled : 1; //!< Output is selected to transmit sACN. bool merge_LTP : 1; //!< Merge Mode is LTP. bool output_short : 1; //!< DMX output short detected on power up. bool merge_ArtNet : 1; //!< Output is merging ArtNet data. bool DMX_text : 1; //!< includes DMX512 text packets. bool DMX_SIP : 1; //!< includes DMX512 SIP’s. bool DMX_test : 1; //!< includes DMX512 test packets. bool active : 1; //!< Data received. }; }; union { uint8_t valueB = 0; struct { uint8_t reserved : 6; //!< Not used, set to zero bool output_continuous : 1; //!< oposite of changes-only bool rdm_disabled : 1; //!< RDM disabled }; }; }; /** * @brief The ActivityReport struct * * reported in the ArtPollReply packet as SwMacro and SwRemote */ struct ActivityReport { union { uint8_t value = 0; struct { bool active_1 : 1; bool active_2 : 1; bool active_3 : 1; bool active_4 : 1; bool active_5 : 1; bool active_6 : 1; bool active_7 : 1; bool active_8 : 1; }; }; }; /** * @brief The AddressCommand enum */ enum AddressCommand : uint8_t { // Node configuration commands: AcNone = 0x00, //!< No action AcCancelMerge = 0x01, //!< if in merge mode, cancel AcLedNormal = 0x02, //!< set front panel indicators to normal AcLedMute = 0x03, //!< switch off front panel indicators AcLedLocate = 0x04, //!< flash front panel indicators AcResetRxFlags = 0x05, //!< reset SIP, Text, Test and Error flags // Fail-over configuration commands: AcFailHold = 0x08, //!< hold last look AcFailZero = 0x09, //!< output zeros on data loss AcFailFull = 0x0a, //!< output full on data loss AcFailScene = 0x0b, //!< output scene on data loss AcFailRecord = 0x0c, //!< record current output state as failover scene // Port configuration commands: AcMergeLtp0 = 0x10, //!< set port 0 to merge LTP AcMergeLtp1 = 0x11, //!< set port 1 to merge LTP AcMergeLtp2 = 0x12, //!< set port 2 to merge LTP AcMergeLtp3 = 0x13, //!< set port 3 to merge LTP AcMergeHtp0 = 0x50, //!< set port 0 to merge HTP AcMergeHtp1 = 0x51, //!< set port 1 to merge HTP AcMergeHtp2 = 0x52, //!< set port 2 to merge HTP AcMergeHtp3 = 0x53, //!< set port 3 to merge HTP AcArtNetSel0 = 0x60, //!< set port 0 protocol to Art-Net AcArtNetSel1 = 0x61, //!< set port 1 protocol to Art-Net AcArtNetSel2 = 0x62, //!< set port 2 protocol to Art-Net AcArtNetSel3 = 0x63, //!< set port 3 protocol to Art-Net AcAcnSel0 = 0x70, //!< set port 0 protocol to sACN AcAcnSel1 = 0x71, //!< set port 1 protocol to sACN AcAcnSel2 = 0x72, //!< set port 2 protocol to sACN AcAcnSel3 = 0x73, //!< set port 3 protocol to sACN AcClearOp0 = 0x90, //!< clear output buffer for port 0 AcClearOp1 = 0x91, //!< clear output buffer for port 1 AcClearOp2 = 0x92, //!< clear output buffer for port 2 AcClearOp3 = 0x93, //!< clear output buffer for port 3 AcStyleDelta0 = 0xa0, //!< set port 0 to changes-only AcStyleDelta1 = 0xa1, //!< set port 1 to changes-only AcStyleDelta2 = 0xa2, //!< set port 2 to changes-only AcStyleDelta3 = 0xa3, //!< set port 3 to changes-only AcStyleConst0 = 0xb0, //!< set port 0 to constant output AcStyleConst1 = 0xb1, //!< set port 1 to constant output AcStyleConst2 = 0xb2, //!< set port 2 to constant output AcStyleConst3 = 0xb3, //!< set port 3 to constant output AcRdEnable0 = 0xc0, //!< enable RDM for port 0 AcRdEnable1 = 0xc1, //!< enable RDM for port 1 AcRdEnable2 = 0xc2, //!< enable RDM for port 2 AcRdEnable3 = 0xc3, //!< enable RDM for port 3 AcRdmDisable0 = 0xd0, //!< disable RDM for port 0 AcRdmDisable1 = 0xd1, //!< disable RDM for port 1 AcRdmDisable2 = 0xd2, //!< disable RDM for port 2 AcRdmDisable3 = 0xd3, //!< disable RDM for port 3 }; /** * @brief The TimecodeType enum */ enum TimecodeType : uint8_t { Film = 0, //!< 24 fps EBU = 1, //!< 25 fps DF = 3, //!< 29.97 fps SMPTE = 4, //!< 30 fps }; /** * @brief The Timecode struct */ struct Timecode { uint8_t frame; //!< 24-30, depending on type uint8_t seconds; //!< 0-59 uint8_t minutes; //!< 0-59 uint8_t hours; //!< 0-24 TimecodeType type; //!< framerate }; // Table 7 – ArtTrigger Key Values. /// The SubKey field contains an ASCII character which the receiving device /// should process as if it were a keyboard press. static const uint8_t KeyAscii = 0; /// The SubKey field contains the number of a Macro which the receiving /// device should execute. static const uint8_t KeyMacro = 1; /// The SubKey field contains a soft-key number which the receiving device /// should process as if it were a soft-key keyboard press. static const uint8_t KeySoft = 2; /// The SubKey field contains the number of a Show which the receiving /// device should run. static const uint8_t KeyShow = 3; static const std::size_t Short_Name_Length = 18; //!< string length static const std::size_t Long_Name_Length = 64; //!< string length static const std::size_t Node_Report_Length = 64; //!< string length /// \cite ARTNET /// ...to allow transition between synchronous and non-synchronous modes, a /// node shall time out to non-synchronous operation if an ArtSync is not /// received for 4 seconds or more. static const uint16_t SYNC_LOSS_TIMEOUT = 4000; namespace VLC { /// The DMX512 start code of this (ArtVlc) packet is set to 0x91. static const uint8_t START_CODE = 0x91; /// Magic number used to identify this packet. static const uint8_t MagicNumber[] = {0x41, 0x4c, 0x45}; /// Payload contains a simple text string representing a URL. static const uint16_t BeaconURL = 0x0000; /// Payload contains a simple ASCII text message static const uint16_t BeaconText = 0x0001; } // namespace ARTNET::VLC namespace FIRMWARE { /// Type of firmware master packet enum MasterType : uint8_t { FirmFirst = 0x00, //!< The first packet of a firmware upload. FirmCont = 0x01, //!< A consecutive continuation packet of a firmware upload. FirmLast = 0x02, //!< The last packet of a firmware upload. UbeaFirst = 0x03, //!< The first packet of a UBEA upload. UbeaCont = 0x04, //!< A consecutive continuation packet of a UBEA upload. UbeaLast = 0x05, //!< The last packet of a UBEA upload. }; /// Type of firmware reply packet enum ResponseType : uint8_t { FirmBlockGood = 0x00, //!< Last packet recieved successfully. FirmAllGood = 0x01, //!< All firmware receieved successfully. FirmFail = 0xff, //!< Firmware upload failed. }; } // namespace ARTNET::FIRMWARE namespace RDM { enum Version : uint8_t { Draft = 0x00, //!< RDM DRAFT V1.0 Standard = 0x01, //!< RDM V1.0 }; enum TodRequestCommand : uint8_t { TodFull = 0x00, //!< Send the Entire TOD }; enum TodCommandResponse : uint8_t { ResponseTodFull = 0x00, //!< entire or packet in sequence of the entire TOD. ResponseTodNak = 0xff, //!< TOD is not available or discovery is incomplete. }; enum TodControlCommand : uint8_t { AtcNone = 0x00, //!< No action. AtcFlush = 0x01, //!< The node flushes its TOD and instigates full discovery. }; enum RdmCommand : uint8_t { ArProcess = 0x00, //!< Process RDM Packet. }; } // namespace ARTNET::RDM /// @brief Communication Activity /// /// On if any Art-Net packets detected on network, timeout after 6 seconds. static const uint16_t DATA_LOSS_TIMEOUT = 6000; } // namespace ARTNET