├── .gitignore ├── LICENSE ├── README.md ├── examples ├── DMXFadeTest │ └── DMXFadeTest.ino ├── DMXInputTest │ └── DMXInputTest.ino ├── DMXUSBSerial │ ├── DMXUSBSerial.ino │ ├── LXENTTECSerial.cpp │ └── LXENTTECSerial.h ├── RDMDeviceTest │ └── RDMDeviceTest.ino └── rdmControllerTest │ └── rdmControllerTest.ino ├── extras └── doc │ ├── Classes │ └── LXSAMD21DMX │ │ ├── index.html │ │ └── toc.html │ ├── index.html │ └── toc.html ├── keywords.txt ├── library.properties └── src ├── LXSAMD21DMX.cpp ├── LXSAMD21DMX.h └── rdm ├── TOD.cpp ├── TOD.h ├── UID.cpp ├── UID.h ├── rdm_utility.c └── rdm_utility.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Claude Heintz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of LXTeensy3DMX nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LXSAMD21DMX 2 | DMX Driver for AVR SAM-D21 microcontrollers (Arduino MKR1000 & Zero, Adafruit Feather) 3 | 4 | LXSAMD21DMX is a driver for sending or receiving DMX using one of the SAM D-21's five SERCOM serial periperhial interfaces 5 | 6 | LXSAMD21DMX output mode continuously sends DMX once its interrupts have been enabled using startOutput(). 7 | Use setSlot() to set the level value for a particular DMX dimmer/address/channel. 8 | 9 | LXSAMD21DMX input mode continuously receives DMX once its interrupts have been enabled using startInput() 10 | Use getSlot() to read the level value for a particular DMX dimmer/address/channel. 11 | 12 | LXSAMD21DMX is used with a single instance called SAMD21DMX -------------------------------------------------------------------------------- /examples/DMXFadeTest/DMXFadeTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file DMXFadeTest.ino 4 | @author Claude Heintz 5 | @license BSD (see SAMD21DMX.h or http://lx.claudeheintzdesign.com/opensource.html) 6 | @copyright 2016 by Claude Heintz 7 | 8 | Simple Fade test of SAMD21 DMX Driver 9 | @section HISTORY 10 | 11 | v1.00 - First release 12 | */ 13 | /**************************************************************************/ 14 | 15 | // The general default for the LXSAMD21DMX is to use SERCOM 4 with pins 5 and 4 for DMX RX/TX 16 | // Some SERCOM & Pin setups are defined for specific boards. 17 | // Read the LXSAMD21DMX.h file for other options and uncomment/edit the following line to select them: 18 | //#define use_optional_sercom_macros 4 19 | 20 | #include 21 | 22 | 23 | uint8_t level = 0; 24 | 25 | void setup() { 26 | SAMD21DMX.setDirectionPin(3); // Or, wire pins 2 & 3 of MAX485 to v+ for testing 27 | SAMD21DMX.startOutput(); 28 | } 29 | 30 | /************************************************************************ 31 | 32 | The main loop fades the levels of addresses 1 and 505 and 512 to full 33 | 34 | *************************************************************************/ 35 | 36 | void loop() { 37 | SAMD21DMX.setSlot(1,level); 38 | SAMD21DMX.setSlot(505,level); 39 | SAMD21DMX.setSlot(512,level); 40 | delay(50); 41 | level++; 42 | } -------------------------------------------------------------------------------- /examples/DMXInputTest/DMXInputTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file DMXInputTest.ino 4 | @author Claude Heintz 5 | @license BSD (see SAMD21DMX.h or http://lx.claudeheintzdesign.com/opensource.html) 6 | @copyright 2016 by Claude Heintz 7 | 8 | Control brightness of LED on PWM_PIN with DMX address 1 9 | @section HISTORY 10 | 11 | v1.00 - First release 12 | */ 13 | /**************************************************************************/ 14 | 15 | // The general default for the LXSAMD21DMX is to use SERCOM 4 with pins 5 and 4 for DMX RX/TX 16 | // Some SERCOM & Pin setups are defined for specific boards. 17 | // Read the LXSAMD21DMX.h file for other options and uncomment/edit the following line to select them: 18 | //#define use_optional_sercom_macros 4 19 | 20 | #include 21 | 22 | #define PWM_PIN 2 23 | int got_dmx = 0; 24 | 25 | void setup() { 26 | pinMode(PWM_PIN, OUTPUT); 27 | 28 | SAMD21DMX.setDataReceivedCallback(&gotDMXCallback); 29 | SAMD21DMX.startInput(); 30 | 31 | Serial.begin(115200); 32 | } 33 | 34 | 35 | // ***************** input callback function ************* 36 | 37 | void gotDMXCallback(int slots) { 38 | got_dmx = slots; 39 | } 40 | 41 | /************************************************************************ 42 | 43 | The main loop checks to see if dmx input is available (got_dmx>0) 44 | And then reads the level of dimmer 1 to set PWM level of LED 45 | 46 | *************************************************************************/ 47 | 48 | void loop() { 49 | if ( got_dmx ) { 50 | analogWrite(PWM_PIN,SAMD21DMX.getSlot(1)); 51 | Serial.println("---"); 52 | Serial.println(SAMD21DMX.getSlot(1)); 53 | Serial.println(SAMD21DMX.getSlot(2)); 54 | got_dmx = 0; 55 | Serial.println(got_dmx); 56 | Serial.println("___"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/DMXUSBSerial/DMXUSBSerial.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file DMXUSBSerial.ino 4 | @author Claude Heintz 5 | @license BSD (see SAMD21DMX.h or http://lx.claudeheintzdesign.com/opensource.html) 6 | @copyright 2016 by Claude Heintz 7 | 8 | This sketch allows a MKR1000 board to emulate parts of an ENTTEC DMX USB Pro's functions. 9 | @section HISTORY 10 | 11 | v1.00 - First release 12 | */ 13 | /**************************************************************************/ 14 | 15 | // The general default for the LXSAMD21DMX is to use SERCOM 4 with pins 5 and 4 for DMX RX/TX 16 | // Some SERCOM & Pin setups are defined for specific boards. 17 | // Read the LXSAMD21DMX.h file for other options and uncomment/edit the following line to select them: 18 | //#define use_optional_sercom_macros 4 19 | 20 | #include 21 | #include "LXENTTECSerial.h" 22 | 23 | // ********************** defines ********************** 24 | 25 | // Pin 6 has an LED connected on MKR1000 26 | #define RED_LED_PIN 7 27 | #define GRN_LED_PIN 6 28 | #define BLU_LED_PIN 8 29 | 30 | #define RED_LED_BIT 1 31 | #define GRN_LED_BIT 2 32 | #define BLU_LED_BIT 4 33 | 34 | #define RXTX_PIN 3 35 | 36 | #define MODE_OUTPUT_DMX 0 37 | #define MODE_INPUT_DMX 1 38 | 39 | #define PACKET_LABEL_DMX 6 40 | #define PACKET_LABEL_RECEIVE 8 41 | #define PACKET_LABEL_GET_INFO 3 42 | #define PACKET_LABEL_GET_SERIAL 10 43 | 44 | // ********************** globals ********************** 45 | 46 | uint8_t green_pin = 0; 47 | uint8_t mode = MODE_OUTPUT_DMX; 48 | int got_dmx = 0; 49 | uint8_t buffer[513]; 50 | LXENTTECSerial eSerial = LXENTTECSerial(); 51 | 52 | // ***************** setup() runs once **************** 53 | 54 | void setup() { 55 | pinMode(RED_LED_PIN, OUTPUT); 56 | pinMode(GRN_LED_PIN, OUTPUT); 57 | pinMode(BLU_LED_PIN, OUTPUT); 58 | SAMD21DMX.setDirectionPin(RXTX_PIN); 59 | Serial.begin(57600);//115200, etc. probably doesn't matter because it changes to USB speed 60 | } 61 | 62 | // ***************** utility functions **************** 63 | 64 | /* setLED(uint8_t flags) 65 | * sets color of RGB LED 66 | */ 67 | 68 | void setLED(uint8_t flags) { 69 | digitalWrite(RED_LED_PIN, 1 & flags); 70 | if ( 1 & (flags>>1) ) { 71 | green_pin = (~green_pin) & 0x1; 72 | digitalWrite(GRN_LED_PIN, green_pin); 73 | } 74 | digitalWrite(BLU_LED_PIN, 1 & (flags>>2)); 75 | } 76 | 77 | 78 | // ***************** input/output functions ************* 79 | 80 | void gotDMXCallback(int slots) { 81 | got_dmx = slots; 82 | } 83 | 84 | void doOutputMode() { 85 | uint8_t label = eSerial.readPacket(); 86 | if ( label == ENTTEC_LABEL_SEND_DMX ) { 87 | int s = eSerial.numberOfSlots() + 1; //add start code 88 | for(int i=0; i) before calling this method 95 | 96 | uint8_t LXENTTECSerial::readPacket( void ) { 97 | uint8_t b; 98 | uint8_t label = ENTTEC_LABEL_NONE; 99 | int data_length; 100 | uint8_t userlsb = 0; 101 | uint8_t usermsb = 0; 102 | 103 | b = getNextByte(); 104 | if ( b == 0x7E ) { // byte must be packet start delimiter 0x7E 105 | label = getNextByte(); // 6 = Send DMX 106 | data_length = getNextByte(); // LSB 107 | b = getNextByte(); // MSB 108 | data_length += (b << 8); 109 | switch ( label ) { 110 | case ENTTEC_LABEL_SEND_DMX: 111 | for(int n=0; n DMX_MAX ) { 138 | data_length = DMX_MAX; 139 | } 140 | _dmx_slots = data_length-1; //data length includes start code 141 | break; 142 | case ENTTEC_LABEL_GET_INFO: 143 | this->writeInfo(userlsb+(usermsb<<8)); 144 | break; 145 | case ENTTEC_LABEL_GET_SERIAL: 146 | this->writeSerialNumber(0xffffffff); 147 | break; 148 | } 149 | } else { // bad packet 150 | if ( label == ENTTEC_LABEL_SEND_DMX ) { 151 | _dmx_slots = 0; // buffer is not valid anymore 152 | } 153 | label = ENTTEC_LABEL_NONE; 154 | } 155 | } // good start delimiter 156 | 157 | return label; 158 | } 159 | 160 | // ***** writeDMXPacket() ***** 161 | // writes a DMX Received packet to Serial 162 | // sends the DMX data from the _dmx_data buffer 163 | // 164 | // Important: must call Serial.begin() before calling this method 165 | 166 | void LXENTTECSerial::writeDMXPacket( void ) { 167 | this->writeDMXPacket(_dmx_data, _dmx_slots+1); //includes start code 168 | } 169 | 170 | // ***** writeDMXPacket(buffer, length) ***** 171 | // writes a DMX Received packet to Serial 172 | // sends the DMX data from an external buffer 173 | // buffer must include start code 174 | // 175 | // Important: must call Serial.begin() before calling this method 176 | 177 | void LXENTTECSerial::writeDMXPacket( uint8_t *buffer, int length ) { 178 | int total_length = length + 1; 179 | uint8_t header[5]; 180 | header[0] = 0x7E; 181 | header[1] = ENTTEC_LABEL_RECEIVED_DMX; 182 | header[2] = total_length & 0xff; 183 | header[3] = total_length >> 8; 184 | header[4] = 0; //status byte unused at present 185 | Serial.write(header,5); 186 | Serial.write(buffer, length); 187 | header[0] = 0xE7; 188 | Serial.write(header,1); 189 | } 190 | 191 | // ***** writeInfo(length) ***** 192 | // writes widget info packet 193 | // writes zeros for user data of length 194 | // 195 | // Important: must call Serial.begin() before calling this method 196 | 197 | void LXENTTECSerial::writeInfo( uint16_t length ) { 198 | uint8_t header[9]; 199 | header[0] = 0x7E; 200 | header[1] = ENTTEC_LABEL_GET_INFO; 201 | header[2] = (length+5) & 0xff; 202 | header[3] = (length+5) >> 8; 203 | header[4] = 44; // protocol version lsb 204 | header[5] = 1; // msb 205 | header[6] = 9; // DMX break x10.67 usecs (~99usec on Teensy2++) 206 | header[7] = 1; // MAB x10.67 usecs (~13usec on Teensy2++) 207 | header[8] = 0; // output speed packets/sec 0=max 208 | Serial.write(header,9); 209 | header[0] = 0x00; 210 | for(uint16_t n=0; n> 8 ) & 0xff; 228 | header[6] = ( sn >> 16 ) & 0xff; 229 | header[7] = ( sn >> 24 ) & 0xff; 230 | header[8] = 0xE7; 231 | Serial.write(header,9); 232 | } 233 | 234 | // ***** getNextByte() ***** 235 | // blocks until byte is available on Serial connection 236 | // 237 | 238 | uint8_t LXENTTECSerial::getNextByte( void ) { 239 | while ( true ) { 240 | if ( Serial.available()) { 241 | return Serial.read(); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /examples/DMXUSBSerial/LXENTTECSerial.h: -------------------------------------------------------------------------------- 1 | /* LXENTTECSerial.h 2 | Copyright 2015 by Claude Heintz Design 3 | This code is in the public domain 4 | */ 5 | 6 | #ifndef LXENTTECSerial_H 7 | #define LXENTTECSerial_H 8 | 9 | #include 10 | #include 11 | 12 | #define DMX_MIN 25 13 | #define DMX_MAX 513 14 | 15 | #define ENTTEC_LABEL_NONE 0 16 | #define ENTTEC_LABEL_GET_INFO 3 17 | #define ENTTEC_LABEL_RECEIVED_DMX 5 18 | #define ENTTEC_LABEL_SEND_DMX 6 19 | #define ENTTEC_LABEL_RECEIVE_DMX 8 20 | #define ENTTEC_LABEL_GET_SERIAL 10 21 | 22 | class LXENTTECSerial { 23 | 24 | public: 25 | LXENTTECSerial ( void ); 26 | ~LXENTTECSerial ( void ); 27 | 28 | int numberOfSlots ( void ); 29 | void setNumberOfSlots ( int n ); 30 | uint8_t getSlot ( int slot ); 31 | void setSlot ( int slot, uint8_t value ); 32 | uint8_t startCode ( void ); 33 | void setStartCode ( uint8_t value ); 34 | uint8_t* dmxData ( void ); 35 | 36 | uint8_t readPacket ( void ); 37 | void writeDMXPacket ( void ); 38 | void writeDMXPacket ( uint8_t *buffer, int length ); 39 | void writeInfo ( uint16_t length ); 40 | void writeSerialNumber ( uint32_t sn ); 41 | 42 | private: 43 | uint8_t _dmx_data[DMX_MAX]; 44 | int _dmx_slots; 45 | 46 | uint8_t getNextByte( void ); 47 | }; 48 | 49 | #endif // ifndef LXENTTECSerial_H -------------------------------------------------------------------------------- /examples/RDMDeviceTest/RDMDeviceTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file RDMDeviceTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXSAMD21DMX LICENSE) 6 | @copyright 2017-2021 by Claude Heintz 7 | 8 | Example showing LXSAMD21DMX RDM support for devices. 9 | Control brightness of LED on GPIO14 with DMX address 1 (settable via RDM) 10 | 11 | @section HISTORY 12 | v1.00 - First release 13 | */ 14 | /**************************************************************************/ 15 | 16 | // The general default for the LXSAMD21DMX is to use SERCOM 4 with pins 5 and 4 for DMX RX/TX 17 | // Some SERCOM & Pin setups are defined for specific boards. 18 | // Read the LXSAMD21DMX.h file for other options and uncomment/edit the following line to select them: 19 | //#define use_optional_sercom_macros 4 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | int got_dmx = 0; 28 | int got_rdm = 0; 29 | uint8_t discovery_enabled = 1; 30 | uint16_t start_address = 1; 31 | uint16_t input_value = 0; 32 | 33 | uint8_t device_label[33]; 34 | 35 | #define DEFAULT_DEVICE_LABEL "RDM dev test v1.0" 36 | #define MFG_LABEL "LXDMX" 37 | #define MODEL_DESCRIPTION "RDMDeviceTest" 38 | 39 | #define DIRECTION_PIN 15 40 | #define LED_PIN 14 41 | #define BUILTIN_LED 13 42 | 43 | void setup() { 44 | 45 | pinMode(BUILTIN_LED, OUTPUT); 46 | pinMode(DIRECTION_PIN, OUTPUT); 47 | pinMode(LED_PIN, OUTPUT); 48 | //diagnostic pins 49 | pinMode(12, OUTPUT); 50 | pinMode(16, INPUT_PULLUP); 51 | 52 | strcpy((char*)device_label, DEFAULT_DEVICE_LABEL); 53 | 54 | SAMD21DMX.setDataReceivedCallback(&gotDMXCallback); 55 | SAMD21DMX.setRDMReceivedCallback(&gotRDMCallback); 56 | LXSAMD21DMX::THIS_DEVICE_ID.setBytes(0x6C, 0x78, 0x0F, 0x0A, 0x0C, 0x0E); //change device ID from default 57 | 58 | SAMD21DMX.startRDM(DIRECTION_PIN, DMX_TASK_RECEIVE); 59 | } 60 | 61 | 62 | // ***************** input callback function ************* 63 | 64 | void gotDMXCallback(int slots) { 65 | got_dmx = slots; 66 | } 67 | 68 | void gotRDMCallback(int len) { 69 | // rdm start code and checksum are validated before this is called 70 | got_rdm = len; 71 | } 72 | 73 | /************************************************************************ 74 | 75 | The main loop checks to see if dmx input is available (got_dmx>0) 76 | And then reads the level of dimmer 1 to set PWM level of LED connected to pin 14 77 | 78 | *************************************************************************/ 79 | 80 | void loop() { 81 | if ( got_dmx ) { 82 | input_value = SAMD21DMX.getSlot(start_address); 83 | //gamma correct 84 | input_value = (input_value * input_value ) / 255; 85 | analogWrite(LED_PIN,input_value); 86 | got_dmx = 0; //reset 87 | 88 | } else if ( got_rdm ) { 89 | 90 | uint8_t* rdmdata = SAMD21DMX.receivedRDMData(); 91 | 92 | uint8_t cmdclass = rdmdata[RDM_IDX_CMD_CLASS]; 93 | uint16_t pid = (rdmdata[RDM_IDX_PID_MSB] << 8 ) | rdmdata[RDM_IDX_PID_LSB]; 94 | 95 | 96 | if ( cmdclass == RDM_DISCOVERY_COMMAND ) { 97 | 98 | if ( pid == RDM_DISC_UNIQUE_BRANCH ) { 99 | if ( discovery_enabled ) { 100 | uint64_t tv = SAMD21DMX.THIS_DEVICE_ID.getValue(); 101 | UID u; 102 | u.setBytes(&rdmdata[24]); //lower 103 | uint64_t uv = u.getValue(); 104 | if ( tv >= uv ) { 105 | u.setBytes(&rdmdata[30]); //upper 106 | uv = u.getValue(); 107 | if ( tv <= uv ) { 108 | SAMD21DMX.sendRDMDiscoverBranchResponse(); 109 | } 110 | } 111 | } 112 | } else { // mute RDM_DISCOVERY_COMMAND PIDs 113 | UID destination; 114 | destination.setBytes(&rdmdata[RDM_IDX_DESTINATION_UID]); 115 | 116 | if ( pid == RDM_DISC_MUTE ) { 117 | if ( destination == SAMD21DMX.THIS_DEVICE_ID ) { 118 | discovery_enabled = 0; 119 | // send ACK 120 | UID source; 121 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 122 | SAMD21DMX.sendMuteAckRDMResponse(RDM_DISC_COMMAND_RESPONSE, source, RDM_DISC_MUTE); 123 | } 124 | } else if ( pid == RDM_DISC_UNMUTE ) { 125 | if ( destination == BROADCAST_ALL_DEVICES_ID ) { 126 | // just un-mute 127 | discovery_enabled = 1; 128 | } else if ( destination == SAMD21DMX.THIS_DEVICE_ID ) { 129 | discovery_enabled = 1; 130 | // send ACK 131 | UID source; 132 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 133 | SAMD21DMX.sendMuteAckRDMResponse(RDM_DISC_COMMAND_RESPONSE, source, RDM_DISC_UNMUTE); 134 | } 135 | } 136 | 137 | } 138 | 139 | } else if ( cmdclass == RDM_GET_COMMAND ) { 140 | UID destination; 141 | destination.setBytes(&rdmdata[RDM_IDX_DESTINATION_UID]); 142 | 143 | if ( destination == SAMD21DMX.THIS_DEVICE_ID ) { 144 | UID source; 145 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 146 | 147 | if ( pid == RDM_DEVICE_START_ADDR ) { 148 | 149 | uint8_t sa[2]; 150 | sa[0] = start_address >> 8; 151 | sa[1] = start_address & 0xff; 152 | SAMD21DMX.sendRDMGetResponse(source, pid, sa, 2); 153 | } else if ( pid == RDM_DEVICE_MFG_LABEL ) { 154 | const char * label = MFG_LABEL; 155 | SAMD21DMX.sendRDMGetResponse(source, pid, (uint8_t*)label, 5); 156 | } else if ( pid == RDM_DEVICE_MODEL_DESC ) { 157 | const char * label = MODEL_DESCRIPTION; 158 | SAMD21DMX.sendRDMGetResponse(source, pid, (uint8_t*)label, 13); 159 | } else if ( pid == RDM_DEVICE_DEV_LABEL ) { 160 | SAMD21DMX.sendRDMGetResponse(source, pid, device_label, strlen((const char*)device_label)); 161 | } 162 | 163 | 164 | } 165 | } else if ( cmdclass == RDM_SET_COMMAND ) { 166 | UID destination; 167 | destination.setBytes(&rdmdata[RDM_IDX_DESTINATION_UID]); 168 | 169 | if ( destination == SAMD21DMX.THIS_DEVICE_ID ) { 170 | UID source; 171 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 172 | 173 | if ( pid == RDM_DEVICE_START_ADDR ) { 174 | uint16_t scratch = (rdmdata[24] << 8) + rdmdata[25]; 175 | if (( scratch > 0 ) && ( scratch < 513 )) { 176 | start_address = scratch; 177 | } 178 | SAMD21DMX.sendAckRDMResponse(RDM_SET_COMMAND_RESPONSE, source, pid); 179 | 180 | } else if ( pid == RDM_DEVICE_DEV_LABEL ) { 181 | uint8_t llen = 0; 182 | if ( rdmdata[2] > 24 ) { //label not empty string 183 | llen = rdmdata[2] - 24; 184 | if ( llen > 32 ) { //limit to max 32 characters 185 | llen = 32; 186 | } 187 | } 188 | for ( uint8_t j=0; j<33; j++) { //copy label, zero the rest of the array 189 | if ( j < llen ) { 190 | device_label[j] = rdmdata[24+j]; 191 | } else { 192 | device_label[j] = 0; 193 | } 194 | } // <-for 195 | SAMD21DMX.sendAckRDMResponse(RDM_SET_COMMAND_RESPONSE, source, pid); 196 | } // <-pid RDM_DEVICE_DEV_LABEL 197 | } 198 | } 199 | got_rdm = 0; 200 | } //gotRDM 201 | } 202 | -------------------------------------------------------------------------------- /examples/rdmControllerTest/rdmControllerTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file rdmControllerTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXSAMD21DMX.h LICENSE) 6 | @copyright 2017 by Claude Heintz 7 | 8 | Test of LXSAMD21DMX RDM functions 9 | Changes output level of some DMX Addresses while building RDM 10 | table of devices. Turns identify on and off for found RDM devices. 11 | 12 | @section HISTORY 13 | 14 | v1.00 - First release 15 | */ 16 | /**************************************************************************/ 17 | 18 | // The general default for the LXSAMD21DMX is to use SERCOM 4 with pins 5 and 4 for DMX RX/TX 19 | // Some SERCOM & Pin setups are defined for specific boards. 20 | // Read the LXSAMD21DMX.h file for other options and uncomment/edit the following line to select them: 21 | //#define use_optional_sercom_macros 4 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | uint8_t testLevel = 0; 30 | uint8_t loopDivider = 0; 31 | uint8_t identifyFlag = 1; 32 | uint8_t tableChangedFlag = 0; 33 | 34 | TOD tableOfDevices; 35 | TOD discoveryTree; 36 | 37 | UID lower(0,0,0,0,0,0); 38 | UID upper(0,0,0,0,0,0); 39 | UID mid(0,0,0,0,0,0); 40 | UID found(0,0,0,0,0,0); 41 | 42 | #define DIRECTION_PIN 3 43 | #define DISC_STATE_SEARCH 0 44 | #define DISC_STATE_TBL_CK 1 45 | uint8_t discovery_state = DISC_STATE_TBL_CK; 46 | uint8_t discovery_tbl_ck_index = 0; 47 | 48 | 49 | //***************** discovery functions 50 | 51 | void checkDeviceFound(UID found) { 52 | Serial.print("checkDeviceFound "); 53 | Serial.println(found); 54 | if ( testMute(found) ) { 55 | Serial.print("!!!!!!!!!!!!!!!!!!!! found one"); 56 | tableOfDevices.add(found); 57 | tableChangedFlag = 1; 58 | } 59 | } 60 | 61 | uint8_t testMute(UID u) { 62 | // try three times to get response when sending a mute message 63 | if ( SAMD21DMX.sendRDMDiscoveryMute(u, RDM_DISC_MUTE) ) { 64 | return 1; 65 | } 66 | if ( SAMD21DMX.sendRDMDiscoveryMute(u, RDM_DISC_MUTE) ) { 67 | return 1; 68 | } 69 | if ( SAMD21DMX.sendRDMDiscoveryMute(u, RDM_DISC_MUTE) ) { 70 | return 1; 71 | } 72 | return 0; 73 | } 74 | 75 | uint8_t checkTable(uint8_t ck_index) { 76 | if ( ck_index == 0 ) { 77 | SAMD21DMX.sendRDMDiscoveryMute(BROADCAST_ALL_DEVICES_ID, RDM_DISC_UNMUTE); 78 | } 79 | 80 | if ( tableOfDevices.getUIDAt(ck_index, &found) ) { 81 | if ( testMute(found) ) { 82 | // device confirmed 83 | return ck_index += 6; 84 | } 85 | 86 | // device not found 87 | tableOfDevices.removeUIDAt(ck_index); 88 | tableChangedFlag = 1; 89 | return ck_index; 90 | } 91 | // index invalid 92 | return 0; 93 | } 94 | 95 | void identifyEach() { 96 | int i = 0; 97 | uint8_t notDone = 1; 98 | while ( notDone ) { 99 | i = tableOfDevices.getNextUID(i, &found); 100 | if ( i < 0 ) { 101 | notDone = 0; 102 | } else { 103 | //uint16_t data; //for DMX address and identify device on/off 104 | uint8_t data[2]; 105 | if ( SAMD21DMX.sendRDMGetCommand(found, RDM_DEVICE_START_ADDR, data, 2) ) { 106 | uint16_t addr = (data[0] << 8) | data[1]; 107 | 108 | if ( addr == 0x0F ) { 109 | data[0] = 0x00; 110 | data[1] = 0x01; 111 | SAMD21DMX.sendRDMSetCommand(found, RDM_DEVICE_START_ADDR, (uint8_t*)data, 2); 112 | } 113 | 114 | data[0] = 0x01; 115 | SAMD21DMX.sendRDMSetCommand(found, RDM_IDENTIFY_DEVICE, (uint8_t*)data, 1); 116 | delay(2000); 117 | data[0] = 0x00; 118 | SAMD21DMX.sendRDMSetCommand(found, RDM_IDENTIFY_DEVICE, (uint8_t*)data, 1); 119 | } 120 | } 121 | } 122 | } 123 | 124 | //called when range responded, so divide into sub ranges push them on stack to be further checked 125 | void pushActiveBranch(UID lower, UID upper) { 126 | if ( mid.becomeMidpoint(lower, upper) ) { 127 | discoveryTree.push(lower); 128 | discoveryTree.push(mid); 129 | discoveryTree.push(mid); 130 | discoveryTree.push(upper); 131 | } else { 132 | // No midpoint possible: lower and upper are equal or a 1 apart 133 | checkDeviceFound(lower); 134 | checkDeviceFound(upper); 135 | } 136 | } 137 | 138 | void pushInitialBranch() { 139 | lower.setBytes(0); 140 | upper.setBytes(BROADCAST_ALL_DEVICES_ID); 141 | discoveryTree.push(lower); 142 | discoveryTree.push(upper); 143 | 144 | //ETC devices seem to only respond with wildcard or exact manufacturer ID 145 | lower.setBytes(0x657400000000); 146 | upper.setBytes(0x6574FFFFFFFF); 147 | discoveryTree.push(lower); 148 | discoveryTree.push(upper); 149 | } 150 | 151 | uint8_t checkNextRange() { 152 | if ( discoveryTree.pop(&upper) ) { 153 | if ( discoveryTree.pop(&lower) ) { 154 | if ( lower == upper ) { 155 | checkDeviceFound(lower); 156 | } else { //not leaf so, check range lower->upper 157 | uint8_t result = SAMD21DMX.sendRDMDiscoveryPacket(lower, upper, &found); 158 | if ( result ) { 159 | //this range responded, so divide into sub ranges push them on stack to be further checked 160 | pushActiveBranch(lower, upper); 161 | 162 | } else if ( SAMD21DMX.sendRDMDiscoveryPacket(lower, upper, &found) ) { 163 | pushActiveBranch(lower, upper); //if discovery fails, try a second time 164 | } 165 | } // end check range 166 | return 1; // UID ranges may be remaining to test 167 | } // end valid pop 168 | } // end valid pop 169 | return 0; // none left to pop 170 | } 171 | 172 | 173 | 174 | void testRDMDiscovery() { 175 | if ( discovery_state ) { 176 | // check the table of devices 177 | discovery_tbl_ck_index = checkTable(discovery_tbl_ck_index); 178 | if ( discovery_tbl_ck_index == 0 ) { 179 | // done with table check 180 | discovery_state = DISC_STATE_SEARCH; 181 | pushInitialBranch(); 182 | 183 | if ( identifyFlag ) { //once per cycle identify each device 184 | identifyEach(); //this is just to demonstrate GET device address 185 | identifyFlag = 0; //and SET identify device 186 | } 187 | 188 | if ( tableChangedFlag ) { //if the table has changed... 189 | tableChangedFlag = 0; 190 | 191 | // if this were an Art-Net application, you would send an 192 | // ArtTOD packet here, because the device table has changed. 193 | // for this test, we just print the list of devices 194 | Serial.println("_______________ Table Of Devices _______________"); 195 | tableOfDevices.printTOD(); 196 | } 197 | } 198 | } else { // search for devices in range popped from discoveryTree 199 | if ( checkNextRange() == 0 ) { 200 | // done with search 201 | discovery_tbl_ck_index = 0; 202 | discovery_state = DISC_STATE_TBL_CK; 203 | } 204 | } 205 | } 206 | 207 | /************************************************************************ 208 | setup 209 | *************************************************************************/ 210 | void setup() { 211 | Serial.begin(115200); 212 | while( ! Serial ) {} 213 | Serial.print("setup... "); 214 | 215 | pinMode(6, OUTPUT); 216 | 217 | SAMD21DMX.startRDM(DIRECTION_PIN, RDM_DIRECTION_OUTPUT); 218 | Serial.println("setup complete"); 219 | } 220 | 221 | 222 | /************************************************************************ 223 | 224 | The main loop checks to see if the level of the designated slot has changed 225 | and prints the new level to the serial monitor. If a PWM channel is assigned, 226 | it also sets the output level. 227 | 228 | *************************************************************************/ 229 | 230 | void loop() { 231 | delay(2); 232 | testRDMDiscovery(); 233 | 234 | SAMD21DMX.setSlot(152,testLevel); 235 | SAMD21DMX.setSlot(157,255); 236 | loopDivider++; 237 | if ( loopDivider == 4 ) { 238 | testLevel++; 239 | loopDivider = 0; 240 | } 241 | if ( testLevel == 1 ) { 242 | delay(500); 243 | identifyFlag = 1; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /extras/doc/Classes/LXSAMD21DMX/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | LXSAMD21DMX 5 | 6 | 7 | 8 | 134 | 150 | 162 | 252 | 253 | 254 | 255 |
256 | 257 |

LXSAMD21DMX

258 |
259 | 260 |
Declared In:
261 |

Introduction

262 |

LXSAMD21DMX is a driver for sending or receiving DMX using one of the SAM D-21's five serial peripheral interfaces (SERCOM) 263 | UART0 RX pin 0, TX pin 1 264 |

265 |

LXSAMD21DMX output mode continuously sends DMX once its interrupts have been enabled using startOutput(). 266 | Use setSlot() to set the level value for a particular DMX dimmer/address/channel. 267 |

268 |

LXSAMD21DMX input mode continuously receives DMX once its interrupts have been enabled using startInput() 269 | Use getSlot() to read the level value for a particular DMX dimmer/address/channel. 270 |

271 |

LXSAMD21DMX is used with a single instance called LXSAMD21DMX . 272 |

273 |

274 |

Member Functions

275 |
276 |
dmxData
277 |

provides direct access to data array 278 |

279 |
getSlot
280 |

reads the value of a slot/address/channel 281 |

282 |
setDataReceivedCallback
283 |

Function called when DMX frame has been read 284 |

285 |
setDirectionPin
286 |

optional utility sets the pin used to control driver chip's 287 | DE (data enable) line, HIGH for output, LOW for input. 288 |

289 |
setMaxSlots
290 |

Sets the number of slots (aka addresses or channels) sent per DMX frame. 291 |

292 |
setSlot
293 |

Sets the output value of a slot 294 |

295 |
startInput
296 |

starts interrupt that continuously reads DMX data 297 |

298 |
startOutput
299 |

starts interrupt that continuously sends DMX output 300 |

301 |
stop
302 |

disables tx, rx and interrupts. 303 |

304 |
305 |
306 | 307 |

dmxData

308 |

provides direct access to data array 309 |

310 |
311 |
public
312 | 
uint8_t* dmxData( 313 | void);
314 |
315 |
Return Value

pointer to dmx array 316 | 317 |


318 | 319 |

getSlot

320 |

reads the value of a slot/address/channel 321 |

322 |
323 |
public
324 | 
uint8_t getSlot ( 325 | int slot);
326 |
327 |
Return Value

level (0-255) 328 | 329 |

Discussion
330 |

NOTE: Data is not double buffered. 331 | So a complete single frame is not guaranteed. 332 | The ISR continuously reads the next frame into the buffer 333 | 334 |


335 | 336 |

setDataReceivedCallback

337 |

Function called when DMX frame has been read 338 |

339 |
340 |
public
341 | 
void setDataReceivedCallback( 342 | LXRecvCallback callback);
343 |
344 |
Discussion
345 |

Sets a pointer to a function that is called 346 | on the break after a DMX frame has been received. 347 | Whatever happens in this function should be quick! 348 | Best used to set a flag that is polled outside of ISR for available data. 349 | 350 |


351 | 352 |

setDirectionPin

353 |

optional utility sets the pin used to control driver chip's 354 | DE (data enable) line, HIGH for output, LOW for input. 355 |

356 |
357 |
public
358 | 
void setDirectionPin( 359 | uint8_t pin );
360 |
361 |
Parameters
362 |
363 |
364 |
365 | 366 | pin

to be automatically set for input/output direction

367 |
368 |
369 |
370 | 371 |

setMaxSlots

372 |

Sets the number of slots (aka addresses or channels) sent per DMX frame. 373 |

374 |
375 |
public
376 | 
void setMaxSlots ( 377 | int slot);
378 |
379 |
Parameters
380 |
381 |
382 |
383 | 384 | slot

the highest slot number (~24 to 512)

385 |
386 |
387 |
Discussion
388 |

defaults to 512 or DMX_MAX_SLOTS and should be no less DMX_MIN_SLOTS slots. 389 | The DMX standard specifies min break to break time no less than 1024 usecs. 390 | At 44 usecs per slot ~= 24 391 | 392 |


393 | 394 |

setSlot

395 |

Sets the output value of a slot 396 |

397 |
398 |
public
399 | 
void setSlot ( 400 | int slot, 401 | uint8_t value);
402 |
403 |
Parameters
404 |
405 |
406 |
407 | 408 | slot

number of the slot/address/channel (1-512)

409 |
410 | 411 | value

level (0-255)

412 |
413 |
414 |
415 | 416 |

startInput

417 |

starts interrupt that continuously reads DMX data 418 |

419 |
420 |
public
421 | 
void startInput( 422 | void );
423 |
424 |
Discussion
425 |

sets up baud rate, bits and parity, 426 | sets globals accessed in ISR, 427 | enables receive (RE) and rx interrupt (RIE) 428 | 429 |


430 | 431 |

startOutput

432 |

starts interrupt that continuously sends DMX output 433 |

434 |
435 |
public
436 | 
void startOutput( 437 | void );
438 |
439 |
Discussion
440 |

Sets up baud rate, bits and parity, 441 | sets globals accessed in ISR, 442 | enables transmission (TE) and tx interrupts (TIE/TCIE). 443 | 444 |


445 | 446 |

stop

447 |

disables tx, rx and interrupts. 448 |

449 |
450 |
public
451 | 
void stop( 452 | void );
453 |
454 |
455 | 456 |

Member Data

457 |
458 |
_direction_pin
459 |

pin used to control direction of output driver chip 460 |

461 |
_dmxData
462 |

Array of dmx data including start code 463 |

464 |
_interrupt_status
465 |

Indicates mode ISR_OUTPUT_ENABLED or ISR_INPUT_ENABLED or ISR_DISABLED 466 |

467 |
468 |
469 | 470 |

_direction_pin

471 |

pin used to control direction of output driver chip 472 |

473 |
474 |
private
475 | 
uint8_t _direction_pin;
476 |
477 |
478 | 479 |

_dmxData

480 |

Array of dmx data including start code 481 |

482 |
483 |
private
484 | 
uint8_t _dmxData[DMX_MAX_SLOTS+1];
485 |
486 |
487 | 488 |

_interrupt_status

489 |

Indicates mode ISR_OUTPUT_ENABLED or ISR_INPUT_ENABLED or ISR_DISABLED 490 |

491 |
492 |
private
493 | 
uint8_t _interrupt_status;
494 |
495 |

 


498 |
499 | 500 | -------------------------------------------------------------------------------- /extras/doc/Classes/LXSAMD21DMX/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for LXSAMD21DMX (LXSAMD21DMX.h) 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 60 |
Class:
  LXSAMD21DMX
18 | 19 |
20 | 21 | 22 |
Introduction
23 |
24 |
25 | 27 |
26 | Member Functions
28 |
29 | Public 30 |
40 |
41 | 43 |
42 | Member Data
44 |
45 | Protected 46 |
50 |
51 |

Other Reference

52 |
53 | 54 | 55 |
Header
56 |
57 | 58 | 59 |

 

61 |

62 | 63 | -------------------------------------------------------------------------------- /extras/doc/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | LXSAMD21DMX.h 5 | 6 | 7 | 8 | 134 | 150 | 162 | 252 | 253 | 254 | 255 | 256 |
257 | 258 |

LXSAMD21DMX.h

259 |
260 | 262 |
Includes:
"SERCOM.h"
261 | "inttypes.h"
263 |

Introduction

264 |

Use the links in the table of contents to the left to access the documentation.
265 |

266 |

267 |

Classes

268 |
269 |
LXSAMD21DMX
270 |

LXSAMD21DMX is a driver for sending or receiving DMX using one of the SAM D-21's five serial peripheral interfaces (SERCOM). 271 |

272 |

LXSAMD21DMX output mode continuously sends DMX once its interrupts have been enabled using startOutput(). 273 | Use setSlot() to set the level value for a particular DMX dimmer/address/channel. 274 |

275 |

LXSAMD21DMX input mode continuously receives DMX once its interrupts have been enabled using startInput() 276 | Use getSlot() to read the level value for a particular DMX dimmer/address/channel. 277 |

278 |

LXSAMD21DMX is used with a single instance called SAMD21DMX . 279 |

280 |
281 |

 


284 |
285 | 286 | -------------------------------------------------------------------------------- /extras/doc/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for LXSAMD21DMX.h 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 34 |
Header:
  LXSAMD21DMX.h
18 | 19 |
20 | 21 | 22 |
Introduction
23 |
24 |
25 | 27 |
26 | Classes
28 |
29 |
31 | 32 | 33 |

 

35 |

36 | 37 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For LXSAMD21DMX 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | LXSAMD21DMX KEYWORD1 8 | SAMD21DMX KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions 12 | ####################################### 13 | 14 | startOutput KEYWORD2 15 | startInput KEYWORD2 16 | startRDM KEYWORD2 17 | stop KEYWORD2 18 | setDirectionPin KEYWORD2 19 | setMaxSlots KEYWORD2 20 | setSlot KEYWORD2 21 | dmxData KEYWORD2 22 | rdmData KEYWORD2 23 | receivedRDMData KEYWORD2 24 | getSlot KEYWORD2 25 | setDataReceivedCallback KEYWORD2 26 | sendRDMDiscoveryMute KEYWORD2 27 | sendRDMDiscoveryPacket KEYWORD2 28 | sendRDMControllerPacket KEYWORD2 29 | 30 | 31 | ####################################### 32 | # Constants 33 | ####################################### 34 | 35 | DMX_MIN_SLOTS LITERAL1 36 | DMX_MAX_SLOTS LITERAL1 37 | 38 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LXSAMD21DMX 2 | version=2.0 3 | author=Claude Heintz 4 | maintainer=Claude Heintz 5 | sentence=DMX input/output driver for AVR SAM-D21 microcontrollers 6 | paragraph=With addition of external Max485 liner driver chip, enables DMX input/output using hardware serial. 7 | category=Communication 8 | url=https://github.com/claudeheintz/LXSAMD21DMX 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/LXSAMD21DMX.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file LXSAMD21DMX.cpp 4 | @author Claude Heintz 5 | @license BSD (see SAMD21DMX.h or http://lx.claudeheintzdesign.com/opensource.html) 6 | @copyright 2016 by Claude Heintz 7 | 8 | DMX Driver for Arduino MKR1000 9 | 10 | @section HISTORY 11 | 12 | v1.0 - First release 13 | */ 14 | /**************************************************************************/ 15 | 16 | #include "LXSAMD21DMX.h" 17 | #include "Arduino.h" 18 | #include "wiring_private.h" 19 | #include "variant.h" 20 | #include 21 | #include 22 | #include 23 | 24 | //************************************************************************************** 25 | // single instance and shared interrupt status 26 | 27 | LXSAMD21DMX SAMD21DMX; 28 | 29 | UID LXSAMD21DMX::THIS_DEVICE_ID(0x6C, 0x78, 0x00, 0x00, 0x00, 0x04); 30 | 31 | uint8_t _interrupt_mode; 32 | 33 | // **************************** SERCOMn_Handler *************** 34 | // 35 | // DMX_SERCOM_HANDLER_FUNC macro points to handler name 36 | 37 | void DMX_SERCOM_HANDLER_FUNC() 38 | { 39 | switch ( _interrupt_mode ) { 40 | case ISR_OUTPUT_ENABLED: 41 | SAMD21DMX.outputIRQHandler(); 42 | break; 43 | case ISR_INPUT_ENABLED: 44 | SAMD21DMX.inputIRQHandler(); 45 | break; 46 | case ISR_RDM_ENABLED: 47 | SAMD21DMX.rdmIRQHandler(); 48 | break; 49 | } 50 | } 51 | 52 | 53 | // **************************** global data (can be accessed in ISR) *************** 54 | 55 | Uart SerialDMX (&DMX_sercom, PIN_DMX_RX, PIN_DMX_TX, PAD_DMX_RX, PAD_DMX_TX); 56 | 57 | 58 | void setBaudRate(uint32_t baudrate) { 59 | DMX_SERCOM->USART.CTRLA.bit.ENABLE = 0x0u; // must be disabled before writing to USART.BAUD or USART.CTRLA 60 | uint16_t sampleRateValue = 16; 61 | 62 | // see SERCOM.initUART 63 | uint32_t baudTimes8 = (SystemCoreClock * 8) / (sampleRateValue * baudrate); 64 | 65 | DMX_SERCOM->USART.BAUD.FRAC.FP = (baudTimes8 % 8); 66 | DMX_SERCOM->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8); 67 | DMX_SERCOM->USART.CTRLA.bit.ENABLE = 0x1u; // re-enable 68 | } 69 | 70 | //************************************************************************************ 71 | // ************************ LXSAMD21DMXOutput member functions ******************** 72 | 73 | LXSAMD21DMX::LXSAMD21DMX ( void ) { 74 | _direction_pin = DIRECTION_PIN_NOT_USED; //optional 75 | _slots = DMX_MAX_SLOTS; 76 | _interrupt_mode = ISR_DISABLED; 77 | 78 | //zero buffer including _dmxData[0] which is start code 79 | memset(_dmxData, 0, DMX_MAX_SLOTS+1); 80 | } 81 | 82 | 83 | LXSAMD21DMX::~LXSAMD21DMX ( void ) { 84 | stop(); 85 | _receive_callback = NULL; 86 | } 87 | 88 | 89 | void LXSAMD21DMX::startOutput ( void ) { 90 | if ( _direction_pin != DIRECTION_PIN_NOT_USED ) { 91 | digitalWrite(_direction_pin, HIGH); 92 | } 93 | 94 | if ( _interrupt_mode == ISR_INPUT_ENABLED ) { 95 | stop(); 96 | } 97 | 98 | if ( _interrupt_mode == ISR_DISABLED ) { //prevent messing up sequence if already started... 99 | SerialDMX.begin(DMX_BREAK_BAUD, (uint8_t)SERIAL_8N2); 100 | 101 | // Assign pin mux to SERCOM functionality (must come after SerialDMX.begin) 102 | pinPeripheral(PIN_DMX_RX, MUX_DMX_RX); 103 | pinPeripheral(PIN_DMX_TX, MUX_DMX_TX); 104 | 105 | _interrupt_mode = ISR_OUTPUT_ENABLED; 106 | _rdm_task_mode = DMX_TASK_SEND; 107 | _dmx_send_state = DMX_STATE_BREAK; 108 | 109 | transmissionComplete(); //sets TXC interrupt and sends break 110 | } 111 | } 112 | 113 | void LXSAMD21DMX::startInput ( void ) { 114 | if ( _direction_pin != DIRECTION_PIN_NOT_USED ) { 115 | digitalWrite(_direction_pin, LOW); 116 | } 117 | if ( _interrupt_mode == ISR_OUTPUT_ENABLED ) { 118 | stop(); 119 | } 120 | if ( _interrupt_mode == ISR_DISABLED ) { //prevent messing up sequence if already started... 121 | SerialDMX.begin(DMX_DATA_BAUD, (uint8_t)SERIAL_8N2); 122 | 123 | // Assign pin mux to SERCOM functionality (must come after SerialDMX.begin) 124 | pinPeripheral(PIN_DMX_RX, MUX_DMX_RX); 125 | pinPeripheral(PIN_DMX_TX, MUX_DMX_TX); 126 | 127 | _next_read_slot = 0; 128 | _dmx_read_state = DMX_STATE_IDLE; 129 | 130 | _interrupt_mode = ISR_INPUT_ENABLED; 131 | } 132 | } 133 | 134 | void LXSAMD21DMX::startRDM( uint8_t pin, uint8_t direction ) { 135 | pinMode(pin, OUTPUT); 136 | _direction_pin = pin; 137 | if ( direction ) { 138 | startOutput(); //enables transmit interrupt 139 | _next_read_slot = 0; 140 | _dmx_read_state = DMX_STATE_IDLE; 141 | _rdm_task_mode = DMX_TASK_SEND; 142 | } else { 143 | startInput(); 144 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC | SERCOM_USART_INTENSET_ERROR; 145 | } 146 | _interrupt_mode = ISR_RDM_ENABLED; 147 | } 148 | 149 | void LXSAMD21DMX::stop ( void ) { 150 | SerialDMX.end(); 151 | _interrupt_mode = ISR_DISABLED; 152 | } 153 | 154 | void LXSAMD21DMX::setDirectionPin( uint8_t pin ) { 155 | _direction_pin = pin; 156 | pinMode(_direction_pin, OUTPUT); 157 | } 158 | 159 | void LXSAMD21DMX::setMaxSlots (int slots) { 160 | if ( slots > DMX_MIN_SLOTS ) { 161 | _slots = slots; 162 | } else { 163 | _slots = DMX_MIN_SLOTS; 164 | } 165 | } 166 | 167 | uint8_t LXSAMD21DMX::getSlot (int slot) { 168 | return _dmxData[slot]; 169 | } 170 | 171 | void LXSAMD21DMX::setSlot (int slot, uint8_t value) { 172 | _dmxData[slot] = value; 173 | } 174 | 175 | uint8_t* LXSAMD21DMX::dmxData(void) { 176 | return &_dmxData[0]; 177 | } 178 | 179 | uint8_t* LXSAMD21DMX::rdmData( void ) { 180 | return _rdmPacket; 181 | } 182 | 183 | uint8_t* LXSAMD21DMX::receivedData( void ) { 184 | return _receivedData; 185 | } 186 | 187 | uint8_t* LXSAMD21DMX::receivedRDMData( void ) { 188 | return _rdmData; 189 | } 190 | 191 | //************************************************************************************ 192 | 193 | void LXSAMD21DMX::transmissionComplete( void ) { 194 | if ( _dmx_send_state == DMX_STATE_BREAK ) { 195 | setBaudRate(DMX_BREAK_BAUD); 196 | _dmx_send_state = DMX_STATE_START; 197 | _next_send_slot = 0; 198 | DMX_SERCOM->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; 199 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC; 200 | DMX_SERCOM->USART.DATA.reg = 0; //break 201 | } else if ( _dmx_send_state == DMX_STATE_IDLE ) { //after data completely sent 202 | if ( _rdm_task_mode == DMX_TASK_SEND_RDM ) { 203 | DMX_SERCOM->USART.INTFLAG.bit.TXC = 1; // clear txc interrupt !!! 204 | DMX_SERCOM->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_TXC; // shut off interrupt 205 | _rdm_task_mode = DMX_TASK_RECEIVE; 206 | if ( _rdm_read_handled ) { 207 | _dmx_read_state = DMX_READ_STATE_START; 208 | _next_read_slot = 0; 209 | } else { 210 | _dmx_read_state = DMX_READ_STATE_IDLE; 211 | } 212 | digitalWrite(_direction_pin, LOW); 213 | } else { 214 | _dmx_send_state = DMX_STATE_BREAK; 215 | // txc interrupt not cleared so it will fire again... 216 | // if necessary, change mode 217 | if ( _rdm_task_mode == DMX_TASK_SET_SEND_RDM ) { 218 | _rdm_task_mode = DMX_TASK_SEND_RDM; 219 | } else if ( _rdm_task_mode == DMX_TASK_SET_SEND ) { 220 | _rdm_task_mode = DMX_TASK_SEND; 221 | } 222 | } 223 | } else if ( _dmx_send_state == DMX_STATE_START ) { 224 | setBaudRate(DMX_DATA_BAUD); 225 | _dmx_send_state = DMX_STATE_DATA; 226 | DMX_SERCOM->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_TXC; 227 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; 228 | //rdm task (?) 229 | if ( _rdm_task_mode == DMX_TASK_SEND_RDM ) { 230 | DMX_SERCOM->USART.DATA.reg = _rdmPacket[_next_send_slot++]; 231 | } else { 232 | DMX_SERCOM->USART.DATA.reg = _dmxData[_next_send_slot++]; 233 | } 234 | } 235 | } 236 | 237 | void LXSAMD21DMX::dataRegisterEmpty( void ) { 238 | if ( _dmx_send_state == DMX_STATE_DATA ) { 239 | if ( _rdm_task_mode == DMX_TASK_SEND_RDM ) { 240 | DMX_SERCOM->USART.DATA.reg = _rdmPacket[_next_send_slot++]; //send next slot; 241 | if ( _next_send_slot > _rdm_len ) { 242 | _dmx_send_state = DMX_STATE_IDLE; 243 | DMX_SERCOM->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; 244 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC; 245 | // switch to wait for last byte transmission to complete 246 | } 247 | } else { 248 | DMX_SERCOM->USART.DATA.reg = _dmxData[_next_send_slot++]; //send next slot; 249 | if ( _next_send_slot > _slots ) { 250 | _dmx_send_state = DMX_STATE_IDLE; 251 | DMX_SERCOM->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; 252 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC; 253 | // switch to wait for last byte transmission to complete 254 | } 255 | } 256 | 257 | } 258 | } 259 | 260 | //************************************************************************************ 261 | 262 | void LXSAMD21DMX::printReceivedData( void ) { 263 | for(int j=0; j<_next_read_slot; j++) { 264 | Serial.println(_receivedData[j]); 265 | } 266 | } 267 | 268 | void LXSAMD21DMX::packetComplete( void ) { 269 | if ( _receivedData[0] == 0 ) { //zero start code is DMX 270 | if ( _rdm_read_handled == 0 ) { // not handled by specific method 271 | if ( _next_read_slot > DMX_MIN_SLOTS ) { 272 | _slots = _next_read_slot - 1; //_next_read_slot represents next slot so subtract one 273 | for(int j=0; j<_next_read_slot; j++) { //copy dmx values from read buffer 274 | _dmxData[j] = _receivedData[j]; 275 | } 276 | 277 | if ( _receive_callback != NULL ) { 278 | _receive_callback(_slots); 279 | } 280 | } 281 | } 282 | } else { 283 | if ( _receivedData[0] == RDM_START_CODE ) { //zero start code is RDM 284 | if ( _rdm_read_handled == 0 ) { // not handled by specific method 285 | if ( validateRDMPacket(_receivedData) ) { // evaluate checksum 286 | uint8_t plen = _receivedData[2] + 2; 287 | for(int j=0; j 1 ) { // break before end of maximum frame 313 | if ( _receivedData[0] == 0 ) { // zero start code is DMX 314 | packetComplete(); // packet terminated with slots<512 315 | } 316 | } 317 | } 318 | _dmx_read_state = DMX_READ_STATE_START; //break causes spurious 0 byte on next interrupt, ignore... 319 | _next_read_slot = 0; 320 | _packet_length = DMX_MAX_FRAME; // default to receive complete frame 321 | } 322 | 323 | void LXSAMD21DMX::byteReceived(uint8_t c) { 324 | 325 | if ( _dmx_read_state == DMX_READ_STATE_RECEIVING ) { 326 | //digitalWrite(6, LOW);//<- debug pin 327 | _receivedData[_next_read_slot] = c; 328 | if ( _next_read_slot == 2 ) { //RDM length slot 329 | if ( _receivedData[0] == RDM_START_CODE ) { //RDM start code 330 | if ( _rdm_read_handled == 0 ) { 331 | _packet_length = c + 2; //add two bytes for checksum 332 | } 333 | } else if ( _receivedData[0] == 0xFE ) { //RDM Discovery Response 334 | _packet_length = DMX_MAX_FRAME; 335 | } else if ( _receivedData[0] != 0 ) { // if Not Null Start Code 336 | _dmx_read_state = DMX_STATE_IDLE; //unrecognized, ignore packet 337 | } 338 | } 339 | 340 | _next_read_slot++; 341 | if ( _next_read_slot >= _packet_length ) { //reached expected end of packet 342 | packetComplete(); 343 | } 344 | //digitalWrite(6, HIGH);//<- debug pin 345 | } else if ( _dmx_read_state == DMX_READ_STATE_START ) { 346 | _dmx_read_state = DMX_READ_STATE_RECEIVING; 347 | } 348 | } 349 | 350 | void LXSAMD21DMX::setDataReceivedCallback(LXRecvCallback callback) { 351 | _receive_callback = callback; 352 | } 353 | 354 | /************************************ RDM Methods **************************************/ 355 | 356 | void LXSAMD21DMX::setRDMReceivedCallback(LXRecvCallback callback) { 357 | _rdm_receive_callback = callback; 358 | } 359 | 360 | 361 | void LXSAMD21DMX::outputIRQHandler(void) { 362 | if ( DMX_SERCOM->USART.INTFLAG.bit.TXC ) { 363 | transmissionComplete(); 364 | } else if ( DMX_SERCOM->USART.INTFLAG.bit.DRE ) { 365 | dataRegisterEmpty(); 366 | } 367 | } 368 | 369 | void LXSAMD21DMX::inputIRQHandler(void) { 370 | 371 | if ( DMX_SERCOM->USART.INTFLAG.bit.ERROR ) { 372 | DMX_SERCOM->USART.INTFLAG.bit.ERROR = 1; //acknowledge error, clear interrupt 373 | 374 | if ( DMX_SERCOM->USART.STATUS.bit.FERR ) { //framing error happens when break is sent 375 | breakReceived(); 376 | return; 377 | } 378 | // other error flags? 379 | //return; 380 | } //ERR 381 | 382 | if ( DMX_SERCOM->USART.INTFLAG.bit.RXC ) { 383 | uint8_t incoming_byte = DMX_SERCOM->USART.DATA.reg; // read buffer to clear interrupt flag 384 | byteReceived(incoming_byte); 385 | } // RXC 386 | 387 | } // <-inputIRQHandler(void) 388 | 389 | void LXSAMD21DMX::rdmIRQHandler(void) { 390 | if ( _rdm_task_mode ) { 391 | if ( DMX_SERCOM->USART.INTFLAG.bit.TXC ) { 392 | transmissionComplete(); 393 | } else if ( DMX_SERCOM->USART.INTFLAG.bit.DRE ) { 394 | dataRegisterEmpty(); 395 | } 396 | } else { 397 | inputIRQHandler(); 398 | } 399 | } //--IrqHandler(void) 400 | 401 | /*********************************** RDM *****************************************/ 402 | 403 | uint8_t LXSAMD21DMX::rdmTaskMode( void ) { // applies to bidirectional RDM connection 404 | return _rdm_task_mode; 405 | } 406 | 407 | void LXSAMD21DMX::setTaskSendDMX( void ) { // only valid if connection started using startRDM() 408 | digitalWrite(_direction_pin, HIGH); 409 | _rdm_task_mode = DMX_TASK_SEND; 410 | } 411 | 412 | 413 | void LXSAMD21DMX::restoreTaskSendDMX( void ) { // only valid if connection started using startRDM() 414 | digitalWrite(_direction_pin, HIGH); 415 | _dmx_send_state = DMX_STATE_BREAK; 416 | _rdm_task_mode = DMX_TASK_SET_SEND; 417 | transmissionComplete(); // sends break and sets interrupt 418 | delay(1); 419 | while ( _rdm_task_mode != DMX_TASK_SEND ) { 420 | delay(1); 421 | } 422 | } 423 | 424 | void LXSAMD21DMX::setTaskReceive( void ) { // only valid if connection started using startRDM() 425 | _next_read_slot = 0; 426 | _packet_length = DMX_MAX_FRAME; 427 | _dmx_send_state = DMX_STATE_IDLE; 428 | _rdm_task_mode = DMX_TASK_RECEIVE; 429 | _rdm_read_handled = 0; 430 | 431 | 432 | digitalWrite(_direction_pin, LOW); 433 | } 434 | 435 | void LXSAMD21DMX::sendRawRDMPacket( uint8_t len ) { // only valid if connection started using startRDM() 436 | _rdm_len = len; 437 | // calculate checksum: len should include 2 bytes for checksum at the end 438 | uint16_t checksum = rdmChecksum(_rdmPacket, _rdm_len-2); 439 | _rdmPacket[_rdm_len-2] = checksum >> 8; 440 | _rdmPacket[_rdm_len-1] = checksum & 0xFF; 441 | 442 | if ( _rdm_task_mode ) { //already sending, flag to send RDM 443 | _rdm_task_mode = DMX_TASK_SET_SEND_RDM; 444 | } else { 445 | _rdm_task_mode = DMX_TASK_SEND_RDM; //listening 446 | digitalWrite(_direction_pin, HIGH); 447 | _dmx_send_state = DMX_STATE_BREAK; 448 | //set the interrupts 449 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC | SERCOM_USART_INTENSET_ERROR; 450 | // call transmissionComplete(); to send the break and begin RDM data packet sending 451 | transmissionComplete(); 452 | } 453 | 454 | while ( _rdm_task_mode ) { //wait for packet to be sent and listening to start 455 | delay(2); //_rdm_task_mode is set to 0 (receive) after RDM packet is completely sent 456 | } 457 | } 458 | 459 | void LXSAMD21DMX::setupRDMControllerPacket(uint8_t* pdata, uint8_t msglen, uint8_t port, uint16_t subdevice) { 460 | pdata[RDM_IDX_START_CODE] = RDM_START_CODE; 461 | pdata[RDM_IDX_SUB_START_CODE] = RDM_SUB_START_CODE; 462 | pdata[RDM_IDX_PACKET_SIZE] = msglen; 463 | 464 | // must set target UID outside this method 465 | UID::copyFromUID(THIS_DEVICE_ID, pdata, RDM_IDX_SOURCE_UID); 466 | 467 | pdata[RDM_IDX_TRANSACTION_NUM] = _transaction++; 468 | pdata[RDM_IDX_PORT] = port; 469 | pdata[RDM_IDX_MSG_COUNT] = 0x00; //(always zero for controller msgs) 470 | pdata[RDM_IDX_SUB_DEV_MSB] = subdevice >> 8; 471 | pdata[RDM_IDX_SUB_DEV_LSB] = subdevice & 0xFF; 472 | // total always 20 bytes 473 | } 474 | 475 | void LXSAMD21DMX::setupRDMMessageDataBlock(uint8_t* pdata, uint8_t cmdclass, uint16_t pid, uint8_t pdl) { 476 | pdata[RDM_IDX_CMD_CLASS] = cmdclass; 477 | pdata[RDM_IDX_PID_MSB] = (pid >> 8) & 0xFF; 478 | pdata[RDM_IDX_PID_LSB] = pid & 0xFF; 479 | pdata[RDM_IDX_PARAM_DATA_LEN] = pdl; 480 | // total always 4 bytes 481 | } 482 | 483 | uint8_t LXSAMD21DMX::sendRDMDiscoveryPacket(UID lower, UID upper, UID* single) { 484 | uint8_t rv = RDM_NO_DISCOVERY; 485 | uint8_t j; 486 | 487 | //Build RDM packet 488 | setupRDMControllerPacket(_rdmPacket, RDM_DISC_UNIQUE_BRANCH_MSGL, RDM_PORT_ONE, RDM_ROOT_DEVICE); 489 | UID::copyFromUID(BROADCAST_ALL_DEVICES_ID, _rdmPacket, 3); 490 | setupRDMMessageDataBlock(_rdmPacket, RDM_DISCOVERY_COMMAND, RDM_DISC_UNIQUE_BRANCH, RDM_DISC_UNIQUE_BRANCH_PDL); 491 | UID::copyFromUID(lower, _rdmPacket, 24); 492 | UID::copyFromUID(upper, _rdmPacket, 30); 493 | 494 | _rdm_read_handled = 1; 495 | sendRawRDMPacket(RDM_DISC_UNIQUE_BRANCH_PKTL); 496 | delay(2); 497 | 498 | // any bytes read indicate response to discovery packet 499 | // check if a single, complete, uncorrupted packet has been received 500 | // otherwise, refine discovery search 501 | 502 | if ( _next_read_slot ) { 503 | rv = RDM_PARTIAL_DISCOVERY; 504 | 505 | // find preamble separator 506 | for(j=0; j<8; j++) { 507 | if ( _receivedData[j] == RDM_DISC_PREAMBLE_SEPARATOR ) { 508 | break; 509 | } 510 | } 511 | // 0-7 bytes preamble 512 | if ( j < 8 ) { 513 | if ( _next_read_slot == j + 17 ) { //preamble separator plus 16 byte payload 514 | uint8_t bindex = j + 1; 515 | 516 | //calculate checksum of 12 slots representing UID 517 | uint16_t checksum = rdmChecksum(&_receivedData[bindex], 12); 518 | 519 | //convert dual bytes to payload of single bytes 520 | uint8_t payload[8]; 521 | for (j=0; j<8; j++) { 522 | payload[j] = _receivedData[bindex] & _receivedData[bindex+1]; 523 | bindex += 2; 524 | } 525 | 526 | if ( testRDMChecksum( checksum, payload, 6 ) ) { 527 | //copy UID into uldata 528 | rv = RDM_DID_DISCOVER; 529 | *single = payload; 530 | } 531 | } 532 | } // j<8 533 | 534 | _rdm_read_handled = 0; 535 | resetFrame(); 536 | } else { 537 | _rdm_read_handled = 0; 538 | } 539 | 540 | restoreTaskSendDMX(); 541 | return rv; 542 | } 543 | 544 | uint8_t LXSAMD21DMX::sendRDMDiscoveryMute(UID target, uint8_t cmd) { 545 | uint8_t rv = 0; 546 | 547 | //Build RDM packet 548 | // total packet length 0 parameter is 24 (+cksum =26 for sendRawRDMPacket) 549 | setupRDMControllerPacket(_rdmPacket, RDM_PKT_BASE_MSG_LEN, RDM_PORT_ONE, RDM_ROOT_DEVICE); 550 | UID::copyFromUID(target, _rdmPacket, 3); 551 | setupRDMMessageDataBlock(_rdmPacket, RDM_DISCOVERY_COMMAND, cmd, 0x00); 552 | 553 | _rdm_read_handled = 1; 554 | sendRawRDMPacket(RDM_PKT_BASE_TOTAL_LEN); 555 | delay(3); 556 | 557 | if ( _next_read_slot >= (RDM_PKT_BASE_TOTAL_LEN+2) ) { //expected pdl 2 or 8 558 | if ( validateRDMPacket(_receivedData) ) { 559 | if ( _receivedData[RDM_IDX_RESPONSE_TYPE] == RDM_RESPONSE_TYPE_ACK ) { 560 | if ( _receivedData[RDM_IDX_CMD_CLASS] == RDM_DISC_COMMAND_RESPONSE ) { 561 | if ( THIS_DEVICE_ID == UID(&_receivedData[RDM_IDX_DESTINATION_UID]) ) { 562 | rv = 1; 563 | } 564 | } 565 | } 566 | } 567 | 568 | _rdm_read_handled = 0; 569 | resetFrame(); 570 | } else { 571 | _rdm_read_handled = 0; 572 | } 573 | restoreTaskSendDMX(); 574 | return rv; 575 | } 576 | 577 | void LXSAMD21DMX::setupRDMDevicePacket(uint8_t* pdata, uint8_t msglen, uint8_t rtype, uint8_t msgs, uint16_t subdevice) { 578 | pdata[RDM_IDX_START_CODE] = RDM_START_CODE; 579 | pdata[RDM_IDX_SUB_START_CODE] = RDM_SUB_START_CODE; 580 | pdata[RDM_IDX_PACKET_SIZE] = msglen; 581 | 582 | // must set target UID outside this method 583 | UID::copyFromUID(THIS_DEVICE_ID, pdata, RDM_IDX_SOURCE_UID); 584 | 585 | pdata[RDM_IDX_TRANSACTION_NUM] = _transaction; //set this on read 586 | pdata[RDM_IDX_RESPONSE_TYPE] = rtype; 587 | pdata[RDM_IDX_MSG_COUNT] = msgs; 588 | pdata[RDM_IDX_SUB_DEV_MSB] = subdevice >> 8; 589 | pdata[RDM_IDX_SUB_DEV_LSB] = subdevice & 0xFF; 590 | // total always 20 bytes 591 | } 592 | 593 | uint8_t LXSAMD21DMX::sendRDMControllerPacket( void ) { 594 | uint8_t rv = 0; 595 | _rdm_read_handled = 1; 596 | sendRawRDMPacket(_rdmPacket[2]+2); 597 | delay(3); 598 | 599 | if ( _next_read_slot > 0 ) { 600 | if ( validateRDMPacket(_receivedData) ) { 601 | uint8_t plen = _receivedData[2] + 2; 602 | for(int rv=0; rv> 8; 740 | _rdmPacket[21] = bite | 0xAA; 741 | _rdmPacket[22] = bite | 0x55; 742 | bite = checksum & 0xFF; 743 | _rdmPacket[23] = bite | 0xAA; 744 | _rdmPacket[24] = bite | 0x55; 745 | 746 | // send (no break) 747 | _rdm_len = 25; 748 | digitalWrite(_direction_pin, HIGH); // could cut off receiving (?) 749 | delayMicroseconds(100); 750 | _next_send_slot = 1;//SKIP start code 751 | setBaudRate(DMX_DATA_BAUD); 752 | _dmx_send_state = DMX_STATE_DATA; 753 | 754 | _rdm_task_mode = DMX_TASK_SEND_RDM; 755 | //set the interrupt 756 | DMX_SERCOM->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; 757 | 758 | 759 | while ( _rdm_task_mode ) { //wait for packet to be sent and listening to start again 760 | delay(1); //_rdm_task_mode is set to 0 (receive) after RDM packet is completely sent 761 | } 762 | } 763 | 764 | -------------------------------------------------------------------------------- /src/LXSAMD21DMX.h: -------------------------------------------------------------------------------- 1 | /* LXSAMD21DMX.h 2 | Copyright 2016 by Claude Heintz Design 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of LXSAMD21DMX nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ----------------------------------------------------------------------------------- 30 | 31 | The LXSAMD21DMX library supports output and input of DMX using 32 | sercom4 of a MKR1000 microcontroller in UART mode. (pins 7 & 8) 33 | 34 | This is the circuit for a simple unisolated DMX Shield 35 | that could be used with LXSAMD21DMX. It uses a line driver IC 36 | to convert the output from the SAM D-21 to DMX: 37 | 38 | MKR1000 Pin 39 | | SN 75176 A or MAX 481CPA 40 | V _______________ 41 | | | 1 Vcc 8 |------(+5v) 42 | RX (5) |----------------------| | DMX Output 43 | | +----| 2 B 7 |---------------- Pin 2 44 | | | | | 45 | (3) |----------------------| 3 DE A 6 |---------------- Pin 3 46 | | | | 47 | TX (4) |----------------------| 4 DI Gnd 5 |---+------------ Pin 1 48 | | | 49 | | (GND) 50 | 51 | Data Enable (DE) and Inverted Read Enable (!RE) can be wired to +5v for output or Gnd for input 52 | if direction switching is not needed. 53 | */ 54 | 55 | #ifndef LXSAM21_DMX_H 56 | #define LXSAM21_DMX_H 57 | 58 | #include 59 | #include "SERCOM.h" 60 | #include 61 | 62 | #define DMX_MIN_SLOTS 24 63 | #define RDM_MAX_FRAME 257 64 | #define DMX_MAX_SLOTS 512 65 | #define DMX_MAX_FRAME 513 66 | 67 | #define DIRECTION_PIN_NOT_USED 255 68 | 69 | //***** baud rate defines 70 | #define DMX_DATA_BAUD 250000 71 | #define DMX_BREAK_BAUD 90000 72 | //99900 73 | 74 | //***** states indicate current position in DMX stream 75 | #define DMX_STATE_BREAK 0 76 | #define DMX_STATE_START 1 77 | #define DMX_STATE_DATA 2 78 | #define DMX_STATE_IDLE 3 79 | 80 | //***** status is if interrupts are enabled and IO is active 81 | #define ISR_DISABLED 0 82 | #define ISR_OUTPUT_ENABLED 1 83 | #define ISR_INPUT_ENABLED 2 84 | #define ISR_RDM_ENABLED 3 85 | 86 | //***** states indicate current position in DMX stream 87 | #define DMX_READ_STATE_IDLE 0 88 | #define DMX_READ_STATE_RECEIVING 1 89 | #define DMX_READ_STATE_START 2 90 | 91 | #define DMX_TASK_RECEIVE 0 92 | #define DMX_TASK_SEND 1 93 | #define DMX_TASK_SEND_RDM 2 94 | #define DMX_TASK_SET_SEND 3 95 | #define DMX_TASK_SET_SEND_RDM 4 96 | 97 | #define RDM_NO_DISCOVERY 0 98 | #define RDM_PARTIAL_DISCOVERY 1 99 | #define RDM_DID_DISCOVER 2 100 | 101 | #define RDM_DIRECTION_INPUT 0 102 | #define RDM_DIRECTION_OUTPUT 1 103 | 104 | typedef void (*LXRecvCallback)(int); 105 | 106 | /*! 107 | @class LXSAMD21DMX 108 | @abstract 109 | LXSAMD21DMX is a driver for sending or receiving DMX using one of a SAM D-21's five serial peripheral interfaces (SERCOMs). 110 | 111 | LXSAMD21DMX output mode continuously sends DMX once its interrupts have been enabled using startOutput(). 112 | Use setSlot() to set the level value for a particular DMX dimmer/address/channel. 113 | 114 | LXSAMD21DMX input mode continuously receives DMX once its interrupts have been enabled using startInput() 115 | Use getSlot() to read the level value for a particular DMX dimmer/address/channel. 116 | 117 | LXSAMD21DMX is used with a single instance called SAMD21DMX . 118 | */ 119 | 120 | class LXSAMD21DMX { 121 | 122 | public: 123 | 124 | LXSAMD21DMX ( void ); 125 | ~LXSAMD21DMX ( void ); 126 | 127 | /*! 128 | * @brief starts interrupt that continuously sends DMX output 129 | * @discussion Sets up baud rate, bits and parity, 130 | * sets globals accessed in ISR, 131 | * enables transmission (TE) and tx interrupts (TIE/TCIE). 132 | */ 133 | void startOutput( void ); 134 | 135 | /*! 136 | * @brief starts interrupt that continuously reads DMX data 137 | * @discussion sets up baud rate, bits and parity, 138 | * sets globals accessed in ISR, 139 | * enables receive (RE) and rx interrupt (RIE) 140 | */ 141 | void startInput( void ); 142 | 143 | /*! 144 | * @brief starts interrupt that continuously sends DMX output 145 | * @discussion direction pin is required, calls startOutput 146 | */ 147 | 148 | void startRDM( uint8_t pin, uint8_t direction=1); 149 | 150 | /*! 151 | * @brief disables tx, rx and interrupts. 152 | */ 153 | void stop( void ); 154 | 155 | /*! 156 | * @brief optional utility sets the pin used to control driver chip's 157 | * DE (data enable) line, HIGH for output, LOW for input. 158 | * @param pin to be automatically set for input/output direction 159 | */ 160 | void setDirectionPin( uint8_t pin ); 161 | 162 | /*! 163 | * @brief Sets the number of slots (aka addresses or channels) sent per DMX frame. 164 | * @discussion defaults to 512 or DMX_MAX_SLOTS and should be no less DMX_MIN_SLOTS slots. 165 | * The DMX standard specifies min break to break time no less than 1024 usecs. 166 | * At 44 usecs per slot ~= 24 167 | * @param slot the highest slot number (~24 to 512) 168 | */ 169 | void setMaxSlots (int slot); 170 | 171 | /*! 172 | * @brief reads the value of a slot/address/channel 173 | * @discussion NOTE: Data is not double buffered. 174 | * So a complete single frame is not guaranteed. 175 | * The ISR continuously reads the next frame into the buffer 176 | * @return level (0-255) 177 | */ 178 | uint8_t getSlot (int slot); 179 | 180 | /*! 181 | * @brief Sets the output value of a slot 182 | * @param slot number of the slot/address/channel (1-512) 183 | * @param value level (0-255) 184 | */ 185 | void setSlot (int slot, uint8_t value); 186 | 187 | /*! 188 | * @brief provides direct access to data array 189 | * @return pointer to dmx array 190 | */ 191 | uint8_t* dmxData(void); 192 | 193 | uint8_t* rdmData( void ); 194 | 195 | uint8_t* receivedData( void ); 196 | 197 | uint8_t* receivedRDMData( void ); 198 | 199 | /*! 200 | * @brief called when last data byte and break are completely sent 201 | */ 202 | void transmissionComplete( void ); 203 | 204 | /*! 205 | * @brief called when data register is empty and ready for the next byte 206 | */ 207 | void dataRegisterEmpty( void ); 208 | 209 | /*! 210 | * @brief utility for debugging prints received data 211 | */ 212 | void printReceivedData( void ); 213 | 214 | /*! 215 | * @brief called when a packet is finished being received either through start of next packet or size reached 216 | */ 217 | void packetComplete( void ); 218 | 219 | /*! 220 | * @brief sets read state to wait for break 221 | */ 222 | void resetFrame( void ); 223 | 224 | /*! 225 | * @brief called when a break is detected 226 | */ 227 | void breakReceived( void ); 228 | 229 | /*! 230 | * @brief called from isr when a byte is read from register 231 | */ 232 | void byteReceived(uint8_t c); 233 | 234 | /*! 235 | * @brief Function called when DMX frame has been read 236 | * @discussion Sets a pointer to a function that is called 237 | * on the break after a DMX frame has been received. 238 | * Whatever happens in this function should be quick! 239 | * Best used to set a flag that is polled outside of ISR for available data. 240 | */ 241 | void setDataReceivedCallback(LXRecvCallback callback); 242 | 243 | /************************************ RDM Methods ***********************************/ 244 | 245 | /*! 246 | * @brief Function called when RDM frame has been read 247 | * @discussion Sets a pointer to a function that is called 248 | * after an RDM frame has been received. 249 | * Whatever happens in this function should be quick! 250 | * Best used to set a flag that is polled outside of ISR for available data. 251 | */ 252 | void setRDMReceivedCallback(LXRecvCallback callback); 253 | 254 | /*! 255 | * @brief interrupt handler functions 256 | */ 257 | void outputIRQHandler(); 258 | void inputIRQHandler(); 259 | void rdmIRQHandler(); 260 | 261 | /*! 262 | * @brief indicate if dmx frame should be sent by bi-directional task loop 263 | * @discussion should only be called by task loop 264 | * @return 1 if dmx frame should be sent 265 | * return 2 if RDM should be sent 266 | * return 3 if RDM should be sent and set mode to 1 after first frame finished 267 | */ 268 | uint8_t rdmTaskMode( void ); 269 | 270 | 271 | /*! 272 | * @brief sets rdm task to send mode and the direction pin to HIGH 273 | */ 274 | void setTaskSendDMX( void ); 275 | 276 | /*! 277 | * @brief sets rdm task to send mode after task mode loops. 278 | * Sent after sending RDM message so DMX is resumed. 279 | * Blocks until task loop sets mode to send. 280 | */ 281 | void restoreTaskSendDMX( void ); 282 | 283 | /*! 284 | * @brief sets rdm task to receive mode 285 | * Prepares variables to receive starting with next break. 286 | * Sets the direction pin to LOW. 287 | */ 288 | void setTaskReceive( void ); 289 | 290 | /*! 291 | * @brief length of the rdm packet awaiting being sent 292 | */ 293 | uint8_t rdmPacketLength( void ); 294 | 295 | /*! 296 | * @brief sends packet using bytes from _rdmPacket ( rdmData() ) 297 | * @discussion sets rdm task mode to DMX_TASK_SEND_RDM which causes 298 | * _rdmPacket to be sent on next opportunity from task loop. 299 | * after _rdmPacket is sent, task mode switches to listen for response. 300 | * 301 | * set _rdm_read_handled flag prior to calling sendRawRDMPacket 302 | * _rdm_read_handled = 1 if reading is handled by calling function 303 | * _rdm_read_handled = 0 if desired to resume passive listening for next break 304 | */ 305 | void sendRawRDMPacket( uint8_t len ); 306 | 307 | /*! 308 | * @brief convenience method for setting fields in the top 20 bytes of an RDM message 309 | * that will be sent. 310 | * Destination UID needs to be set outside this method. 311 | * Source UID is set to constant THIS_DEVICE_ID below. 312 | */ 313 | void setupRDMControllerPacket(uint8_t* pdata, uint8_t msglen, uint8_t port, uint16_t subdevice); 314 | 315 | 316 | /*! 317 | * @brief convenience method for setting fields in the top 20 bytes of an RDM message 318 | * that will be sent. 319 | * Destination UID needs to be set outside this method. 320 | * Source UID is set to static member THIS_DEVICE_ID 321 | */ 322 | void setupRDMDevicePacket(uint8_t* pdata, uint8_t msglen, uint8_t rtype, uint8_t msgs, uint16_t subdevice); 323 | 324 | /*! 325 | * @brief convenience method for setting fields in the top bytes 20-23 of an RDM message 326 | * that will be sent. 327 | */ 328 | void setupRDMMessageDataBlock(uint8_t* pdata, uint8_t cmdclass, uint16_t pid, uint8_t pdl); 329 | 330 | /*! 331 | * @brief send discovery packet using upper and lower bounds 332 | * @discussion Assumes that regular DMX was sending when method is called and 333 | * so restores sending, waiting for a frame to be sent before returning. 334 | * @return 1 if discovered, 2 if valid packet (UID stored in uldata[12-17]) 335 | */ 336 | uint8_t sendRDMDiscoveryPacket(UID lower, UID upper, UID* single); 337 | 338 | /*! 339 | * @brief send discovery mute/un-mute packet to target UID 340 | * @discussion Assumes that regular DMX was sending when method is called and 341 | * so restores sending, waiting for a frame to be sent before returning. 342 | * @return 1 if ack response is received. 343 | */ 344 | uint8_t sendRDMDiscoveryMute(UID target, uint8_t cmd); 345 | 346 | /*! 347 | * @brief send previously built packet in _rdmPacket and validate response 348 | * @discussion Response to packet, if valid, is copied into _rdmData and 1 is returned 349 | * Otherwise, 0 is returned. 350 | */ 351 | uint8_t sendRDMControllerPacket( void ); 352 | 353 | /*! 354 | * @brief copies len of bytes into _rdmPacket and sends it 355 | * @discussion Response to packet, if valid, is copied into _rdmData and 1 is returned 356 | * Otherwise, 0 is returned. 357 | */ 358 | uint8_t sendRDMControllerPacket( uint8_t* bytes, uint8_t len ); 359 | 360 | /*! 361 | * @brief send RDM_GET_COMMAND packet 362 | * @discussion Assumes that regular DMX was sending when method is called and 363 | * so restores sending, waiting for a frame to be sent before returning. 364 | * @return 1 if ack is received. 365 | */ 366 | uint8_t sendRDMGetCommand(UID target, uint16_t pid, uint8_t* info, uint8_t len); 367 | 368 | /*! 369 | * @brief send RDM_SET_COMMAND packet 370 | * @discussion Assumes that regular DMX was sending when method is called and 371 | * so restores sending, waiting for a frame to be sent before returning. 372 | * @return 1 if ack is received. 373 | */ 374 | uint8_t sendRDMSetCommand(UID target, uint16_t pid, uint8_t* info, uint8_t len); 375 | 376 | /*! 377 | * @brief send RDM_GET_COMMAND_RESPONSE with RDM_RESPONSE_TYPE_ACK 378 | * @discussion sends data (info) of length (len) 379 | */ 380 | void sendRDMGetResponse(UID target, uint16_t pid, uint8_t* info, uint8_t len); 381 | 382 | /*! 383 | * @brief send RDM_SET_COMMAND_RESPONSE/RDM_DISC_COMMAND_RESPONSE with RDM_RESPONSE_TYPE_ACK 384 | * @discussion PDL is zero 385 | */ 386 | void sendAckRDMResponse(uint8_t cmdclass, UID target, uint16_t pid); 387 | 388 | 389 | void sendMuteAckRDMResponse(uint8_t cmdclass, UID target, uint16_t pid); 390 | 391 | /*! 392 | * @brief send response to RDM_DISC_UNIQUE_BRANCH packet 393 | */ 394 | void sendRDMDiscoverBranchResponse( void ); 395 | 396 | static UID THIS_DEVICE_ID; 397 | 398 | 399 | private: 400 | 401 | /*! 402 | * @brief pin used to control direction of output driver chip 403 | */ 404 | uint8_t _direction_pin; 405 | 406 | /*! 407 | * @brief represents phase of sending dmx packet data/break/etc used to change baud settings 408 | */ 409 | uint8_t _dmx_send_state; 410 | 411 | /*! 412 | * @brief represents phase of sending dmx packet data/break/etc used to change baud settings 413 | */ 414 | uint8_t _dmx_read_state; 415 | 416 | /*! 417 | * @brief flag indicating RDM task should send dmx slots 418 | */ 419 | uint8_t _rdm_task_mode; 420 | 421 | /*! 422 | * @brief flag indicating RDM task should send dmx slots 423 | */ 424 | uint8_t _rdm_read_handled; 425 | 426 | /*! 427 | * @brief transaction number 428 | */ 429 | uint8_t _transaction; 430 | 431 | /*! 432 | * @brief maximum expected length of packet 433 | */ 434 | uint16_t _packet_length; 435 | 436 | /*! 437 | * @brief slot index indicating position of byte to be sent 438 | */ 439 | uint16_t _next_send_slot; 440 | 441 | /*! 442 | * @brief slot index indicating position of last byte received 443 | */ 444 | uint16_t _next_read_slot; 445 | 446 | /*! 447 | * @brief number of dmx slots ~24 to 512 448 | */ 449 | uint16_t _slots; 450 | 451 | /*! 452 | * @brief outgoing rdm packet length 453 | */ 454 | uint16_t _rdm_len; 455 | 456 | /*! 457 | * @brief Array of dmx data including start code 458 | */ 459 | uint8_t _dmxData[DMX_MAX_FRAME]; 460 | 461 | /*! 462 | * @brief Array of received bytes first byte is start code 463 | */ 464 | uint8_t _receivedData[DMX_MAX_FRAME]; 465 | 466 | /*! 467 | * @brief Array representing an rdm packet to be sent 468 | */ 469 | uint8_t _rdmPacket[RDM_MAX_FRAME]; 470 | 471 | /*! 472 | * @brief Array representing a received rdm packet 473 | */ 474 | uint8_t _rdmData[RDM_MAX_FRAME]; 475 | 476 | /*! 477 | * @brief Pointer to receive callback function 478 | */ 479 | LXRecvCallback _receive_callback; 480 | 481 | /*! 482 | * @brief Pointer to receive callback function 483 | */ 484 | LXRecvCallback _rdm_receive_callback; 485 | 486 | }; 487 | 488 | extern LXSAMD21DMX SAMD21DMX; 489 | 490 | /******************* CONFIGURING THIS LIBRARY FOR OTHER PINS ************** 491 | * 492 | * Alternate SERCOM and Pins 493 | * 494 | * The SAMD21 uses a combination of SERCOM (serial communication) hardware modules 495 | * and pin MUX (a register that maps internal hardware to external pins) settings 496 | * for various communication interfaces. There's an excellent article by Adafruit 497 | * explaining how this works: 498 | * https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/muxing-it-up 499 | * 500 | * This library uses a macro, use_optional_sercom_macros, to make the changes necessary 501 | * to choose which of the SAMD21's SERCOM modules and pins to use. 502 | * 503 | * This table shows the possible configurations 504 | * 505 | * use_optional_sercom_macros TX Pin RX Pin SERCOM 506 | * -------------------------------------------------- 507 | * undefined | 4 | 5 | SERCOM4 508 | * 1 | 4 | 3 | SERCOM2 509 | * 2 | 10 | 11 | SERCOM1 510 | * 3 | 1 | 0 | SERCOM2 511 | * 4 | 13 | 14 | SERCOM5 512 | * 5 | 4 | 3 | SERCOM2 513 | * 514 | * Uncomment line 500 and define use_optional_sercom_macros 515 | * to use alternate SERCOM and pins. 516 | * 517 | * If compiling for Adafruit Feather MO, option 3 is automatically selected. 518 | * If compiling for Arduino Zero, option 1 is automatically selected 519 | * 520 | *****************************************************************/ 521 | 522 | #if defined ( ADAFRUIT_FEATHER_M0 ) 523 | #define use_optional_sercom_macros 2 524 | #elif defined(SEEED_XIAO_M0) 525 | #define use_optional_sercom_macros 5 526 | #elif defined ( ARDUINO_SAMD_ZERO ) 527 | #define use_optional_sercom_macros 1 528 | #elif defined ( ARDUINO_SAMD_MKRWIFI1010 ) 529 | #define use_optional_sercom_macros 1 530 | #endif 531 | 532 | //#define use_optional_sercom_macros 1 533 | 534 | #if defined( use_optional_sercom_macros ) 535 | 536 | #if ( use_optional_sercom_macros == 1 ) 537 | //********************** optional sercom macros 1 ********************** 538 | // --might be used for Arduino Zero sercom2 pins 3 and 4 539 | 540 | #define PIN_DMX_RX (3ul) 541 | #define PIN_DMX_TX (4ul) 542 | #define PAD_DMX_RX SERCOM_RX_PAD_1 543 | #define PAD_DMX_TX UART_TX_PAD_0 544 | 545 | // Set to PIO_SERCOM or PIO_SERCOM_ALT 546 | #define MUX_DMX_RX PIO_SERCOM_ALT 547 | #define MUX_DMX_TX PIO_SERCOM_ALT 548 | 549 | // SERCOMn is pointer to memory address where SERCOM registers are located. 550 | #define DMX_SERCOM SERCOM2 551 | 552 | // sercomN is C++ wrapper for SERCOMn (passed to UART constructor) 553 | #define DMX_sercom sercom2 554 | 555 | // sercom handler function 556 | #define DMX_SERCOM_HANDLER_FUNC SERCOM2_Handler 557 | 558 | #warning Using use_optional_sercom_macros = 1 559 | 560 | #elif ( use_optional_sercom_macros == 2 ) 561 | 562 | //********************** optional sercom macros 2 ********************** 563 | // --might be used for Adafruit M0 Feather sercom1 pins 10 and 11 564 | 565 | #define PIN_DMX_RX (11ul) 566 | #define PIN_DMX_TX (10ul) 567 | #define PAD_DMX_RX SERCOM_RX_PAD_0 568 | #define PAD_DMX_TX UART_TX_PAD_2 569 | 570 | // Set to PIO_SERCOM or PIO_SERCOM_ALT 571 | #define MUX_DMX_RX PIO_SERCOM 572 | #define MUX_DMX_TX PIO_SERCOM 573 | 574 | // SERCOMn is pointer to memory address where SERCOM registers are located. 575 | #define DMX_SERCOM SERCOM1 576 | 577 | // sercomN is C++ wrapper for SERCOMn (passed to UART constructor) 578 | #define DMX_sercom sercom1 579 | 580 | // sercom handler function 581 | #define DMX_SERCOM_HANDLER_FUNC SERCOM1_Handler 582 | 583 | #warning Using use_optional_sercom_macros = 2 584 | 585 | #elif ( use_optional_sercom_macros == 3 ) 586 | 587 | //********************** optional sercom macros 3 ********************** 588 | // --might be used for Feather M0 pins D0 and D1 589 | // --added by shiftingtech 7-21-19 590 | // --mostly to enable support for https://www.sparkfun.com/products/15110 591 | 592 | #define PIN_DMX_RX (0ul) 593 | #define PIN_DMX_TX (1ul) 594 | #define PAD_DMX_RX SERCOM_RX_PAD_3 595 | #define PAD_DMX_TX UART_TX_PAD_2 596 | 597 | // Set to PIO_SERCOM or PIO_SERCOM_ALT 598 | #define MUX_DMX_RX PIO_SERCOM_ALT 599 | #define MUX_DMX_TX PIO_SERCOM_ALT 600 | 601 | // SERCOMn is pointer to memory address where SERCOM registers are located. 602 | #define DMX_SERCOM SERCOM2 603 | 604 | // sercomN is C++ wrapper for SERCOMn (passed to UART constructor) 605 | #define DMX_sercom sercom2 606 | 607 | // sercom handler function 608 | #define DMX_SERCOM_HANDLER_FUNC SERCOM2_Handler 609 | 610 | #warning Using use_optional_sercom_macros = 3 611 | 612 | #elif ( use_optional_sercom_macros == 4 ) 613 | 614 | //********************** optional sercom macros 4 ********************** 615 | // --replaces Serial1 on MKR1000 616 | // --requires commenting out lines 178 to 186 in 617 | // Arduino15/packages/arduino/hardware/samd/1.8.4/variants/mkr1000/variant.cpp 618 | // -or- 619 | // ~/.platformio/packages/framework-arduino-samd/variants/mkr1000/variant.cpp 620 | // otherwise Serial1 will conflict on Sercom5 and this won't compile 621 | 622 | #define PIN_DMX_RX (13ul) 623 | #define PIN_DMX_TX (14ul) 624 | #define PAD_DMX_RX SERCOM_RX_PAD_3 625 | #define PAD_DMX_TX UART_TX_PAD_2 626 | 627 | // Set to PIO_SERCOM or PIO_SERCOM_ALT 628 | #define MUX_DMX_RX PIO_SERCOM_ALT 629 | #define MUX_DMX_TX PIO_SERCOM_ALT 630 | 631 | // SERCOMn is pointer to memory address where SERCOM registers are located. 632 | #define DMX_SERCOM SERCOM5 633 | 634 | // sercomN is C++ wrapper for SERCOMn (passed to UART constructor) 635 | #define DMX_sercom sercom5 636 | 637 | // sercom handler function 638 | #define DMX_SERCOM_HANDLER_FUNC SERCOM5_Handler 639 | 640 | #warning Using use_optional_sercom_macros = 4 641 | 642 | #elif (use_optional_sercom_macros == 5) 643 | //********************** optional sercom macros 5 ********************** 644 | // --might be used for SEEED XIAO M0 sercom2 pins 3 and 4 645 | 646 | #define PIN_DMX_RX (3ul) 647 | #define PIN_DMX_TX (4ul) 648 | #define PAD_DMX_RX SERCOM_RX_PAD_3 649 | #define PAD_DMX_TX UART_TX_PAD_0 650 | 651 | // Set to PIO_SERCOM or PIO_SERCOM_ALT 652 | #define MUX_DMX_RX PIO_SERCOM_ALT 653 | #define MUX_DMX_TX PIO_SERCOM_ALT 654 | 655 | // SERCOMn is pointer to memory address where SERCOM registers are located. 656 | #define DMX_SERCOM SERCOM2 657 | 658 | // sercomN is C++ wrapper for SERCOMn (passed to UART constructor) 659 | #define DMX_sercom sercom2 660 | 661 | // sercom handler function 662 | #define DMX_SERCOM_HANDLER_FUNC SERCOM2_Handler 663 | 664 | #warning Using use_optional_sercom_macros = 5 665 | 666 | 667 | 668 | #endif 669 | //---- end of #elif (use_optional_sercom_macros == )... 670 | 671 | #else 672 | //---- else == use_optional_sercom_macros is not defined ) 673 | 674 | //********************** default sercom macros ********************** 675 | 676 | #define PIN_DMX_RX (5ul) 677 | #define PIN_DMX_TX (4ul) 678 | #define PAD_DMX_RX SERCOM_RX_PAD_3 679 | #define PAD_DMX_TX UART_TX_PAD_2 680 | 681 | // Set to PIO_SERCOM or PIO_SERCOM_ALT 682 | #define MUX_DMX_RX PIO_SERCOM_ALT 683 | #define MUX_DMX_TX PIO_SERCOM_ALT 684 | 685 | // SERCOMn is pointer to memory address where SERCOM registers are located. 686 | #define DMX_SERCOM SERCOM4 687 | 688 | // sercomN is C++ wrapper for SERCOMn (passed to UART constructor) 689 | #define DMX_sercom sercom4 690 | 691 | // sercom handler function 692 | #define DMX_SERCOM_HANDLER_FUNC SERCOM4_Handler 693 | 694 | #endif 695 | 696 | #endif // ifndef LXSAM21_DMX_H 697 | -------------------------------------------------------------------------------- /src/rdm/TOD.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file TOD.cpp 4 | @author Claude Heintz 5 | @license BSD (see LXESP32DMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | RDM support for DMX Driver for ESP32 9 | 10 | @section HISTORY 11 | 12 | v1.0 - First release 13 | */ 14 | /**************************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | TOD::TOD( void ) { 21 | reset(); 22 | } 23 | 24 | 25 | uint8_t TOD::addUID(UID uid) { 26 | if ( next < (STORAGE_SIZE-6) ) { 27 | UID::copyFromUID(uid, storage, next); 28 | next += 6; 29 | return 1; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | uint8_t TOD::add(UID uid) { 36 | if ( ! contains(uid) ) { 37 | return addUID(uid); 38 | } 39 | return 0; 40 | } 41 | 42 | void TOD::removeUIDAt(int index) { 43 | if ( index < next-5 ) { 44 | next -= 6; 45 | int len = next - index; 46 | if ( len > 0 ) { 47 | memcpy(&storage[index], &storage[index+6], len); 48 | } 49 | } 50 | } 51 | 52 | uint8_t TOD::getUIDAt(int index, UID* uid) { 53 | if ( index < next-5 ) { 54 | *uid = &storage[index]; 55 | return 1; 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | int TOD::getNextUID(int index, UID* uid) { 62 | if ( index < next-5 ) { 63 | *uid = &storage[index]; 64 | return index + 6; 65 | } 66 | 67 | return -1; 68 | } 69 | 70 | void TOD::push (UID uid) { 71 | addUID(uid); 72 | } 73 | 74 | uint8_t TOD::pop (UID* uid) { 75 | if ( next > 5 ) { 76 | uint16_t n = next - 6; 77 | if ( getUIDAt(n, uid) ) { 78 | next = n; 79 | return 1; 80 | } 81 | } 82 | return 0; 83 | } 84 | 85 | uint8_t TOD::contains( UID uid ) { 86 | int index = 0; 87 | UID u(0,0,0,0,0,0); 88 | while ( index >= 0 ) { 89 | index = getNextUID(index, &u); 90 | if ( index > 0 ) { 91 | if ( uid == u ) { 92 | return 1; 93 | } 94 | } 95 | } 96 | return 0; 97 | } 98 | 99 | uint8_t TOD::count( void ) { 100 | return next / 6; 101 | } 102 | 103 | void TOD::reset( void ) { 104 | memset(storage, 0, 1200); 105 | next = 0; 106 | } 107 | 108 | uint8_t* TOD::rawBytes( void ) { 109 | return storage; 110 | } 111 | 112 | void TOD::printTOD( void ) { 113 | Serial.print("TOD-"); 114 | Serial.println(next/6); 115 | int index = 0; 116 | UID u(0,0,0,0,0,0); 117 | while ( index >= 0 ) { 118 | index = getNextUID(index, &u); 119 | if ( index > 0 ) { 120 | Serial.println(u); 121 | } 122 | } 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/rdm/TOD.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file TOD.h 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | RDM support for DMX Driver for ESP32 9 | 10 | Implements RDM Table of Devices as array of 6 byte (48bit) UIDs 11 | capable of storing 200 UIDs in static array 12 | 13 | @section HISTORY 14 | 15 | v1.0 - First release 16 | */ 17 | /**************************************************************************/ 18 | 19 | #ifndef RDMTOD_h 20 | #define RDMTOD_h 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | #define STORAGE_SIZE 1200 28 | 29 | /*! 30 | @class TOD 31 | @abstract 32 | Implements an RDM table of devices. Contains a list of up to 200 UIDs. 33 | */ 34 | 35 | class TOD { 36 | private: 37 | uint8_t storage[STORAGE_SIZE]; 38 | uint16_t next; 39 | 40 | public: 41 | TOD( void ); 42 | 43 | /*! 44 | * @brief add UID to array 45 | * @discussion copies 6 bytes into the next six indexes in the storage array 46 | * the next index is increased by 6 47 | * @returns 0 if no more room in storage, otherwise 1 48 | */ 49 | uint8_t addUID(UID uid); 50 | /*! 51 | * @brief Add UID to array if storage does not contain this UID. 52 | * @discussion checks storage for match to uid 53 | * if not found, calls addUID 54 | * returns 0 if no more room 55 | */ 56 | uint8_t add(UID uid); 57 | /*! 58 | * @brief Removes 6 bytes from storage starting at index. 59 | */ 60 | void removeUIDAt(int index); 61 | 62 | /*! 63 | * @brief Sets the bytes of the UID, copying from storage starting at at index. 64 | */ 65 | uint8_t getUIDAt(int index, UID* uid); 66 | int getNextUID(int index, UID* uid); 67 | 68 | /*! 69 | * @brief calls addUID, does nothing if not enough room 70 | */ 71 | void push (UID uid); 72 | /*! 73 | * @brief pops the last 6 bytes from storage 74 | * @discussion Sets the bytes of the UID, using last 6 bytes of storage 75 | * and decreases next index by 6 76 | * @returns 0 if no more remaining, otherwise 1 77 | */ 78 | uint8_t pop (UID* uid); 79 | 80 | /*! 81 | * @brief searches storage for match to UID 82 | * @returns 1 if found, otherwise 0 83 | */ 84 | uint8_t contains( UID uid ); 85 | 86 | /*! 87 | * @brief Number of UIDs in table 88 | * @returns number of 6 byte UIDs currently in storage 89 | */ 90 | uint8_t count( void ); 91 | 92 | /*! 93 | * @brief Zeros out the table 94 | */ 95 | void reset( void ); 96 | 97 | /*! 98 | * @brief pointer to the raw storage bytes 99 | */ 100 | uint8_t* rawBytes( void ); 101 | 102 | /*! 103 | * @brief utility to print the table using Serial 104 | */ 105 | void printTOD( void ); 106 | }; 107 | 108 | 109 | #endif //RDMTOD 110 | -------------------------------------------------------------------------------- /src/rdm/UID.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file UID.cpp 4 | @author Claude Heintz 5 | @license BSD (see LXESP32DMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | RDM support for DMX Driver for ESP32 9 | 10 | @section HISTORY 11 | 12 | v1.0 - First release 13 | */ 14 | /**************************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | UID::UID( void ) { 21 | setBytes((uint64_t)0); 22 | } 23 | 24 | UID::UID( uint64_t u ) { 25 | setBytes(u); 26 | } 27 | 28 | UID::UID(uint8_t m1, uint8_t m2, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4) { 29 | setBytes(m1, m2, d1, d2, d3, d4); 30 | } 31 | 32 | UID::UID(const uint8_t *address) { 33 | memcpy(bytes, address, sizeof(bytes)); 34 | } 35 | 36 | UID& UID::operator=(const uint8_t *address) { 37 | memcpy(bytes, address, sizeof(bytes)); 38 | return *this; 39 | } 40 | 41 | UID& UID::operator=(UID address) { 42 | memcpy(bytes, address.bytes, sizeof(bytes)); 43 | return *this; 44 | } 45 | 46 | bool UID::operator==(const UID& addr) const { 47 | return memcmp(addr.bytes, bytes, sizeof(bytes)) == 0; 48 | } 49 | 50 | bool UID::operator==(const uint8_t* addr) const { 51 | return memcmp(addr, bytes, sizeof(bytes)) == 0; 52 | } 53 | 54 | uint8_t UID::becomeMidpoint(UID a, UID b) { 55 | uint64_t a64 = uid_bytes2long(a.rawbytes()); 56 | uint64_t b64 = uid_bytes2long(b.rawbytes()); 57 | if ( a64 > b64 ) { 58 | if ( (a64 - b64) < 2 ) { 59 | return 0; 60 | } 61 | } else if ( (b64 - a64) < 2 ) { 62 | return 0; 63 | } 64 | a64 += b64; 65 | b64 = a64 >> 1; 66 | uid_long2Bytes(b64, bytes); 67 | return 1; 68 | } 69 | 70 | uint8_t* UID::rawbytes( void ) { 71 | return bytes; 72 | } 73 | 74 | void UID::setBytes(uint64_t u) { 75 | uid_long2Bytes(u, bytes); 76 | } 77 | 78 | void UID::setBytes(UID u) { 79 | memcpy(bytes, u.bytes, sizeof(bytes)); 80 | } 81 | 82 | void UID::setBytes(uint8_t m1, uint8_t m2, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4) { 83 | bytes[0] = m1; 84 | bytes[1] = m2; 85 | bytes[2] = d1; 86 | bytes[3] = d2; 87 | bytes[4] = d3; 88 | bytes[5] = d4; 89 | } 90 | 91 | /* 92 | void UID::setBytes(uint8_t* u) { 93 | memcpy(bytes, u, sizeof(bytes)); 94 | }*/ 95 | 96 | uint64_t UID::getValue ( void ) { 97 | return uid_bytes2long(bytes); 98 | } 99 | 100 | size_t UID::printTo(Print& p) const { 101 | size_t n = 0; 102 | n += p.print(bytes[0], HEX); 103 | n += p.print(bytes[1], HEX); 104 | n += p.print(':'); 105 | n += p.print(bytes[2], HEX); 106 | n += p.print(bytes[3], HEX); 107 | n += p.print(bytes[4], HEX); 108 | n += p.print(bytes[5], HEX); 109 | return n; 110 | } 111 | 112 | String UID::toString() const { 113 | char szRet[20]; 114 | sprintf(szRet,"%u%u:%u%u%u%u", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]); 115 | return String(szRet); 116 | } 117 | 118 | void print64Bit(uint64_t n) { 119 | uint8_t b[6]; 120 | b[0] = ((uint64_t)(n >> 40)) & 0xFF; 121 | b[1] = ((uint64_t)(n >> 32)) & 0xFF; 122 | b[2] = ((uint64_t)(n >> 24)) & 0xFF; 123 | b[3] = ((uint64_t)(n >> 16)) & 0xFF; 124 | b[4] = ((uint64_t)(n >> 8)) & 0xFF; 125 | b[5] = n & 0xFF; 126 | Serial.print("0x "); 127 | Serial.print(b[0], HEX); 128 | Serial.print(" "); 129 | Serial.print(b[1], HEX); 130 | Serial.print(" "); 131 | Serial.print(b[2], HEX); 132 | Serial.print(" "); 133 | Serial.print(b[3], HEX); 134 | Serial.print(" "); 135 | Serial.print(b[4], HEX); 136 | Serial.print(" "); 137 | Serial.println(b[5], HEX); 138 | } 139 | 140 | uint64_t uid_bytes2long(uint8_t* b) { 141 | return (((uint64_t)b[0]) <<40)| (((uint64_t)b[1])<<32) | (((uint64_t)b[2])<<24) | (((uint64_t)b[3])<<16) | (((uint64_t)b[4])<<8) | b[5]; 142 | } 143 | 144 | uint64_t uid_bytes2long(const uint8_t* b) { 145 | return (((uint64_t)b[0]) <<40)| (((uint64_t)b[1])<<32) | (((uint64_t)b[2])<<24) | (((uint64_t)b[3])<<16) | (((uint64_t)b[4])<<8) | b[5]; 146 | } 147 | 148 | void uid_long2Bytes(uint64_t u, uint8_t* bytes) { 149 | bytes[0] = ((uint64_t)(u >> 40)) & 0xFF; 150 | bytes[1] = ((uint64_t)(u >> 32)) & 0xFF; 151 | bytes[2] = ((uint64_t)(u >> 24)) & 0xFF; 152 | bytes[3] = ((uint64_t)(u >> 16)) & 0xFF; 153 | bytes[4] = ((uint64_t)(u >> 8)) & 0xFF; 154 | bytes[5] = u & 0xFF; 155 | } 156 | -------------------------------------------------------------------------------- /src/rdm/UID.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file UID.cpp 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | RDM support for DMX Driver for ESP32 9 | 10 | Implements class encapsulating 48bit RDM unique device identifier 11 | MM:DDDD 12 | Where MM is ESTA manufacturer code. 13 | 14 | @section HISTORY 15 | 16 | v1.0 - First release 17 | */ 18 | /**************************************************************************/ 19 | 20 | #ifndef RDMUID_h 21 | #define RDMUID_h 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | //for Arduino M0 1.8.13 Printable.h and WString.h are deprecated, use Arduino.h 28 | #include 29 | 30 | // for Arduino SAMD 32bit Cortex M0 boards package prior to 1.8.10 use 31 | //#include 32 | //#include 33 | 34 | 35 | 36 | // utility functions 37 | void print64Bit(uint64_t n); 38 | uint64_t uid_bytes2long(uint8_t* b); 39 | uint64_t uid_bytes2long(const uint8_t* b); 40 | void uid_long2Bytes(uint64_t u, uint8_t* bytes); 41 | 42 | 43 | /*! 44 | @class UID 45 | @abstract 46 | A class to make it easier to handle RDM UIDs. 47 | (Based on Arduino IPAddress class using 6 bytes instead of 4) 48 | */ 49 | 50 | class UID: public Printable 51 | { 52 | private: 53 | uint8_t bytes[6]; 54 | 55 | public: 56 | /*! 57 | * @brief default constructor 58 | * @discussion 59 | */ 60 | UID( void ); 61 | 62 | /*! 63 | * @brief construct UID from 64bit integer 64 | * @discussion 65 | */ 66 | UID( uint64_t u ); 67 | /*! 68 | * @brief construct UID from 6 individual bytes 69 | */ 70 | UID(uint8_t m1, uint8_t m2, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4); 71 | /*! 72 | * @brief construct UID from pointer to 6 byte array 73 | */ 74 | UID(const uint8_t *address); 75 | 76 | // Overloaded equality operator 77 | bool operator==(const UID& addr) const; 78 | bool operator==(const uint8_t* addr) const; 79 | 80 | // Overloaded index operator to allow getting and setting individual bytes 81 | uint8_t operator[](int index) const { 82 | return bytes[index]; 83 | } 84 | uint8_t& operator[](int index) { 85 | return bytes[index]; 86 | } 87 | 88 | // Overloaded copy operators to allow initialisation of UID objects 89 | UID& operator=(const uint8_t *address); 90 | UID& operator=(UID address); 91 | 92 | /*! 93 | * @brief copy bytes of a UID into an array starting at index 94 | */ 95 | static void copyFromUID(UID id, uint8_t *address, uint16_t index=0) { 96 | memcpy(&address[index], id.bytes, sizeof(id.bytes)); 97 | } 98 | 99 | /*! 100 | * @brief copy bytes into a UID from an array starting at index 101 | */ 102 | static void copyToUID(UID id, uint8_t *address, uint16_t index=0) { 103 | memcpy(id.bytes, &address[index], sizeof(id.bytes)); 104 | } 105 | 106 | /*! 107 | * @brief set the bytes of this UID to the midpoint of 2 UIDs 108 | */ 109 | uint8_t becomeMidpoint(UID a, UID b); 110 | 111 | /*! 112 | * @brief pointer to the 6 byte array 113 | */ 114 | uint8_t* rawbytes( void ); 115 | 116 | /*! 117 | * @brief set the bytes of this UID with a 64bit integer 118 | */ 119 | void setBytes(uint64_t u); 120 | 121 | /*! 122 | * @brief set the bytes of this UID with another UID (copy) 123 | */ 124 | void setBytes(UID u); 125 | 126 | /*! 127 | * @brief set the bytes of this UID with individual mfg code and device ID bytes 128 | */ 129 | void setBytes(uint8_t m1, uint8_t m2, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4); 130 | 131 | //void setBytes(uint8_t* u); 132 | 133 | /*! 134 | * @brief UID as 64 bit integer 135 | */ 136 | uint64_t getValue ( void ); 137 | 138 | /*! 139 | * @brief print with formatting 140 | */ 141 | virtual size_t printTo(Print& p) const; 142 | 143 | /*! 144 | * @brief convert to formatted string 145 | */ 146 | String toString() const; 147 | }; 148 | 149 | /*! 150 | * @brief ALL_DEVICES wildcard UID 151 | */ 152 | const UID BROADCAST_ALL_DEVICES_ID(0xFFFFFFFFFFFF); 153 | 154 | #endif //RDMUID 155 | -------------------------------------------------------------------------------- /src/rdm/rdm_utility.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file rdm_utility.c 4 | @author Claude Heintz 5 | @license BSD (see LXESP32DMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | RDM support for DMX Driver for ESP32 9 | 10 | @section HISTORY 11 | 12 | v1.0 - First release 13 | */ 14 | /**************************************************************************/ 15 | #include 16 | 17 | uint16_t rdmChecksum(uint8_t* bytes, uint8_t len) { 18 | uint16_t rv = 0; 19 | for(int j=0; j> 8) == data[index] ) && ( (cksum &0xFF) == data[index+1] )); 27 | } 28 | 29 | 30 | uint8_t validateRDMPacket(uint8_t* pdata) { 31 | if ( pdata[0] != RDM_START_CODE ) { 32 | return 0; 33 | } 34 | uint16_t checksum = rdmChecksum(pdata, pdata[2]); 35 | return testRDMChecksum(checksum, pdata, pdata[2]); 36 | } -------------------------------------------------------------------------------- /src/rdm/rdm_utility.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file rdm_utility.h 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | RDM support for DMX Driver for ESP32 9 | 10 | defines constants and utility functions 11 | 12 | @section HISTORY 13 | 14 | v1.0 - First release 15 | */ 16 | /**************************************************************************/ 17 | /* 18 | 19 | RDM Packet format 20 | 21 | 0 Start Code 22 | 1 Sub Start Code 23 | 2 Message Length 24 | 3 Destination UID 25 | 4 26 | 5 27 | 6 28 | 7 29 | 8 30 | 9 Source UID 31 | 10 32 | 11 33 | 12 34 | 13 35 | 14 36 | 15 Transaction Number 37 | 16 Port ID/Response Type 38 | 17 Message Count 39 | 18 SubDevice MSB 40 | 19 SubDevice LSB 41 | 20 Command Class 42 | 21 Parameter id (PID) MSB 43 | 22 Parameter id (PID) LSB 44 | 23 Paramater Data Length 45 | 24 Variable length Data 46 | 47 | 48 | Message with zero parameter len is 24 bytes plus 2 byte checksum, 26 bytes total: 49 | 50 | byte[2] = 24 + byte[23] 51 | bytesSent = 26 + byte[23] 52 | 53 | */ 54 | 55 | #ifndef RDMutility_h 56 | #define RDMutility_h 57 | 58 | #ifdef __cplusplus 59 | extern "C" { 60 | #endif 61 | 62 | #include 63 | 64 | 65 | /***************************** defines *****************************/ 66 | 67 | 68 | // start codes 69 | #define RDM_START_CODE 0xCC 70 | #define RDM_SUB_START_CODE 0x01 71 | 72 | // command classes 73 | #define RDM_DISCOVERY_COMMAND 0x10 74 | #define RDM_DISC_COMMAND_RESPONSE 0x11 75 | #define RDM_GET_COMMAND 0x20 76 | #define RDM_GET_COMMAND_RESPONSE 0x21 77 | #define RDM_SET_COMMAND 0x30 78 | #define RDM_SET_COMMAND_RESPONSE 0x31 79 | 80 | 81 | // response types 82 | #define RDM_RESPONSE_TYPE_ACK 0x00 83 | 84 | // discovery-network management Parameter IDs (PID) 85 | #define RDM_DISC_UNIQUE_BRANCH 0x0001 86 | #define RDM_DISC_MUTE 0x0002 87 | #define RDM_DISC_UNMUTE 0x0003 88 | 89 | // product information PIDs 90 | #define RDM_DEVICE_INFO 0x0060 91 | #define RDM_DEVICE_START_ADDR 0x00F0 92 | #define RDM_DEVICE_MODEL_DESC 0x0080 93 | #define RDM_DEVICE_MFG_LABEL 0x0081 94 | #define RDM_DEVICE_DEV_LABEL 0x0082 95 | 96 | // control information PIDs 97 | #define RDM_IDENTIFY_DEVICE 0x1000 98 | 99 | // packet heading constants 100 | #define RDM_PORT_ONE 0x01 101 | #define RDM_ROOT_DEVICE 0x0000 102 | 103 | // RDM packet byte indexes 104 | #define RDM_IDX_START_CODE 0 105 | #define RDM_IDX_SUB_START_CODE 1 106 | #define RDM_IDX_PACKET_SIZE 2 107 | #define RDM_IDX_DESTINATION_UID 3 108 | #define RDM_IDX_SOURCE_UID 9 109 | #define RDM_IDX_TRANSACTION_NUM 15 110 | #define RDM_IDX_PORT 16 111 | #define RDM_IDX_RESPONSE_TYPE 16 112 | #define RDM_IDX_MSG_COUNT 17 113 | #define RDM_IDX_SUB_DEV_MSB 18 114 | #define RDM_IDX_SUB_DEV_LSB 19 115 | #define RDM_IDX_CMD_CLASS 20 116 | #define RDM_IDX_PID_MSB 21 117 | #define RDM_IDX_PID_LSB 22 118 | #define RDM_IDX_PARAM_DATA_LEN 23 119 | 120 | 121 | // packet sizes with and without checksum, an additional 2 bytes 122 | #define RDM_PKT_BASE_MSG_LEN 24 123 | #define RDM_PKT_BASE_TOTAL_LEN 26 124 | 125 | #define RDM_DISC_UNIQUE_BRANCH_PKTL 38 126 | #define RDM_DISC_UNIQUE_BRANCH_MSGL 0x24 127 | #define RDM_DISC_UNIQUE_BRANCH_PDL 0x0C 128 | 129 | // discover unique branch reply 130 | #define RDM_DISC_PREAMBLE_SEPARATOR 0xAA 131 | 132 | 133 | 134 | 135 | /***************************** functions *****************************/ 136 | 137 | /* 138 | * rdmChecksum calculates mod 0x10000 sum of bytes 139 | * this is used in an RDM packet as follows 140 | * bytes[len] = rdmChecksum[MSB] 141 | * bytes[len+1] = rdmChecksum[LSB] 142 | * 143 | */ 144 | uint16_t rdmChecksum(uint8_t* bytes, uint8_t len); 145 | 146 | /* 147 | * testRDMChecksum evaluates cksum and returns true if both 148 | * bytes[index] == cksum[MSB] 149 | * bytes[index+1] == cksum[LSB] 150 | * 151 | */ 152 | uint8_t testRDMChecksum(uint16_t cksum, uint8_t* data, uint8_t index); 153 | 154 | /* 155 | * validateRDMPacket tests the RDM start code in pdata[0] 156 | * then it calls rdmChecksum using the packet length 157 | * which is the third byte of packet 158 | * checksum = rdmChecksum( pdata, pdata[2] ) 159 | * validateRDMPacket returns the result of testRDMChecksum( checksum, pdata, pdata[2] ) 160 | * 161 | */ 162 | uint8_t validateRDMPacket(uint8_t* pdata); 163 | 164 | #ifdef __cplusplus 165 | } 166 | #endif 167 | 168 | #endif //RDMutility_h --------------------------------------------------------------------------------