├── .gitignore ├── LICENSE ├── README.md ├── examples ├── DMXFadeTest │ └── DMXFadeTest.ino ├── DMXFadeTest2univ │ ├── DMXFadeTest2univ.ino │ ├── LXESP8266UART0DMX.cpp │ └── LXESP8266UART0DMX.h ├── DMXInputNeoPixel │ └── DMXInputNeoPixel.ino ├── DMXInputTest │ └── DMXInputTest.ino ├── RDMDeviceTest │ └── RDMDeviceTest.ino ├── feather shield ESP8266:ESP32 │ ├── FeatherDMXShieldDesign.pdf │ ├── Proto FeatherWing.brd │ └── Proto FeatherWing.sch └── rdmControllerTest │ └── rdmControllerTest.ino ├── extras └── doc │ ├── Classes │ └── LX8266DMX │ │ ├── index.html │ │ └── toc.html │ ├── index.html │ ├── rdm │ ├── TOD │ │ ├── Classes │ │ │ └── TOD │ │ │ │ ├── index.html │ │ │ │ └── toc.html │ │ ├── index.html │ │ └── toc.html │ └── UID │ │ ├── Classes │ │ └── UID │ │ │ ├── index.html │ │ │ └── toc.html │ │ ├── index.html │ │ └── toc.html │ └── toc.html ├── keywords.txt ├── library.properties └── src ├── LXESP8266UARTDMX.cpp ├── LXESP8266UARTDMX.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) 2015-2017, 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 LXESP8266DMX 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 | # LXESP8266DMX 2 | DMX Driver for ESP8266 using ArduinoIDE 3 | -------------------------------------------------------------------------------- /examples/DMXFadeTest/DMXFadeTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file DMXFadeTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX LICENSE) 6 | @copyright 2015 by Claude Heintz 7 | 8 | Simple Fade test of RESP8266 DMX Driver 9 | @section HISTORY 10 | 11 | v1.00 - First release 12 | v1.01 - Updated for single LX8266DMX class 13 | 14 | 15 | 16 | 17 | 18 | 19 | This is the circuit for a simple unisolated DMX Shield for input: 20 | 21 | Arduino SN 75176 A or MAX 481CPA 22 | pin 3k 1k _______________ 23 | | GND---/\/\/\-+-/\/\/\--| 1 Vcc 8 |------ +5v 24 | V | | | DMX Output 25 | RX |------------+ +----| 2 B 7 |---------------- Pin 2 26 | | | | | 27 | | +3.3v------_----| 3 DE A 6 |---------------- Pin 3 28 | | | | 29 | TX(2)|----------------------| 4 DI Gnd 5 |---+------------ Pin 1 30 | | _______________ | 31 | | 330 ohm GND 32 | 14 |-----------/\/\/\-----[ LED ]------------| 33 | 34 | 35 | !) Pins 2 & 3 of the MAX481 are held HIGH to enable output 36 | !) The 1k/3k resistors are a simple voltage divider to convert from 5v to 3.3v (for input only) 37 | 38 | !!) Note that ESP8266 has limited UARTs and this library will conflict with Serial. 39 | !!) Also, depending on the board, the above circuit may need to be disconnected 40 | in order to load a sketch. 41 | 42 | */ 43 | /**************************************************************************/ 44 | #include 45 | 46 | #define LED_PIN_OUTPUT 14 47 | #define DIRECTION_PIN 5 48 | 49 | uint8_t level = 0; 50 | int d = 1; 51 | 52 | void setup() { 53 | pinMode(LED_PIN_OUTPUT, OUTPUT); 54 | pinMode(DIRECTION_PIN, OUTPUT); 55 | 56 | Serial.setDebugOutput(UART0); //use uart0 for debugging 57 | 58 | ESP8266DMX.setDirectionPin(DIRECTION_PIN); 59 | ESP8266DMX.startOutput(); 60 | } 61 | 62 | /************************************************************************ 63 | 64 | The main loop fades the levels of addresses 7 and 8 to full 65 | 66 | *************************************************************************/ 67 | 68 | void loop() { 69 | ESP8266DMX.setSlot(8,level); 70 | ESP8266DMX.setSlot(1,level); 71 | analogWrite(LED_PIN_OUTPUT, level); 72 | delay(50); 73 | level+= d; 74 | if ( level == 0 ) { 75 | d = 1; 76 | } else if ( level == 100 ) { 77 | d = -1; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /examples/DMXFadeTest2univ/DMXFadeTest2univ.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file DMXFadeTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX LICENSE) 6 | @copyright 2017 by Claude Heintz 7 | 8 | Simple Fade test of ESP8266 DMX Driver and second UART output 9 | @section HISTORY 10 | 11 | v1.00 - First release 12 | 13 | */ 14 | /**************************************************************************/ 15 | #include 16 | #include "LXESP8266UART0DMX.h" 17 | #include 18 | 19 | uint8_t level = 0; 20 | 21 | void setup() { 22 | uart_set_debug(UART_NO); //disable debug print 23 | pinMode(14, OUTPUT); 24 | delay(1000); //avoid boot print?? 25 | 26 | ESP8266DMX0.startDualOutput(); 27 | } 28 | 29 | /************************************************************************ 30 | 31 | The main loop fades the levels of addresses 7 and 8 to full 32 | 33 | *************************************************************************/ 34 | 35 | void loop() { 36 | ESP8266DMX.setSlot(7,level); 37 | ESP8266DMX.setSlot(8,255); 38 | ESP8266DMX0.setSlot(5,level); 39 | ESP8266DMX0.setSlot(8,255); 40 | delay(50); 41 | level++; 42 | } 43 | -------------------------------------------------------------------------------- /examples/DMXFadeTest2univ/LXESP8266UART0DMX.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file LXESP8266UART0DMX.cpp 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266UARTDMX.h) 6 | @copyright 2017 by Claude Heintz 7 | 8 | DMX Driver for ESP8266 using UART0. 9 | 10 | @section HISTORY 11 | 12 | v1.0 - Alternative second universe output using UART0 13 | in addition to LXESP8266UARTDMX which uses 14 | TX - UART1 15 | RX - UART0 16 | 17 | Warning: The ESP8266 will write debugging messages to UART0 18 | prior to your code executing. This may produce garbled DMX 19 | on startup if the output driver is enabled prior to 20 | the startup() method of your sketch. 21 | */ 22 | /**************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "Arduino.h" 29 | #include "cbuf.h" 30 | 31 | extern "C" { 32 | #include "osapi.h" 33 | #include "ets_sys.h" 34 | #include "mem.h" 35 | #include "user_interface.h" 36 | } 37 | 38 | #include "LXESP8266UART0DMX.h" 39 | #include "LXESP8266UARTDMX.h" 40 | 41 | LX8266U0DMX ESP8266DMX0; 42 | 43 | 44 | /* ***************** Utility functions derived from ESP HardwareSerial.cpp **************** 45 | HardwareSerial.cpp - esp8266 UART support - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. 46 | HardwareSerial is licensed under GNU Lesser General Public License 47 | HardwareSerial is included in the esp8266 by ESP8266 Community board package for Arduino IDE 48 | */ 49 | 50 | //these constants are now defined in the Arduino ESP8266 library v2.1.0 51 | //static const int UART0 = 0; 52 | //static const int UART1 = 1; 53 | //static const int UART_NO = -1; 54 | 55 | 56 | /** 57 | * UART GPIOs 58 | * 59 | * UART0 is used by Serial for debug output 60 | * so UART1 is used for DMX output (tx) 61 | * UART1 does not have the rx pin available on most ESP modules so 62 | * UART0 is used for DMX input (rx) 63 | * 64 | * 65 | * UART0 TX: 1 or 2 SPECIAL or FUNCTION_4 66 | * UART0 RX: 3 SPECIAL 67 | * 68 | * UART0 SWAP TX: 15 69 | * UART0 SWAP RX: 13 70 | * 71 | * 72 | * UART1 TX: 2 or 7 (NC) SPECIAL or FUNCTION_4 73 | * UART1 RX: 8 (NC) FUNCTION_4 74 | * 75 | * UART1 SWAP TX: 11 (NC) 76 | * UART1 SWAP RX: 6 (NC) 77 | * 78 | * NC = Not Connected to Module Pads --> No Access 79 | * Pins 6-11 typically connected to flash on most modules 80 | * see http://arduino.esp8266.com/versions/1.6.5-1160-gef26c5f/doc/reference.html 81 | */ 82 | 83 | void uart0_tx_interrupt_handler(LX8266U0DMX* dmxo); 84 | void uart_dual_tx_interrupt_handler(LX8266U0DMX* dmxo); 85 | void uart0_tx_flush(void); 86 | void uart0_enable_tx_interrupt(LX8266U0DMX* dmxo); 87 | void uart_dual_enable_tx_interrupt(LX8266U0DMX* dmxo); 88 | void uart0_enable_dual_tx_interrupt(LX8266U0DMX* dmxo); 89 | void uar0t_disable_tx_interrupt(void); 90 | void uart_dual_disable_tx_interrupt(void); 91 | void uart0_set_baudrate(int uart_nr, int baud_rate); 92 | 93 | void uart0_init_tx(int baudrate, byte config, LX8266U0DMX* dmxo); 94 | 95 | void uart0_uninit_tx(void); 96 | void uart_dual_uninit_tx(void); 97 | 98 | // #################################################################################################### 99 | 100 | // UART register definitions see esp8266_peri.h 101 | 102 | ICACHE_RAM_ATTR void uart0_tx_interrupt_handler(LX8266U0DMX* dmxo) { 103 | 104 | // -------------- UART 0 -------------- 105 | // check uart status register 106 | // if fifo is empty clear interrupt 107 | // then call _tx_empty_irq 108 | if(U0IS & (1 << UIFE)) { 109 | U0IC = (1 << UIFE); 110 | dmxo->txEmptyInterruptHandler(); 111 | } 112 | 113 | } 114 | 115 | ICACHE_RAM_ATTR void uart_dual_tx_interrupt_handler(LX8266U0DMX* dmxo) { 116 | digitalWrite(14,LOW); 117 | 118 | if(U0IS & (1 << UIFE)) { 119 | U0IC = (1 << UIFE); 120 | dmxo->txEmptyInterruptHandler(); 121 | } 122 | 123 | 124 | if(U1IS & (1 << UIFE)) { 125 | U1IC = (1 << UIFE); 126 | ESP8266DMX.txEmptyInterruptHandler(); 127 | } 128 | 129 | digitalWrite(14,HIGH); 130 | } 131 | 132 | 133 | // #################################################################################################### 134 | 135 | //LX uses uart0 for tx 136 | void uart0_tx_flush(void) { 137 | uint32_t tmp = 0x00000000; 138 | 139 | tmp |= (1 << UCTXRST); 140 | 141 | USC0(UART0) |= (tmp); 142 | USC0(UART0) &= ~(tmp); 143 | } 144 | 145 | // ------------- uart0_enable/disable TX functions 146 | //LX uses uart0 for tx 147 | void uart0_enable_tx_interrupt(LX8266U0DMX* dmxo) { 148 | USIC(UART0) = 0x1ff; //clear interrupts 149 | ETS_UART_INTR_ATTACH(&uart0_tx_interrupt_handler, dmxo); 150 | USIE(UART0) |= (1 << UIFE); // enable fifo empty interrupt 151 | ETS_UART_INTR_ENABLE(); // enables all UART interrupts! 152 | } 153 | 154 | void uart_dual_enable_tx_interrupt(LX8266U0DMX* dmxo) { 155 | USIC(UART0) = 0x1ff; //clear interrupts 156 | ETS_UART_INTR_ATTACH(&uart_dual_tx_interrupt_handler, dmxo); 157 | USIE(UART0) |= (1 << UIFE); // enable fifo empty interrupt 158 | ETS_UART_INTR_ENABLE(); // enables all UART interrupts! 159 | } 160 | 161 | //LX uses uart0 for tx 162 | void uart0_disable_tx_interrupt(void) { 163 | USIE(UART0) &= ~(1 << UIFE); 164 | //ETS_UART_INTR_DISABLE(); disables all UART interrupts including Hardware serial 165 | } 166 | 167 | void uart_dual_disable_tx_interrupt(void) { 168 | USIE(UART0) &= ~(1 << UIFE); 169 | USIE(UART1) &= ~(1 << UIFE); 170 | } 171 | 172 | // ------------- uart_set functions 173 | 174 | //LX uses uart0 for tx 175 | void uart0_set_baudrate(int uart_nr, int baud_rate) { 176 | USD(uart_nr) = (ESP8266_CLOCK / baud_rate); 177 | } 178 | 179 | //LX uses uart0 for tx 180 | void uart0_set_config(int uart_nr, byte config) { 181 | USC0(uart_nr) = config; 182 | } 183 | 184 | // ------------- uart_init function 185 | 186 | void uart_dual_init_tx(int baudrate, byte config, LX8266U0DMX* dmxo) { 187 | ESP8266DMX.startOutput(); 188 | ETS_UART_INTR_DISABLE(); //start ESP8266DMX then disable interrupt 189 | 190 | pinMode(1, SPECIAL); 191 | uint32_t conf1 = 0x00000000; 192 | 193 | uart0_set_baudrate(UART0, baudrate); 194 | USC0(UART0) = config; 195 | uart0_tx_flush(); 196 | uart_dual_enable_tx_interrupt(dmxo); 197 | 198 | //conf1 |= (0x00 << UCFET);// tx empty threshold is zero 199 | // tx fifo empty interrupt triggers continuously unless 200 | // data register contains a byte which has not moved to shift reg yet 201 | USC1(UART0) = conf1; 202 | } 203 | 204 | void uart0_init_tx(int baudrate, byte config, LX8266U0DMX* dmxo) { 205 | pinMode(1, SPECIAL); 206 | uint32_t conf1 = 0x00000000; 207 | 208 | uart0_set_baudrate(UART0, baudrate); 209 | USC0(UART0) = config; 210 | uart0_tx_flush(); 211 | uart0_enable_tx_interrupt(dmxo); 212 | 213 | //conf1 |= (0x00 << UCFET);// tx empty threshold is zero 214 | // tx fifo empty interrupt triggers continuously unless 215 | // data register contains a byte which has not moved to shift reg yet 216 | USC1(UART0) = conf1; 217 | } 218 | 219 | // ------------- uart_uninit function 220 | 221 | void uart0_uninit_tx(void) { 222 | uart0_disable_tx_interrupt(); 223 | } 224 | 225 | void uart_dual_uninit_tx(void) { 226 | uart_dual_disable_tx_interrupt(); 227 | } 228 | 229 | // **************************** global data (can be accessed in ISR) *************** 230 | 231 | // UART register definitions see esp8266_peri.h 232 | 233 | #define DMX_DATA_BAUD 250000 234 | #define DMX_BREAK_BAUD 88000 235 | /* 236 | #define UART_STOP_BIT_NUM_SHIFT 4 237 | TWO_STOP_BIT = 0x3 238 | ONE_STOP_BIT = 0x1, 239 | 240 | #define UART_BIT_NUM_SHIFT 2 241 | EIGHT_BITS = 0x3 242 | 243 | parity 244 | #define UCPAE 1 //Parity Enable (possibly set for none??) 245 | #define UCPA 0 //Parity 0:even, 1:odd 246 | 247 | 111100 = 8n2 = 60 = 0x3C (or 0x3E if bit1 is set for no parity) 248 | 011100 = 8n1 = 28 = 0x1C 249 | 250 | */ 251 | 252 | #define FORMAT_8N2 0x3C 253 | #define FORMAT_8E1 0x1C 254 | 255 | 256 | //***** states indicate current position in DMX stream 257 | #define DMX_STATE_BREAK 0 258 | #define DMX_STATE_START 1 259 | #define DMX_STATE_DATA 2 260 | #define DMX_STATE_IDLE 3 261 | #define DMX_STATE_BREAK_SENT 4 262 | 263 | //***** interrupts to wait before changing Baud 264 | #define DATA_END_WAIT 50 //initially was 25 with processor at 80 mHz set to 50 @ 160mHz 265 | #define BREAK_SENT_WAIT 80 //initially was 70 with processor at 80 mHz set to 80 @ 160mHz 266 | 267 | //***** status is if interrupts are enabled and IO is active 268 | #define ISR_DISABLED 0 269 | #define ISR_OUTPUT_ENABLED 1 270 | #define ISR_DUAL_OUTPUT_ENABLED 2 271 | 272 | 273 | /******************************************************************************* 274 | *********************** LX8266DMX member functions ********************/ 275 | 276 | LX8266U0DMX::LX8266U0DMX ( void ) { 277 | _direction_pin = DIRECTION_PIN_NOT_USED; //optional 278 | _slots = DMX_MAX_SLOTS; 279 | _interrupt_status = ISR_DISABLED; 280 | clearSlots(); 281 | } 282 | 283 | LX8266U0DMX::~LX8266U0DMX ( void ) { 284 | stop(); 285 | } 286 | 287 | void LX8266U0DMX::startOutput ( void ) { 288 | if ( _direction_pin != DIRECTION_PIN_NOT_USED ) { 289 | digitalWrite(_direction_pin, HIGH); 290 | } 291 | if ( _interrupt_status != ISR_OUTPUT_ENABLED ) { 292 | stop(); 293 | } 294 | if ( _interrupt_status == ISR_DISABLED ) { //prevent messing up sequence if already started... 295 | _interrupt_status = ISR_OUTPUT_ENABLED; 296 | _dmx_send_state = DMX_STATE_BREAK; 297 | _idle_count = 0; 298 | uart0_init_tx(DMX_BREAK_BAUD, FORMAT_8E1, this);//starts interrupt because fifo is empty 299 | } 300 | } 301 | 302 | void LX8266U0DMX::startDualOutput ( void ) { 303 | if ( _direction_pin != DIRECTION_PIN_NOT_USED ) { 304 | digitalWrite(_direction_pin, HIGH); 305 | } 306 | if ( _interrupt_status != ISR_OUTPUT_ENABLED ) { 307 | stop(); 308 | } 309 | if ( _interrupt_status == ISR_DISABLED ) { //prevent messing up sequence if already started... 310 | _interrupt_status = ISR_DUAL_OUTPUT_ENABLED; 311 | _dmx_send_state = DMX_STATE_BREAK; 312 | _idle_count = 0; 313 | uart_dual_init_tx(DMX_BREAK_BAUD, FORMAT_8E1, this);//starts interrupt because fifo is empty 314 | } 315 | } 316 | 317 | void LX8266U0DMX::stop ( void ) { 318 | if ( _interrupt_status == ISR_OUTPUT_ENABLED ) { 319 | uart0_uninit_tx(); 320 | } else if ( _interrupt_status == ISR_DUAL_OUTPUT_ENABLED ) { 321 | uart_dual_uninit_tx(); 322 | } 323 | _interrupt_status = ISR_DISABLED; 324 | } 325 | 326 | void LX8266U0DMX::setDirectionPin( uint8_t pin ) { 327 | _direction_pin = pin; 328 | pinMode(_direction_pin, OUTPUT); 329 | } 330 | 331 | uint16_t LX8266U0DMX::numberOfSlots (void) { 332 | return _slots; 333 | } 334 | 335 | void LX8266U0DMX::setMaxSlots (int slots) { 336 | _slots = max(slots, DMX_MIN_SLOTS); 337 | } 338 | 339 | void LX8266U0DMX::setSlot (int slot, uint8_t value) { 340 | _dmxData[slot] = value; 341 | } 342 | 343 | uint8_t LX8266U0DMX::getSlot (int slot) { 344 | return _dmxData[slot]; 345 | } 346 | 347 | void LX8266U0DMX::clearSlots (void) { 348 | memset(_dmxData, 0, DMX_MAX_SLOTS+1); 349 | } 350 | 351 | uint8_t* LX8266U0DMX::dmxData(void) { 352 | return &_dmxData[0]; 353 | } 354 | 355 | 356 | /*! 357 | * @discussion TX FIFO EMPTY INTERRUPT 358 | * 359 | * this routine is called when UART fifo is empty 360 | * 361 | * what this does is to push the next byte into the fifo register 362 | * 363 | * when that byte is shifted out and the fifo is empty , the ISR is called again 364 | * 365 | * and the cycle repeats... 366 | * 367 | * until _slots worth of bytes have been sent on succesive triggers of the ISR 368 | * 369 | * and then the fifo empty interrupt is allowed to trigger 25 times to insure the last byte is fully sent 370 | * 371 | * then the break/mark after break is sent at a different speed 372 | * 373 | * and then the fifo empty interrupt is allowed to trigger 60 times to insure the MAB is fully sent 374 | * 375 | * then the baud is restored and the start code is sent 376 | * 377 | * and then on the next fifo empty interrupt 378 | * 379 | * the next data byte is sent 380 | * 381 | * and the cycle repeats... 382 | */ 383 | 384 | ICACHE_RAM_ATTR void LX8266U0DMX::txEmptyInterruptHandler(void) { 385 | 386 | switch ( _dmx_send_state ) { 387 | 388 | case DMX_STATE_BREAK: 389 | // set the slower baud rate and send the break 390 | uart0_set_baudrate(UART0, DMX_BREAK_BAUD); 391 | uart0_set_config(UART0, FORMAT_8E1); 392 | _dmx_send_state = DMX_STATE_BREAK_SENT; 393 | _idle_count = 0; 394 | USF(0) = 0x0; 395 | break; // <- DMX_STATE_BREAK 396 | 397 | case DMX_STATE_START: 398 | // set the baud to full speed and send the start code 399 | uart0_set_baudrate(UART0, DMX_DATA_BAUD); 400 | uart0_set_config(UART0, FORMAT_8N2); 401 | _next_send_slot = 0; 402 | _dmx_send_state = DMX_STATE_DATA; 403 | USF(0) = _dmxData[_next_send_slot++]; //send next slot (start code) 404 | break; // <- DMX_STATE_START 405 | 406 | case DMX_STATE_DATA: 407 | // send the next data byte until the end is reached 408 | USF(0) = _dmxData[_next_send_slot++]; //send next slot 409 | if ( _next_send_slot > _slots ) { 410 | _dmx_send_state = DMX_STATE_IDLE; 411 | _idle_count = 0; 412 | } 413 | break; // <- DMX_STATE_DATA 414 | 415 | case DMX_STATE_IDLE: 416 | // wait a number of interrupts to be sure last data byte is sent before changing baud 417 | _idle_count++; 418 | if ( _idle_count > DATA_END_WAIT ) { 419 | _dmx_send_state = DMX_STATE_BREAK; 420 | } 421 | break; // <- DMX_STATE_IDLE 422 | 423 | case DMX_STATE_BREAK_SENT: 424 | //wait to insure MAB before changing baud back to data speed (takes longer at slower speed) 425 | _idle_count++; 426 | if ( _idle_count > BREAK_SENT_WAIT ) { 427 | _dmx_send_state = DMX_STATE_START; 428 | } 429 | break; // <- DMX_STATE_BREAK_SENT 430 | } 431 | } 432 | 433 | -------------------------------------------------------------------------------- /examples/DMXFadeTest2univ/LXESP8266UART0DMX.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file LXESP8266UART0DMX.h 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266UARTDMX.h) 6 | @copyright 2015-2017 by Claude Heintz 7 | 8 | DMX Output Driver for ESP8266 using UART0. 9 | */ 10 | /**************************************************************************/ 11 | 12 | 13 | 14 | #ifndef LX8266U0DMX_H 15 | #define LX8266U0DMX_H 16 | 17 | #include 18 | #include 19 | 20 | #define DMX_MIN_SLOTS 24 21 | #define DMX_MAX_SLOTS 512 22 | #define DMX_MAX_FRAME 513 23 | 24 | #define DIRECTION_PIN_NOT_USED 255 25 | 26 | /*! 27 | @class LX8266U0DMX 28 | @abstract 29 | LX8266U0DMX is an output driver for sending DMX using an ESP8266 30 | UART0 which has TX mapped to GPIO1. 31 | 32 | LX8266U0DMX output mode continuously sends DMX once its interrupts have been enabled using startOutput(). 33 | Use setSlot() to set the level value for a particular DMX dimmer/address/channel. 34 | 35 | LX8266U0DMX is used with a single instance called ESP8266DMX0. 36 | */ 37 | 38 | class LX8266U0DMX { 39 | 40 | public: 41 | 42 | LX8266U0DMX ( void ); 43 | ~LX8266U0DMX( void ); 44 | 45 | 46 | /*! 47 | * @brief starts interrupt that continuously sends DMX output 48 | * @discussion Sets up baud rate, bits and parity, 49 | * sets globals accessed in ISR, 50 | * enables transmission and tx interrupt. 51 | */ 52 | void startOutput( void ); 53 | 54 | /*! 55 | * @brief starts interrupt that continuously sends DMX output both with UART0 and with LXESP8266DMX->UART1 56 | * @discussion Sets up baud rate, bits and parity, 57 | * sets globals accessed in ISR, 58 | * enables transmission and tx interrupt. Also calls ESP8266DMX.startOutput(); 59 | */ 60 | void startDualOutput( void ); 61 | 62 | 63 | /*! 64 | * @brief disables transmission and tx interrupt 65 | */ 66 | void stop( void ); 67 | /*! 68 | * @brief optional utility sets the pin used to control driver chip's 69 | * DE (data enable) line, HIGH for output, LOW for input. 70 | * @param pin to be automatically set for input/output direction 71 | */ 72 | void setDirectionPin( uint8_t pin ); 73 | 74 | /*! 75 | * @brief the current number of slots 76 | */ 77 | uint16_t numberOfSlots (void); 78 | 79 | /*! 80 | * @brief Sets the number of slots (aka addresses or channels) sent per DMX frame. 81 | * @discussion defaults to 512 or DMX_MAX_SLOTS and should be no less DMX_MIN_SLOTS slots. 82 | * The DMX standard specifies min break to break time no less than 1024 usecs. 83 | * At 44 usecs per slot ~= 24 84 | * @param slot the highest slot number (~24 to 512) 85 | */ 86 | void setMaxSlots (int slot); 87 | 88 | /*! 89 | * @brief reads the value of a slot/address/channel 90 | * @discussion NOTE: Data is not double buffered. 91 | * So a complete single frame is not guaranteed. 92 | * The ISR continuously reads the next frame into the buffer 93 | * @return level (0-255) 94 | */ 95 | uint8_t getSlot (int slot); 96 | 97 | /*! 98 | * @brief Sets the output value of a slot Note:slot[0] is DMX start code! 99 | * @param slot number of the slot aka address or channel (1-512) 100 | * @param value level (0-255) 101 | */ 102 | void setSlot (int slot, uint8_t value); 103 | 104 | /*! 105 | * @brief zero buffer including slot[0] which is start code 106 | */ 107 | void clearSlots (void); 108 | 109 | /*! 110 | * @brief provides direct access to data array 111 | * @return pointer to dmx array 112 | */ 113 | uint8_t* dmxData(void); 114 | 115 | /*! 116 | * @brief UART tx empty interrupt handler 117 | */ 118 | void txEmptyInterruptHandler(void); 119 | 120 | 121 | 122 | private: 123 | 124 | /*! 125 | * @brief represents phase of sending dmx packet data/break/etc used to change baud settings 126 | */ 127 | uint8_t _dmx_send_state; 128 | 129 | /*! 130 | * @brief true when ISR is enabled 131 | */ 132 | uint8_t _interrupt_status; 133 | 134 | /*! 135 | * @brief count of idle interrupts 136 | */ 137 | uint8_t _idle_count; 138 | 139 | /*! 140 | * @brief pin used to control direction of output driver chip 141 | */ 142 | uint8_t _direction_pin; 143 | 144 | /*! 145 | * @brief slot index indicating position of byte to be sent 146 | */ 147 | uint16_t _next_send_slot; 148 | 149 | /*! 150 | * @brief number of dmx slots ~24 to 512 151 | */ 152 | uint16_t _slots; 153 | 154 | /*! 155 | * @brief Array of dmx data including start code 156 | */ 157 | uint8_t _dmxData[DMX_MAX_FRAME]; 158 | 159 | }; 160 | 161 | extern LX8266U0DMX ESP8266DMX0; 162 | 163 | #endif // ifndef LX8266U0DMX_H 164 | -------------------------------------------------------------------------------- /examples/DMXInputNeoPixel/DMXInputNeoPixel.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file DMXInputTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX LICENSE) 6 | @copyright 2019 by Claude Heintz 7 | 8 | Control Neo-Pixels with DMX input 9 | Demonstrates single frame read using ESP8266DMX.isReceiving() 10 | 11 | @section HISTORY 12 | 13 | v1.00 - First release 14 | */ 15 | /**************************************************************************/ 16 | #include 17 | 18 | #include 19 | #ifdef __AVR__ 20 | #include 21 | #endif 22 | 23 | #define DIRECTION_PIN 4 24 | 25 | // data pin for NeoPixels 26 | #define PIN 14 27 | // NUM_OF_NEOPIXELS, max of 170/RGB or 128/RGBW 28 | #define NUM_OF_NEOPIXELS 12 29 | // LEDS_PER_NEOPIXEL, RGB = 3, RGBW = 4 30 | #define LEDS_PER_NEOPIXEL 3 31 | // see Adafruit NeoPixel Library for options to pass to Adafruit_NeoPixel constructor 32 | Adafruit_NeoPixel ring = Adafruit_NeoPixel(NUM_OF_NEOPIXELS, PIN, NEO_GRB + NEO_KHZ800); 33 | 34 | // min sometimes raises compile error. see https://github.com/esp8266/Arduino/issues/263 solution seems to be using _min 35 | const int total_pixels = _min(512, LEDS_PER_NEOPIXEL * NUM_OF_NEOPIXELS); 36 | byte pixels[NUM_OF_NEOPIXELS][LEDS_PER_NEOPIXEL]; 37 | 38 | // flag for dmx data received 39 | int got_dmx = 0; 40 | 41 | void setup() { 42 | pinMode(DIRECTION_PIN, OUTPUT); // pin allows control of tranceiver chip direction 43 | digitalWrite(DIRECTION_PIN, LOW); // low is input 44 | 45 | ESP8266DMX.setDataReceivedCallback(&gotDMXCallback); 46 | delay(1000); //avoid boot print?? 47 | 48 | ring.begin(); 49 | setPixel(0,255); // initial power up indicator 1 red pixel 50 | sendPixels(); 51 | } 52 | 53 | 54 | // ***************** input callback function ************* 55 | 56 | void gotDMXCallback(int slots) { 57 | got_dmx = slots; 58 | } 59 | 60 | // ***************** DMX read single frame ************* 61 | 62 | #define DMX_READ_TIMEOUT_COUNT 10000 63 | 64 | void readDMXFrame() { // <- Attempt to read a single DMX frame 65 | ESP8266DMX.startInput(); 66 | 67 | uint32_t t_o_ct = 0; // timeout counter should wait no more than 2 frames of DMX 68 | // (? good value for limit) 69 | // --will delay animation if too long 70 | // --will prevent read if too short 71 | while ( (got_dmx == 0 ) && (t_o_ct < DMX_READ_TIMEOUT_COUNT) ) { 72 | t_o_ct++; 73 | if ( ESP8266DMX.isReceiving() ) { // if started to read packet, wait until done 74 | t_o_ct = 0; // still timeout in case of DMX signal interruption 75 | while ( ESP8266DMX.isReceiving() && (got_dmx == 0 ) && (t_o_ct < DMX_READ_TIMEOUT_COUNT) ) { 76 | t_o_ct++; 77 | } 78 | 79 | t_o_ct = 0; 80 | } //<- receiving 81 | } //<- ! got_dmx || timeout 82 | 83 | ESP8266DMX.stop(); 84 | } 85 | 86 | 87 | // ***************** sends pixel buffer to ring ************* 88 | 89 | void sendPixels() { 90 | uint16_t r,g,b; 91 | for (int p=0; p0) 121 | If so, it sends the values to the NEoPixels 122 | 123 | *************************************************************************/ 124 | 125 | void loop() { 126 | readDMXFrame(); 127 | 128 | if ( got_dmx ) { 129 | for (int i=0; i 46 | 47 | int got_dmx = 0; 48 | 49 | void setup() { 50 | WiFi.forceSleepBegin(); //not using WiFi, sleep to prevent background activity 51 | pinMode(BUILTIN_LED, OUTPUT); 52 | pinMode(14, OUTPUT); 53 | ESP8266DMX.setDataReceivedCallback(&gotDMXCallback); 54 | delay(1000); //avoid boot print?? 55 | ESP8266DMX.startInput(); 56 | } 57 | 58 | 59 | // ***************** input callback function ************* 60 | 61 | void gotDMXCallback(int slots) { 62 | got_dmx = slots; 63 | } 64 | 65 | /************************************************************************ 66 | 67 | The main loop checks to see if dmx input is available (got_dmx>0) 68 | And then reads the level of dimmer 1 to set PWM level of LED connected to pin 14 69 | 70 | *************************************************************************/ 71 | 72 | void loop() { 73 | if ( got_dmx ) { 74 | //ESP8266 PWM is 10bit 0-1024 75 | analogWrite(14,2*ESP8266DMX.getSlot(1)); 76 | } 77 | //wdt_reset(); 78 | } 79 | -------------------------------------------------------------------------------- /examples/RDMDeviceTest/RDMDeviceTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file RDMDeviceTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX LICENSE) 6 | @copyright 2017 by Claude Heintz 7 | 8 | Example showing LXESP8266DMX 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 | #include 16 | #include 17 | #include "ESP8266WiFi.h" 18 | 19 | int got_dmx = 0; 20 | int got_rdm = 0; 21 | uint8_t discovery_enabled = 1; 22 | uint16_t start_address = 1; 23 | uint16_t input_value = 0; 24 | 25 | uint8_t device_label[33]; 26 | 27 | #define DEFAULT_DEVICE_LABEL "RDM dev test v1.0" 28 | #define MFG_LABEL "LXDMX" 29 | #define MODEL_DESCRIPTION "RDMDeviceTest" 30 | 31 | #define DIRECTION_PIN 15 32 | #define LED_PIN 14 33 | 34 | void setup() { 35 | WiFi.forceSleepBegin(); 36 | delay(5000); 37 | Serial.setDebugOutput(1); //use uart0 for debugging 38 | 39 | pinMode(BUILTIN_LED, OUTPUT); 40 | pinMode(DIRECTION_PIN, OUTPUT); 41 | pinMode(LED_PIN, OUTPUT); 42 | //diagnostic pins 43 | pinMode(12, OUTPUT); 44 | pinMode(16, INPUT_PULLUP); 45 | 46 | strcpy((char*)device_label, DEFAULT_DEVICE_LABEL); 47 | 48 | ESP8266DMX.setDataReceivedCallback(&gotDMXCallback); 49 | ESP8266DMX.setRDMReceivedCallback(&gotRDMCallback); 50 | LX8266DMX::THIS_DEVICE_ID.setBytes(0x6C, 0x78, 0x0F, 0x0A, 0x0C, 0x0E); //change device ID from default 51 | 52 | ESP8266DMX.startRDM(DIRECTION_PIN, DMX_TASK_RECEIVE); 53 | } 54 | 55 | 56 | // ***************** input callback function ************* 57 | 58 | ICACHE_RAM_ATTR void gotDMXCallback(int slots) { 59 | got_dmx = slots; 60 | } 61 | 62 | ICACHE_RAM_ATTR void gotRDMCallback(int len) { 63 | // rdm start code and checksum are validated before this is called 64 | got_rdm = len; 65 | } 66 | 67 | /************************************************************************ 68 | 69 | The main loop checks to see if dmx input is available (got_dmx>0) 70 | And then reads the level of dimmer 1 to set PWM level of LED connected to pin 14 71 | 72 | *************************************************************************/ 73 | 74 | void loop() { 75 | if ( got_dmx ) { 76 | input_value = ESP8266DMX.getSlot(start_address); 77 | //gamma correct 78 | input_value = (input_value * input_value ) / 255; 79 | //ESP8266 PWM is 10bit 0-1024 80 | analogWrite(LED_PIN,2*input_value); 81 | got_dmx = 0; //reset 82 | 83 | } else if ( got_rdm ) { 84 | 85 | uint8_t* rdmdata = ESP8266DMX.receivedRDMData(); 86 | 87 | uint8_t cmdclass = rdmdata[RDM_IDX_CMD_CLASS]; 88 | uint16_t pid = (rdmdata[RDM_IDX_PID_MSB] << 8 ) | rdmdata[RDM_IDX_PID_LSB]; 89 | 90 | 91 | if ( cmdclass == RDM_DISCOVERY_COMMAND ) { 92 | 93 | if ( pid == RDM_DISC_UNIQUE_BRANCH ) { 94 | if ( discovery_enabled ) { 95 | uint64_t tv = ESP8266DMX.THIS_DEVICE_ID.getValue(); 96 | UID u; 97 | u.setBytes(&rdmdata[24]); //lower 98 | uint64_t uv = u.getValue(); 99 | if ( tv >= uv ) { 100 | u.setBytes(&rdmdata[30]); //upper 101 | uv = u.getValue(); 102 | if ( tv <= uv ) { 103 | ESP8266DMX.sendRDMDiscoverBranchResponse(); 104 | } 105 | } 106 | } 107 | } else { // mute RDM_DISCOVERY_COMMAND PIDs 108 | UID destination; 109 | destination.setBytes(&rdmdata[RDM_IDX_DESTINATION_UID]); 110 | 111 | if ( pid == RDM_DISC_MUTE ) { 112 | if ( destination == ESP8266DMX.THIS_DEVICE_ID ) { 113 | discovery_enabled = 0; 114 | // send ACK 115 | UID source; 116 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 117 | ESP8266DMX.sendMuteAckRDMResponse(RDM_DISC_COMMAND_RESPONSE, source, RDM_DISC_MUTE); 118 | } 119 | } else if ( pid == RDM_DISC_UNMUTE ) { 120 | if ( destination == BROADCAST_ALL_DEVICES_ID ) { 121 | // just un-mute 122 | discovery_enabled = 1; 123 | } else if ( destination == ESP8266DMX.THIS_DEVICE_ID ) { 124 | discovery_enabled = 1; 125 | // send ACK 126 | UID source; 127 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 128 | ESP8266DMX.sendMuteAckRDMResponse(RDM_DISC_COMMAND_RESPONSE, source, RDM_DISC_UNMUTE); 129 | } 130 | } 131 | 132 | } 133 | 134 | } else if ( cmdclass == RDM_GET_COMMAND ) { 135 | UID destination; 136 | destination.setBytes(&rdmdata[RDM_IDX_DESTINATION_UID]); 137 | 138 | if ( destination == ESP8266DMX.THIS_DEVICE_ID ) { 139 | UID source; 140 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 141 | 142 | if ( pid == RDM_DEVICE_START_ADDR ) { 143 | 144 | uint8_t sa[2]; 145 | sa[0] = start_address >> 8; 146 | sa[1] = start_address & 0xff; 147 | ESP8266DMX.sendRDMGetResponse(source, pid, sa, 2); 148 | } else if ( pid == RDM_DEVICE_MFG_LABEL ) { 149 | const char * label = MFG_LABEL; 150 | ESP8266DMX.sendRDMGetResponse(source, pid, (uint8_t*)label, 5); 151 | } else if ( pid == RDM_DEVICE_MODEL_DESC ) { 152 | const char * label = MODEL_DESCRIPTION; 153 | ESP8266DMX.sendRDMGetResponse(source, pid, (uint8_t*)label, 13); 154 | } else if ( pid == RDM_DEVICE_DEV_LABEL ) { 155 | ESP8266DMX.sendRDMGetResponse(source, pid, device_label, strlen((const char*)device_label)); 156 | } 157 | 158 | 159 | } 160 | } else if ( cmdclass == RDM_SET_COMMAND ) { 161 | UID destination; 162 | destination.setBytes(&rdmdata[RDM_IDX_DESTINATION_UID]); 163 | 164 | if ( destination == ESP8266DMX.THIS_DEVICE_ID ) { 165 | UID source; 166 | source.setBytes(&rdmdata[RDM_IDX_SOURCE_UID]); 167 | 168 | if ( pid == RDM_DEVICE_START_ADDR ) { 169 | uint16_t scratch = (rdmdata[24] << 8) + rdmdata[25]; 170 | if (( scratch > 0 ) && ( scratch < 513 )) { 171 | start_address = scratch; 172 | } 173 | ESP8266DMX.sendAckRDMResponse(RDM_SET_COMMAND_RESPONSE, source, pid); 174 | 175 | } else if ( pid == RDM_DEVICE_DEV_LABEL ) { 176 | uint8_t llen = 0; 177 | if ( rdmdata[2] > 24 ) { //label not empty string 178 | llen = rdmdata[2] - 24; 179 | if ( llen > 32 ) { //limit to max 32 characters 180 | llen = 32; 181 | } 182 | } 183 | for ( uint8_t j=0; j<33; j++) { //copy label, zero the rest of the array 184 | if ( j < llen ) { 185 | device_label[j] = rdmdata[24+j]; 186 | } else { 187 | device_label[j] = 0; 188 | } 189 | } // <-for 190 | ESP8266DMX.sendAckRDMResponse(RDM_SET_COMMAND_RESPONSE, source, pid); 191 | } // <-pid RDM_DEVICE_DEV_LABEL 192 | } 193 | } 194 | got_rdm = 0; 195 | } //gotRDM 196 | 197 | wdt_reset(); 198 | } 199 | -------------------------------------------------------------------------------- /examples/feather shield ESP8266:ESP32/FeatherDMXShieldDesign.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudeheintz/LXESP8266DMX/760972edc8e9239692a7a47f3db275ac64f7d5b8/examples/feather shield ESP8266:ESP32/FeatherDMXShieldDesign.pdf -------------------------------------------------------------------------------- /examples/rdmControllerTest/rdmControllerTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file rdmControllerTest.ino 4 | @author Claude Heintz 5 | @license BSD (see LXESP8266DMX LICENSE) 6 | @copyright 2017 by Claude Heintz 7 | 8 | Test of LXESP8266DMX 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 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | uint8_t testLevel = 0; 23 | uint8_t loopDivider = 0; 24 | uint8_t identifyFlag = 1; 25 | uint8_t tableChangedFlag = 0; 26 | 27 | TOD tableOfDevices; 28 | TOD discoveryTree; 29 | 30 | UID lower(0,0,0,0,0,0); 31 | UID upper(0,0,0,0,0,0); 32 | UID mid(0,0,0,0,0,0); 33 | UID found(0,0,0,0,0,0); 34 | 35 | // DIRECTION_PIN use 4 or 15 36 | #define DIRECTION_PIN 4 37 | #define DISC_STATE_SEARCH 0 38 | #define DISC_STATE_TBL_CK 1 39 | uint8_t discovery_state = DISC_STATE_TBL_CK; 40 | uint8_t discovery_tbl_ck_index = 0; 41 | 42 | 43 | //***************** discovery functions 44 | 45 | void checkDeviceFound(UID found) { 46 | if ( testMute(found) ) { 47 | tableOfDevices.add(found); 48 | tableChangedFlag = 1; 49 | } 50 | 51 | } 52 | 53 | uint8_t testMute(UID u) { 54 | // try three times to get response when sending a mute message 55 | if ( ESP8266DMX.sendRDMDiscoveryMute(u, RDM_DISC_MUTE) ) { 56 | return 1; 57 | } 58 | if ( ESP8266DMX.sendRDMDiscoveryMute(u, RDM_DISC_MUTE) ) { 59 | return 1; 60 | } 61 | if ( ESP8266DMX.sendRDMDiscoveryMute(u, RDM_DISC_MUTE) ) { 62 | return 1; 63 | } 64 | return 0; 65 | } 66 | 67 | uint8_t checkTable(uint8_t ck_index) { 68 | if ( ck_index == 0 ) { 69 | ESP8266DMX.sendRDMDiscoveryMute(BROADCAST_ALL_DEVICES_ID, RDM_DISC_UNMUTE); 70 | } 71 | 72 | if ( tableOfDevices.getUIDAt(ck_index, &found) ) { 73 | if ( testMute(found) ) { 74 | // device confirmed 75 | return ck_index += 6; 76 | } 77 | 78 | // device not found 79 | tableOfDevices.removeUIDAt(ck_index); 80 | tableChangedFlag = 1; 81 | return ck_index; 82 | } 83 | // index invalid 84 | return 0; 85 | } 86 | 87 | void identifyEach() { 88 | int i = 0; 89 | uint8_t notDone = 1; 90 | while ( notDone ) { 91 | i = tableOfDevices.getNextUID(i, &found); 92 | if ( i < 0 ) { 93 | notDone = 0; 94 | } else { 95 | //uint16_t data; //for DMX address and identify device on/off 96 | uint8_t data[2]; 97 | if ( ESP8266DMX.sendRDMGetCommand(found, RDM_DEVICE_START_ADDR, data, 2) ) { 98 | uint16_t addr = (data[0] << 8) | data[1]; 99 | 100 | if ( addr == 0x0F ) { 101 | data[0] = 0x00; 102 | data[1] = 0x01; 103 | ESP8266DMX.sendRDMSetCommand(found, RDM_DEVICE_START_ADDR, (uint8_t*)data, 2); 104 | } 105 | 106 | data[0] = 0x01; 107 | ESP8266DMX.sendRDMSetCommand(found, RDM_IDENTIFY_DEVICE, (uint8_t*)data, 1); 108 | delay(2000); 109 | data[0] = 0x00; 110 | ESP8266DMX.sendRDMSetCommand(found, RDM_IDENTIFY_DEVICE, (uint8_t*)data, 1); 111 | } 112 | } 113 | } 114 | } 115 | 116 | //called when range responded, so divide into sub ranges push them on stack to be further checked 117 | void pushActiveBranch(UID lower, UID upper) { 118 | if ( mid.becomeMidpoint(lower, upper) ) { 119 | discoveryTree.push(lower); 120 | discoveryTree.push(mid); 121 | discoveryTree.push(mid); 122 | discoveryTree.push(upper); 123 | } else { 124 | // No midpoint possible: lower and upper are equal or a 1 apart 125 | checkDeviceFound(lower); 126 | checkDeviceFound(upper); 127 | } 128 | } 129 | 130 | void pushInitialBranch() { 131 | lower.setBytes(0); 132 | upper.setBytes(BROADCAST_ALL_DEVICES_ID); 133 | discoveryTree.push(lower); 134 | discoveryTree.push(upper); 135 | 136 | //ETC devices seem to only respond with wildcard or exact manufacturer ID 137 | lower.setBytes(0x657400000000); 138 | upper.setBytes(0x6574FFFFFFFF); 139 | discoveryTree.push(lower); 140 | discoveryTree.push(upper); 141 | } 142 | 143 | uint8_t checkNextRange() { 144 | if ( discoveryTree.pop(&upper) ) { 145 | if ( discoveryTree.pop(&lower) ) { 146 | if ( lower == upper ) { 147 | checkDeviceFound(lower); 148 | } else { //not leaf so, check range lower->upper 149 | uint8_t result = ESP8266DMX.sendRDMDiscoveryPacket(lower, upper, &found); 150 | if ( result ) { 151 | //this range responded, so divide into sub ranges push them on stack to be further checked 152 | pushActiveBranch(lower, upper); 153 | 154 | } else if ( ESP8266DMX.sendRDMDiscoveryPacket(lower, upper, &found) ) { 155 | pushActiveBranch(lower, upper); //if discovery fails, try a second time 156 | } 157 | } // end check range 158 | return 1; // UID ranges may be remaining to test 159 | } // end valid pop 160 | } // end valid pop 161 | return 0; // none left to pop 162 | } 163 | 164 | 165 | 166 | void testRDMDiscovery() { 167 | if ( discovery_state ) { 168 | // check the table of devices 169 | discovery_tbl_ck_index = checkTable(discovery_tbl_ck_index); 170 | if ( discovery_tbl_ck_index == 0 ) { 171 | // done with table check 172 | discovery_state = DISC_STATE_SEARCH; 173 | pushInitialBranch(); 174 | 175 | if ( identifyFlag ) { //once per cycle identify each device 176 | identifyEach(); //this is just to demonstrate GET device address 177 | identifyFlag = 0; //and SET identify device 178 | } 179 | 180 | if ( tableChangedFlag ) { //if the table has changed... 181 | tableChangedFlag = 0; 182 | 183 | // if this were an Art-Net application, you would send an 184 | // ArtTOD packet here, because the device table has changed. 185 | // for this test, we just print the list of devices 186 | // Serial.println("_______________ Table Of Devices _______________"); 187 | // tableOfDevices.printTOD(); 188 | } 189 | } 190 | } else { // search for devices in range popped from discoveryTree 191 | if ( checkNextRange() == 0 ) { 192 | // done with search 193 | discovery_tbl_ck_index = 0; 194 | discovery_state = DISC_STATE_TBL_CK; 195 | } 196 | } 197 | } 198 | 199 | /************************************************************************ 200 | setup 201 | *************************************************************************/ 202 | void setup() { 203 | //Serial.begin(115200); 204 | //Serial.print("setup... "); 205 | Serial.setDebugOutput(1); //use uart0 for debugging 206 | 207 | pinMode(BUILTIN_LED, OUTPUT); 208 | digitalWrite(BUILTIN_LED, HIGH); //means opposite 209 | 210 | //pinMode(15, OUTPUT); // used for testing 211 | 212 | pinMode(DIRECTION_PIN, OUTPUT); 213 | ESP8266DMX.startRDM(DIRECTION_PIN); 214 | //Serial.println("setup complete"); 215 | } 216 | 217 | 218 | /************************************************************************ 219 | 220 | The main loop checks to see if the level of the designated slot has changed 221 | and prints the new level to the serial monitor. If a PWM channel is assigned, 222 | it also sets the output level. 223 | 224 | *************************************************************************/ 225 | 226 | void loop() { 227 | delay(2); 228 | testRDMDiscovery(); 229 | 230 | ESP8266DMX.setSlot(7,testLevel); 231 | ESP8266DMX.setSlot(8,255); 232 | ESP8266DMX.setSlot(103,testLevel); 233 | ESP8266DMX.setSlot(101,255); 234 | loopDivider++; 235 | if ( loopDivider == 4 ) { 236 | testLevel++; 237 | loopDivider = 0; 238 | } 239 | if ( testLevel == 1 ) { 240 | delay(500); 241 | identifyFlag = 1; 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /extras/doc/Classes/LX8266DMX/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for LX8266DMX (LXESP8266UARTDMX.h) 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 68 |
Class:
  LX8266DMX
18 | 19 |
20 | 21 | 22 |
Introduction
23 |
24 | 43 |
44 | 46 |
45 | Member Data
47 |
48 | Protected 49 |
58 |
59 |

Other Reference

60 |
61 | 62 | 63 |
Header
64 |
65 | 66 | 67 |

 

69 |

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

LXESP8266UARTDMX.h

259 |
260 | 262 |
Includes:
<Arduino.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 |
LX8266DMX
270 |

LX8266DMX is a driver for sending or receiving DMX using an ESP8266 271 | UART1 which has TX mapped to GPIO2 is used for output 272 | UART0 which is mapped to the RX pin is used for input 273 |

274 |

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

277 |

LX8266DMX input mode receives DMX using the ESP8266's UART0 RX pin 278 | LX8266DMX continuously updates its DMX buffer once its interrupts have been enabled using startInput() 279 | and DMX data is received by UART0. 280 | Use getSlot() to read the level value for a particular DMX dimmer/address/channel. 281 |

282 |

LX8266DMX is used with a single instance called ESP8266DMX. 283 |

284 |
285 |

 


288 |
289 | 290 | -------------------------------------------------------------------------------- /extras/doc/rdm/TOD/Classes/TOD/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for TOD (TOD.h) 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 52 |
Class:
  TOD
18 | 19 |
20 | 21 | 22 |
Introduction
23 |
24 |
25 | 27 |
26 | Member Functions
28 |
29 | Public 30 |
42 |
43 |

Other Reference

44 |
45 | 46 | 47 |
Header
48 |
49 | 50 | 51 |

 

53 |

54 | 55 | -------------------------------------------------------------------------------- /extras/doc/rdm/TOD/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | TOD.h 5 | 6 | 7 | 8 | 134 | 150 | 162 | 252 | 253 | 254 | 255 | 256 |
257 | 258 |

TOD.h

259 |
260 | 261 | 264 |
Author:
Claude Heintz @license BSD (see LXESP8266DMX.h)
Includes:
<stdint.h>
262 | <WString.h>
263 | <rdm/UID.h>
265 |

Introduction

266 |

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

268 |

269 |

Classes

270 |
271 |
TOD
272 |

Implements an RDM table of devices. Contains a list of up to 200 UIDs. 273 |

274 |
275 |

 


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

 

35 |

36 | 37 | -------------------------------------------------------------------------------- /extras/doc/rdm/UID/Classes/UID/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for UID (UID.h) 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 53 |
Class:
  UID
18 | 19 |
20 | 21 | 22 |
Introduction
23 |
24 | 43 |
44 |

Other Reference

45 |
46 | 47 | 48 |
Header
49 |
50 | 51 | 52 |

 

54 |

55 | 56 | -------------------------------------------------------------------------------- /extras/doc/rdm/UID/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | UID.cpp 5 | 6 | 7 | 8 | 134 | 150 | 162 | 252 | 253 | 254 | 255 | 256 |
257 | 258 |

UID.cpp

259 |
260 | 261 | 264 |
Author:
Claude Heintz @license BSD (see LXESP32DMX.h)
Includes:
<stdint.h>
262 | <WString.h>
263 | <Printable.h>
265 |

Introduction

266 |

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

268 |

269 |

Classes

270 |
271 |
UID
272 |

A class to make it easier to handle RDM UIDs. 273 | (Based on Arduino IPAddress class using 6 bytes instead of 4) 274 |

275 |
276 |

277 |

Functions

278 |
279 |
BROADCAST_ALL_DEVICES_ID
280 |

ALL_DEVICES wildcard UID 281 |

282 |
283 |
284 | 285 |

BROADCAST_ALL_DEVICES_ID

286 |

ALL_DEVICES wildcard UID 287 |

288 |
289 |
const UID BROADCAST_ALL_DEVICES_ID(
290 |     0xFFFFFFFFFFFF);  
291 |
292 |

 


306 |
307 | 308 | -------------------------------------------------------------------------------- /extras/doc/rdm/UID/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for UID.cpp (UID.h) 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 42 |
Header:
  UID.cpp
18 | 19 |
20 | 21 | 22 |
Introduction
23 |
24 |
25 | 27 |
26 | Functions
28 |
29 | 30 |
32 |
33 | 35 |
34 | Classes
36 |
37 |
39 | 40 | 41 |

 

43 |

44 | 45 | -------------------------------------------------------------------------------- /extras/doc/toc.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Documentation for LXESP8266UARTDMX.h 6 | 7 | 8 | 9 | 10 | 11 | 14 |
 
15 | 16 | 17 | 34 |
Header:
  LXESP8266UARTDMX.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 LXESP8266DMX 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | ESP8266DMX KEYWORD1 8 | LX8266DMX 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 | receivedData KEYWORD2 24 | receivedRDMData KEYWORD2 25 | getSlot KEYWORD2 26 | setDataReceivedCallback KEYWORD2 27 | setRDMReceivedCallback KEYWORD2 28 | sendRDMDiscoveryPacket KEYWORD2 29 | sendRDMDiscoveryMute KEYWORD2 30 | sendRDMGetCommand KEYWORD2 31 | sendRDMSetCommand KEYWORD2 32 | 33 | ####################################### 34 | # Constants 35 | ####################################### 36 | 37 | DMX_MIN_SLOTS LITERAL1 38 | DMX_MAX_SLOTS LITERAL1 39 | 40 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LXESP8266DMX 2 | version=2.2 3 | author=Claude Heintz 4 | maintainer=Claude Heintz 5 | sentence=DMX UART driver for ESP8266. 6 | paragraph=With addition of external Max485 liner driver chip, enables DMX output using UART1 serial (GPIO2) and input using UART0 RX. 7 | category=Communication 8 | url=https://github.com/claudeheintz/LXESP8266DMX 9 | architectures=esp8266 10 | -------------------------------------------------------------------------------- /src/LXESP8266UARTDMX.h: -------------------------------------------------------------------------------- 1 | /* LXESP8266UARTDMX.h 2 | Copyright 2015 by Claude Heintz Design 3 | 4 | Copyright (c) 2015, Claude Heintz 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of LXESP8266DMX nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ----------------------------------------------------------------------------------- 32 | 33 | The LXESP8266UARTDMX library supports output and input of DMX using the UART 34 | serial output of an ESP8266 microcontroller. LXESP8266UARTDMX uses 35 | UART1 instead of UART0 for output. This means that hardware Serial 36 | can still be used for communication. (do not use Serial1) 37 | 38 | This is the circuit for a simple unisolated DMX Shield 39 | that could be used with LXESP8266UARTDMX. It uses a line driver IC 40 | to convert the output from the ESP8266 to DMX: 41 | 42 | ESP8266 Pin 43 | | SN 75176 A or MAX 481CPA 44 | V _______________ 45 | RX (3) |----------------------| 1 Vcc 8 |------(+5v) 46 | | | | DMX Output 47 | | +----| 2 !RE B 7 |---------------- Pin 2 48 | | | | | 49 | (4/5) |----{+5v/GND}----+----| 3 DE A 6 |---------------- Pin 3 50 | | | | 51 | TX (2) |----------------------| 4 DI Gnd 5 |---+------------ Pin 1 52 | | | 53 | | (GND) 54 | 55 | Data Enable (DE) and Inverted Read Enable (!RE) can be wired to +5v for output or Gnd for input 56 | if direction switching is not needed. 57 | 58 | -------------------------------------------------------------------------- 59 | ------------------------ About UART GPIOs ------------------------------ 60 | 61 | UART0 is used by Serial for debug output (also see datasheet pg 21) 62 | so UART1 is used for DMX output (tx) 63 | UART1 does not have the rx pin available on most ESP modules so 64 | UART0 is used for DMX input (rx) 65 | 66 | 67 | UART0 TX: 1 or 2 SPECIAL or FUNCTION_4 68 | UART0 RX: 3 SPECIAL 69 | 70 | UART0 SWAP TX: 15 71 | UART0 SWAP RX: 13 72 | 73 | UART1 TX: 2 or 7 (NC) SPECIAL or FUNCTION_4 74 | UART1 RX: 8 (NC) FUNCTION_4 75 | 76 | UART1 SWAP TX: 11 (NC) 77 | UART1 SWAP RX: 6 (NC) 78 | 79 | NC = Not Connected to Module Pads --> No Access 80 | Pins 6-11 typically connected to flash on most modules 81 | see http://arduino.esp8266.com/versions/1.6.5-1160-gef26c5f/doc/reference.html 82 | */ 83 | 84 | 85 | #ifndef LX8266DMX_H 86 | #define LX8266DMX_H 87 | 88 | 89 | #include 90 | #include 91 | #include 92 | 93 | #define DMX_MIN_SLOTS 24 94 | #define RDM_MAX_FRAME 257 95 | #define DMX_MAX_SLOTS 512 96 | #define DMX_MAX_FRAME 513 97 | 98 | //***** states indicate current position in DMX stream 99 | #define DMX_READ_STATE_IDLE 0 100 | #define DMX_READ_STATE_RECEIVING 1 101 | 102 | #define DMX_TASK_RECEIVE 0 103 | #define DMX_TASK_SEND 1 104 | #define DMX_TASK_SEND_RDM 2 105 | #define DMX_TASK_SET_SEND 3 106 | #define DMX_TASK_SET_SEND_RDM 4 107 | 108 | #define RDM_NO_DISCOVERY 0 109 | #define RDM_PARTIAL_DISCOVERY 1 110 | #define RDM_DID_DISCOVER 2 111 | 112 | #define DIRECTION_PIN_NOT_USED 255 113 | 114 | typedef void (*LXRecvCallback)(int); 115 | 116 | /*! 117 | @class LX8266DMX 118 | @abstract 119 | LX8266DMX is a driver for sending or receiving DMX using an ESP8266 120 | UART1 which has TX mapped to GPIO2 is used for output 121 | UART0 which is mapped to the RX pin is used for input 122 | 123 | LX8266DMX output mode continuously sends DMX once its interrupts have been enabled using startOutput(). 124 | Use setSlot() to set the level value for a particular DMX dimmer/address/channel. 125 | 126 | LX8266DMX input mode receives DMX using the ESP8266's UART0 RX pin 127 | LX8266DMX continuously updates its DMX buffer once its interrupts have been enabled using startInput() 128 | and DMX data is received by UART0. 129 | Use getSlot() to read the level value for a particular DMX dimmer/address/channel. 130 | 131 | LX8266DMX is used with a single instance called ESP8266DMX. 132 | 133 | LX8266DMX is NOT compatible with Serial.begin when DMX will be read. 134 | */ 135 | 136 | class LX8266DMX { 137 | 138 | public: 139 | 140 | LX8266DMX ( void ); 141 | ~LX8266DMX( void ); 142 | 143 | /*! 144 | * @brief starts interrupt that continuously sends DMX output 145 | * @discussion Sets up baud rate, bits and parity, 146 | * sets globals accessed in ISR, 147 | * enables transmission and tx interrupt. 148 | */ 149 | void startOutput( void ); 150 | 151 | /*! 152 | * @brief starts interrupt that continuously reads DMX data 153 | * @discussion sets up baud rate, bits and parity, 154 | * sets globals accessed in ISR, 155 | * enables transmission and tx interrupt 156 | */ 157 | void startInput( void ); 158 | 159 | 160 | /*! 161 | * @brief starts interrupt that handled send/receive of RDM & DMX 162 | * @discussion 163 | */ 164 | void startRDM ( uint8_t pin, uint8_t direction=1 ); 165 | 166 | /*! 167 | * @brief disables transmission and tx interrupt 168 | */ 169 | void stop( void ); 170 | 171 | /*! 172 | * @brief optional utility sets the pin used to control driver chip's 173 | * DE (data enable) line, HIGH for output, LOW for input. 174 | * @param pin to be automatically set for input/output direction 175 | */ 176 | void setDirectionPin( uint8_t pin ); 177 | 178 | /*! 179 | * @brief the current number of slots 180 | */ 181 | uint16_t numberOfSlots (void); 182 | 183 | /*! 184 | * @brief Sets the number of slots (aka addresses or channels) sent per DMX frame. 185 | * @discussion defaults to 512 or DMX_MAX_SLOTS and should be no less DMX_MIN_SLOTS slots. 186 | * The DMX standard specifies min break to break time no less than 1024 usecs. 187 | * At 44 usecs per slot ~= 24 188 | * @param slot the highest slot number (~24 to 512) 189 | */ 190 | void setMaxSlots (int slot); 191 | 192 | /*! 193 | * @brief reads the value of a slot/address/channel 194 | * @discussion NOTE: Data is not double buffered. 195 | * So a complete single frame is not guaranteed. 196 | * The ISR continuously reads the next frame into the buffer 197 | * @return level (0-255) 198 | */ 199 | uint8_t getSlot (int slot); 200 | 201 | /*! 202 | * @brief Sets the output value of a slot Note:slot[0] is DMX start code! 203 | * @param slot number of the slot aka address or channel (1-512) 204 | * @param value level (0-255) 205 | */ 206 | void setSlot (int slot, uint8_t value); 207 | 208 | /*! 209 | * @brief zero buffer including slot[0] which is start code 210 | */ 211 | void clearSlots (void); 212 | 213 | /*! 214 | * @brief provides direct access to data array 215 | * @return pointer to dmx array 216 | */ 217 | uint8_t* dmxData(void); 218 | 219 | uint8_t* rdmData( void ); 220 | 221 | uint8_t* receivedData( void ); 222 | 223 | uint8_t* receivedRDMData( void ); 224 | 225 | /*! 226 | * @brief UART tx empty interrupt handler 227 | */ 228 | void txEmptyInterruptHandler(void); 229 | 230 | /*! 231 | * @brief UART RDM tx empty interrupt handler 232 | */ 233 | void rdmTxEmptyInterruptHandler(void); 234 | 235 | /*! 236 | * @brief Function called when DMX frame has been read 237 | * @discussion Sets a pointer to a function that is called 238 | * on the break after a DMX frame has been received. 239 | * Whatever happens in this function should be quick! 240 | * Best used to set a flag that is polled outside of ISR for available data. 241 | */ 242 | void setDataReceivedCallback(LXRecvCallback callback); 243 | 244 | 245 | /*! 246 | * @brief utility for debugging prints received data 247 | */ 248 | void printReceivedData( void ); 249 | 250 | /*! 251 | * @brief called when a packet is finished being received either through start of next packet or size reached 252 | */ 253 | void packetComplete( void ); 254 | 255 | /*! 256 | * @brief sets read state to wait for break 257 | */ 258 | void resetFrame( void ); 259 | 260 | /*! 261 | * @brief called when a break is detected 262 | */ 263 | void breakReceived( void ); 264 | 265 | /*! 266 | * @brief called from isr when a byte is read from register 267 | */ 268 | void byteReceived(uint8_t c); 269 | 270 | /*! 271 | * @brief returns true if _dmx_read_state == DMX_READ_STATE_RECEIVING 272 | */ 273 | uint8_t isReceiving( void ); 274 | 275 | /************************************ RDM Methods ***********************************/ 276 | 277 | /*! 278 | * @brief Function called when RDM frame has been read 279 | * @discussion Sets a pointer to a function that is called 280 | * after an RDM frame has been received. 281 | * Whatever happens in this function should be quick! 282 | * Best used to set a flag that is polled outside of ISR for available data. 283 | */ 284 | void setRDMReceivedCallback(LXRecvCallback callback); 285 | 286 | /*! 287 | * @brief indicate if dmx frame should be sent by bi-directional task loop 288 | * @discussion should only be called by task loop 289 | * @return 1 if dmx frame should be sent 290 | * return 2 if RDM should be sent 291 | * return 3 if RDM should be sent and set mode to 1 after first frame finished 292 | */ 293 | uint8_t rdmTaskMode( void ); 294 | 295 | 296 | /*! 297 | * @brief sets rdm task to send mode and the direction pin to HIGH 298 | */ 299 | void setTaskSendDMX( void ); 300 | 301 | /*! 302 | * @brief sets rdm task to send mode after task mode loops. 303 | * Sent after sending RDM message so DMX is resumed. 304 | * Blocks until task loop sets mode to send. 305 | */ 306 | void restoreTaskSendDMX( void ); 307 | 308 | /*! 309 | * @brief sets rdm task to receive mode 310 | * Prepares variables to receive starting with next break. 311 | * Sets the direction pin to LOW. 312 | */ 313 | void setTaskReceive( void ); 314 | 315 | /*! 316 | * @brief length of the rdm packet awaiting being sent 317 | */ 318 | uint8_t rdmPacketLength( void ); 319 | 320 | /*! 321 | * @brief sends packet using bytes from _rdmPacket ( rdmData() ) 322 | * @discussion sets rdm task mode to DMX_TASK_SEND_RDM which causes 323 | * _rdmPacket to be sent on next opportunity from task loop. 324 | * after _rdmPacket is sent, task mode switches to listen for response. 325 | * 326 | * set _rdm_read_handled flag prior to calling sendRawRDMPacket 327 | * _rdm_read_handled = 1 if reading is handled by calling function 328 | * _rdm_read_handled = 0 if desired to resume passive listening for next break 329 | */ 330 | void sendRawRDMPacket( uint8_t len ); 331 | 332 | /*! 333 | * @brief convenience method for setting fields in the top 20 bytes of an RDM message 334 | * that will be sent. 335 | * Destination UID needs to be set outside this method. 336 | * Source UID is set to static member THIS_DEVICE_ID 337 | */ 338 | void setupRDMControllerPacket(uint8_t* pdata, uint8_t msglen, uint8_t port, uint16_t subdevice); 339 | 340 | /*! 341 | * @brief convenience method for setting fields in the top 20 bytes of an RDM message 342 | * that will be sent. 343 | * Destination UID needs to be set outside this method. 344 | * Source UID is set to static member THIS_DEVICE_ID 345 | */ 346 | void setupRDMDevicePacket(uint8_t* pdata, uint8_t msglen, uint8_t rtype, uint8_t msgs, uint16_t subdevice); 347 | 348 | /*! 349 | * @brief convenience method for setting fields in the top bytes 20-23 of an RDM message 350 | * that will be sent. 351 | */ 352 | void setupRDMMessageDataBlock(uint8_t* pdata, uint8_t cmdclass, uint16_t pid, uint8_t pdl); 353 | 354 | /*! 355 | * @brief send discovery packet using upper and lower bounds 356 | * @discussion Assumes that regular DMX was sending when method is called and 357 | * so restores sending, waiting for a frame to be sent before returning. 358 | * @return 1 if discovered, 2 if valid packet (UID stored in uldata[12-17]) 359 | */ 360 | uint8_t sendRDMDiscoveryPacket(UID lower, UID upper, UID* single); 361 | 362 | /*! 363 | * @brief send discovery mute/un-mute packet to target UID 364 | * @discussion Assumes that regular DMX was sending when method is called and 365 | * so restores sending, waiting for a frame to be sent before returning. 366 | * @return 1 if ack response is received. 367 | */ 368 | uint8_t sendRDMDiscoveryMute(UID target, uint8_t cmd); 369 | 370 | /*! 371 | * @brief send previously built packet in _rdmPacket and validate response 372 | * @discussion Response to packet, if valid, is copied into _rdmData and 1 is returned 373 | * Otherwise, 0 is returned. 374 | */ 375 | uint8_t sendRDMControllerPacket( void ); 376 | 377 | /*! 378 | * @brief copies len of bytes into _rdmPacket and sends it 379 | * @discussion Response to packet, if valid, is copied into _rdmData and 1 is returned 380 | * Otherwise, 0 is returned. 381 | */ 382 | uint8_t sendRDMControllerPacket( uint8_t* bytes, uint8_t len ); 383 | 384 | /*! 385 | * @brief send RDM_GET_COMMAND packet 386 | * @discussion Assumes that regular DMX was sending when method is called and 387 | * so restores sending, waiting for a frame to be sent before returning. 388 | * @return 1 if ack is received. 389 | */ 390 | uint8_t sendRDMGetCommand(UID target, uint16_t pid, uint8_t* info, uint8_t len); 391 | 392 | /*! 393 | * @brief send RDM_SET_COMMAND packet 394 | * @discussion Assumes that regular DMX was sending when method is called and 395 | * so restores sending, waiting for a frame to be sent before returning. 396 | * @return 1 if ack is received. 397 | */ 398 | uint8_t sendRDMSetCommand(UID target, uint16_t pid, uint8_t* info, uint8_t len); 399 | 400 | 401 | /*! 402 | * @brief send RDM_GET_COMMAND_RESPONSE with RDM_RESPONSE_TYPE_ACK 403 | * @discussion sends data (info) of length (len) 404 | */ 405 | void sendRDMGetResponse(UID target, uint16_t pid, uint8_t* info, uint8_t len); 406 | 407 | /*! 408 | * @brief send RDM_SET_COMMAND_RESPONSE/RDM_DISC_COMMAND_RESPONSE with RDM_RESPONSE_TYPE_ACK 409 | * @discussion PDL is zero 410 | */ 411 | void sendAckRDMResponse(uint8_t cmdclass, UID target, uint16_t pid); 412 | 413 | 414 | 415 | 416 | void sendMuteAckRDMResponse(uint8_t cmdclass, UID target, uint16_t pid); 417 | 418 | /*! 419 | * @brief send response to RDM_DISC_UNIQUE_BRANCH packet 420 | */ 421 | void sendRDMDiscoverBranchResponse( void ); 422 | 423 | /*! 424 | * @brief static member containing UID of this device 425 | * @discussion call LX8266DMX::THIS_DEVICE_ID.setBytes() or 426 | * ESP8266DMX.THIS_DEVICE_ID.setBytes() to change default 427 | */ 428 | static UID THIS_DEVICE_ID; 429 | 430 | private: 431 | 432 | /*! 433 | * @brief represents phase of sending dmx packet data/break/etc used to change baud settings 434 | */ 435 | uint8_t _dmx_send_state; 436 | 437 | /*! 438 | * @brief represents phase of sending dmx packet data/break/etc used to change baud settings 439 | */ 440 | volatile uint8_t _dmx_read_state; 441 | 442 | /*! 443 | * @brief true when ISR is enabled 444 | */ 445 | uint8_t _interrupt_status; 446 | 447 | /*! 448 | * @brief count of idle interrupts 449 | */ 450 | uint8_t _idle_count; 451 | 452 | /*! 453 | * @brief flag indicating RDM task should send dmx slots 454 | */ 455 | uint8_t _rdm_task_mode; 456 | 457 | /*! 458 | * @brief flag indicating RDM task should send dmx slots 459 | */ 460 | uint8_t _rdm_read_handled; 461 | 462 | /*! 463 | * @brief transaction number 464 | */ 465 | uint8_t _transaction; 466 | 467 | /*! 468 | * @brief maximum expected length of packet 469 | */ 470 | uint16_t _packet_length; 471 | 472 | /*! 473 | * @brief pin used to control direction of output driver chip 474 | */ 475 | uint8_t _direction_pin; 476 | 477 | /*! 478 | * @brief slot index indicating position of byte to be sent 479 | */ 480 | uint16_t _next_send_slot; 481 | 482 | /*! 483 | * @brief slot index indicating position of last byte received 484 | */ 485 | volatile uint16_t _next_read_slot; 486 | 487 | /*! 488 | * @brief number of dmx slots ~24 to 512 489 | */ 490 | volatile uint16_t _slots; 491 | 492 | /*! 493 | * @brief outgoing rdm packet length 494 | */ 495 | uint16_t _rdm_len; 496 | 497 | /*! 498 | * @brief Array of dmx data including start code 499 | */ 500 | uint8_t _dmxData[DMX_MAX_FRAME]; 501 | 502 | /*! 503 | * @brief Array of received bytes first byte is start code 504 | */ 505 | uint8_t _receivedData[DMX_MAX_FRAME]; 506 | 507 | /*! 508 | * @brief Array representing an rdm packet to be sent 509 | */ 510 | uint8_t _rdmPacket[RDM_MAX_FRAME]; 511 | 512 | /*! 513 | * @brief Array representing a received rdm packet 514 | */ 515 | uint8_t _rdmData[RDM_MAX_FRAME]; 516 | 517 | /*! 518 | * @brief Pointer to receive callback function 519 | */ 520 | LXRecvCallback _receive_callback; 521 | 522 | /*! 523 | * @brief Pointer to receive callback function 524 | */ 525 | LXRecvCallback _rdm_receive_callback; 526 | 527 | 528 | }; 529 | 530 | extern LX8266DMX ESP8266DMX; 531 | 532 | #endif // ifndef LX8266DMX_H -------------------------------------------------------------------------------- /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[13]; 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 | 28 | // utility functions 29 | void print64Bit(uint64_t n); 30 | uint64_t uid_bytes2long(uint8_t* b); 31 | uint64_t uid_bytes2long(const uint8_t* b); 32 | void uid_long2Bytes(uint64_t u, uint8_t* bytes); 33 | 34 | 35 | /*! 36 | @class UID 37 | @abstract 38 | A class to make it easier to handle RDM UIDs. 39 | (Based on Arduino IPAddress class using 6 bytes instead of 4) 40 | */ 41 | 42 | class UID: public Printable 43 | { 44 | private: 45 | uint8_t bytes[6]; 46 | 47 | public: 48 | /*! 49 | * @brief default constructor 50 | * @discussion 51 | */ 52 | UID( void ); 53 | 54 | /*! 55 | * @brief construct UID from 64bit integer 56 | * @discussion 57 | */ 58 | UID( uint64_t u ); 59 | /*! 60 | * @brief construct UID from 6 individual bytes 61 | */ 62 | UID(uint8_t m1, uint8_t m2, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4); 63 | /*! 64 | * @brief construct UID from pointer to 6 byte array 65 | */ 66 | UID(const uint8_t *address); 67 | 68 | // Overloaded equality operator 69 | bool operator==(const UID& addr) const; 70 | bool operator==(const uint8_t* addr) const; 71 | 72 | // Overloaded index operator to allow getting and setting individual bytes 73 | uint8_t operator[](int index) const { 74 | return bytes[index]; 75 | } 76 | uint8_t& operator[](int index) { 77 | return bytes[index]; 78 | } 79 | 80 | // Overloaded copy operators to allow initialisation of UID objects 81 | UID& operator=(const uint8_t *address); 82 | UID& operator=(UID address); 83 | 84 | /*! 85 | * @brief copy bytes of a UID into an array starting at index 86 | */ 87 | static void copyFromUID(UID id, uint8_t *address, uint16_t index=0) { 88 | memcpy(&address[index], id.bytes, sizeof(id.bytes)); 89 | } 90 | 91 | /*! 92 | * @brief copy bytes into a UID from an array starting at index 93 | */ 94 | static void copyToUID(UID id, uint8_t *address, uint16_t index=0) { 95 | memcpy(id.bytes, &address[index], sizeof(id.bytes)); 96 | } 97 | 98 | /*! 99 | * @brief set the bytes of this UID to the midpoint of 2 UIDs 100 | */ 101 | uint8_t becomeMidpoint(UID a, UID b); 102 | 103 | /*! 104 | * @brief pointer to the 6 byte array 105 | */ 106 | uint8_t* rawbytes( void ); 107 | 108 | /*! 109 | * @brief set the bytes of this UID with a 64bit integer 110 | */ 111 | void setBytes(uint64_t u); 112 | 113 | /*! 114 | * @brief set the bytes of this UID with another UID (copy) 115 | */ 116 | void setBytes(UID u); 117 | 118 | /*! 119 | * @brief set the bytes of this UID with individual mfg code and device ID bytes 120 | */ 121 | void setBytes(uint8_t m1, uint8_t m2, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4); 122 | 123 | //void setBytes(uint8_t* u); 124 | 125 | /*! 126 | * @brief UID as 64 bit integer 127 | */ 128 | uint64_t getValue ( void ); 129 | 130 | /*! 131 | * @brief print with formatting 132 | */ 133 | virtual size_t printTo(Print& p) const; 134 | 135 | /*! 136 | * @brief convert to formatted string 137 | */ 138 | String toString() const; 139 | }; 140 | 141 | /*! 142 | * @brief ALL_DEVICES wildcard UID 143 | */ 144 | const UID BROADCAST_ALL_DEVICES_ID(0xFFFFFFFFFFFF); 145 | 146 | #endif //RDMUID 147 | -------------------------------------------------------------------------------- /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 --------------------------------------------------------------------------------