From 8282ba3d45ec3874df045a4e9e4012eb69c57046 Mon Sep 17 00:00:00 2001 From: Liz Date: Thu, 27 Feb 2025 14:20:56 -0500 Subject: [PATCH 1/3] adding factory reset for sparkle motion --- .../.feather_esp32_v2.test.only | 0 .../Adafruit_Sparkle_Motion_FactoryTest.ino | 83 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/.feather_esp32_v2.test.only create mode 100644 Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/Adafruit_Sparkle_Motion_FactoryTest.ino diff --git a/Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/.feather_esp32_v2.test.only b/Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/.feather_esp32_v2.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/Adafruit_Sparkle_Motion_FactoryTest.ino b/Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/Adafruit_Sparkle_Motion_FactoryTest.ino new file mode 100644 index 000000000..f59400cc6 --- /dev/null +++ b/Factory_Tests/Adafruit_Sparkle_Motion_FactoryTest/Adafruit_Sparkle_Motion_FactoryTest.ino @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries +// +// SPDX-License-Identifier: MIT +#include +#include "WiFi.h" +#include +#include "ESP_I2S.h" +extern Adafruit_TestBed TB; + +// I2S pin definitions +const uint8_t I2S_SCK = 26; // BCLK +const uint8_t I2S_WS = 33; // LRCLK +const uint8_t I2S_DIN = 25; // DATA_IN +I2SClass i2s; + +// the setup routine runs once when you press reset: +void setup() { + Serial.begin(115200); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + i2s.setPins(I2S_SCK, I2S_WS, -1, I2S_DIN); + if (!i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT)) { + Serial.println("Failed to initialize I2S bus!"); + return; + } + // TestBed will handle the neopixel swirl for us + TB.neopixelPin = PIN_NEOPIXEL; + TB.neopixelNum = 1; + TB.begin(); + + // Set WiFi to station mode and disconnect from an AP if it was previously connected + WiFi.mode(WIFI_STA); + WiFi.disconnect(); +} + +// the loop routine runs over and over again forever: +uint8_t wheelColor=0; +void loop() { + if (wheelColor == 0) { + // Test I2C! + Serial.print("I2C port "); + TB.theWire = &Wire; + TB.printI2CBusScan(); + + // Test WiFi Scan! + // WiFi.scanNetworks will return the number of networks found + int n = WiFi.scanNetworks(); + Serial.print("WiFi AP scan done..."); + if (n == 0) { + Serial.println("no networks found"); + } else { + Serial.print(n); + Serial.println(" networks found"); + for (int i = 0; i < n; ++i) { + // Print SSID and RSSI for each network found + Serial.print(i + 1); + Serial.print(": "); + Serial.print(WiFi.SSID(i)); + Serial.print(" ("); + Serial.print(WiFi.RSSI(i)); + Serial.print(")"); + Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); + delay(10); + } + } + Serial.println(""); + for (int i=0; i < 5; i++) { + int32_t sample = i2s.read(); + if (sample >= 0){ + Serial.print("Amplitude: "); + Serial.println(sample); + + // Delay to avoid printing too quickly + delay(200); + } + } + } + + TB.setColor(TB.Wheel(wheelColor++)); // swirl NeoPixel + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); + + delay(5); +} From 16c13435ec160115889d18157babbbe9f3185912 Mon Sep 17 00:00:00 2001 From: Liz Date: Thu, 27 Feb 2025 14:49:25 -0500 Subject: [PATCH 2/3] add headers to dmx each example requires the header files since they are in different directories --- .../examples/DMX_Master/Conceptinetics.cpp | 1233 +++++++++++++++++ .../examples/DMX_Master/Conceptinetics.h | 383 +++++ .../Conceptinetics.cpp | 1233 +++++++++++++++++ .../DMX_Master_Manual_Break/Conceptinetics.h | 383 +++++ .../examples/DMX_Slave/Conceptinetics.cpp | 1233 +++++++++++++++++ .../examples/DMX_Slave/Conceptinetics.h | 383 +++++ .../Conceptinetics.cpp | 1233 +++++++++++++++++ .../DMX_Slave_Signal_Timeout/Conceptinetics.h | 383 +++++ .../examples/RDM_Slave/Conceptinetics.cpp | 1233 +++++++++++++++++ .../examples/RDM_Slave/Conceptinetics.h | 383 +++++ .../RDM_Slave_Eeprom/Conceptinetics.cpp | 1233 +++++++++++++++++ .../RDM_Slave_Eeprom/Conceptinetics.h | 383 +++++ 12 files changed, 9696 insertions(+) create mode 100644 DMX_NeoPixels/examples/DMX_Master/Conceptinetics.cpp create mode 100644 DMX_NeoPixels/examples/DMX_Master/Conceptinetics.h create mode 100644 DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.cpp create mode 100644 DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.h create mode 100644 DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.cpp create mode 100644 DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.h create mode 100644 DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.cpp create mode 100644 DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.h create mode 100644 DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.cpp create mode 100644 DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.h create mode 100644 DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.cpp create mode 100644 DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.h diff --git a/DMX_NeoPixels/examples/DMX_Master/Conceptinetics.cpp b/DMX_NeoPixels/examples/DMX_Master/Conceptinetics.cpp new file mode 100644 index 000000000..36ada5924 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master/Conceptinetics.cpp @@ -0,0 +1,1233 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.cpp - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD +*/ + + +#include "pins_arduino.h" +#include "Conceptinetics.h" + +#include +#include + +#include +#include + +#include + + +#if defined (USE_DMX_SERIAL_0) + + #if defined (USART__TXC_vect) + #define USART_TX USART__TXC_vect + #elif defined(USART_TX_vect) + #define USART_TX USART_TX_vect + #elif defined(USART0_TX_vect) + #define USART_TX USART0_TX_vect + #endif + + #if defined (USART__RXC_vect) + #define USART_RX USART__RXC_vect + #elif defined(USART_RX_vect) + #define USART_RX USART_RX_vect + #elif defined(USART0_RX_vect) + #define USART_RX USART0_RX_vect + #endif + + #if defined UDR + #define DMX_UDR UDR + #elif defined UDR0 + #define DMX_UDR UDR0 + #endif + + #if defined(UBRRH) && defined(UBRRL) + #define DMX_UBRRH UBRRH + #define DMX_UBRRL UBRRL + #elif defined(UBRR0H) && defined(UBRR0L) + #define DMX_UBRRH UBRR0H + #define DMX_UBRRL UBRR0L + #endif + + #if defined(UCSRA) + #define DMX_UCSRA UCSRA + #elif defined(UCSR0A) + #define DMX_UCSRA UCSR0A + #endif + + #if defined(UCSRB) + #define DMX_UCSRB UCSRB + #elif defined (UCSR0B) + #define DMX_UCSRB UCSR0B + #endif + + #if defined(TXEN) && defined(TXCIE) + #define DMX_TXEN TXEN + #define DMX_TXCIE TXCIE + #elif defined(TXEN0) && defined(TXCIE0) + #define DMX_TXEN TXEN0 + #define DMX_TXCIE TXCIE0 + #endif + + #if defined(RXEN) && defined(RXCIE) + #define DMX_RXEN RXEN + #define DMX_RXCIE RXCIE + #elif defined(RXEN0) && defined(RXCIE0) + #define DMX_RXEN RXEN0 + #define DMX_RXCIE RXCIE0 + #endif + + #if defined(FE) + #define DMX_FE FE + #elif defined(FE0) + #define DMX_FE FE0 + #endif + + #define RX_PIN 0 + #define TX_PIN 1 + +#elif defined (USE_DMX_SERIAL_1) + #define USART_RX USART1_RX_vect + #define USART_TX USART1_TX_vect + #define DMX_UDR UDR1 + #define DMX_UBRRH UBRR1H + #define DMX_UBRRL UBRR1L + #define DMX_UCSRA UCSR1A + #define DMX_UCSRB UCSR1B + #define DMX_TXEN TXEN1 + #define DMX_TXCIE TXCIE1 + #define DMX_RXEN RXEN1 + #define DMX_RXCIE RXCIE1 + #define DMX_FE FE1 + #define RX_PIN 19 + #define TX_PIN 18 +#elif defined (USE_DMX_SERIAL_2) + #define USART_RX USART2_RX_vect + #define USART_TX USART2_TX_vect + #define DMX_UDR UDR2 + #define DMX_UBRRH UBRR2H + #define DMX_UBRRL UBRR2L + #define DMX_UCSRA UCSR2A + #define DMX_UCSRB UCSR2B + #define DMX_TXEN TXEN2 + #define DMX_TXCIE TXCIE2 + #define DMX_RXEN RXEN2 + #define DMX_RXCIE RXCIE2 + #define DMX_FE FE2 + #define RX_PIN 17 + #define TX_PIN 16 +#elif defined (USE_DMX_SERIAL_3) + #define USART_RX USART3_RX_vect + #define USART_TX USART3_TX_vect + #define DMX_UDR UDR3 + #define DMX_UBRRH UBRR3H + #define DMX_UBRRL UBRR3L + #define DMX_UCSRA UCSR3A + #define DMX_UCSRB UCSR3B + #define DMX_TXEN TXEN3 + #define DMX_TXCIE TXCIE3 + #define DMX_RXEN RXEN3 + #define DMX_RXCIE RXCIE3 + #define DMX_FE FE3 + #define RX_PIN 14 + #define TX_PIN 15 +#endif + + +#define LOWBYTE(v) ((uint8_t) (v)) +#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8)) + +#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 ) + +namespace isr +{ + enum isrState + { + Idle, + Break, + DmxBreak, + DmxBreakManual, + DmxStartByte, + DmxRecordData, + DmxTransmitData, + RdmBreak, + RdmStartByte, + RdmRecordData, + RdmTransmitData, + RDMTransmitComplete, + }; + + enum isrMode + { + Disabled, + Receive, + DMXTransmit, + DMXTransmitManual, /* Manual break... */ + RDMTransmit, + RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */ + }; +}; + + +DMX_Master *__dmx_master; +DMX_Slave *__dmx_slave; +RDM_Responder *__rdm_responder; + +int8_t __re_pin; // R/W Pin on shield + +isr::isrState __isr_txState; // TX ISR state +isr::isrState __isr_rxState; // RX ISR state + + +void SetISRMode ( isr::isrMode ); + + +DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size ) +{ + m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) ); + + if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE ) + { + m_buffer = (uint8_t*) malloc ( buffer_size ); + if ( m_buffer != NULL ) + { + memset ( (void *)m_buffer, 0x0, buffer_size ); + m_bufferSize = buffer_size; + } + else + m_buffer = 0x0; + } + else + m_bufferSize = 0x0; + + *m_refcount++; +} + +DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer ) +{ + // Copy references and make sure the parent object does not dispose our + // buffer when deleted and we are still active + this->m_refcount = buffer.m_refcount; + (*this->m_refcount)++; + + this->m_buffer = buffer.m_buffer; + this->m_bufferSize = buffer.m_bufferSize; +} + +DMX_FrameBuffer::~DMX_FrameBuffer ( void ) +{ + // If we are the last object using the + // allocated buffer then free it together + // with the refcounter + if ( --(*m_refcount) == 0 ) + { + if ( m_buffer ) + free ( m_buffer ); + + free ( m_refcount ); + } +} + +uint16_t DMX_FrameBuffer::getBufferSize ( void ) +{ + return m_bufferSize; +} + + +uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if (index < m_bufferSize) + return m_buffer[index]; + else + return 0x0; +} + + +void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < m_bufferSize ) + m_buffer[index] = value; +} + + +void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start < m_bufferSize && end < m_bufferSize && start < end ) + memset ( (void *) &m_buffer[start], value, end-start+1 ); +} + +void DMX_FrameBuffer::clear ( void ) +{ + memset ( (void *) m_buffer, 0x0, m_bufferSize ); +} + +uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index ) +{ + return m_buffer[index]; +} + + +DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ) +: m_frameBuffer ( buffer ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin ) +: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::~DMX_Master ( void ) +{ + disable (); // Stop sending + __dmx_master = NULL; +} + +DMX_FrameBuffer &DMX_Master::getBuffer ( void ) +{ + return m_frameBuffer; // Return reference to frame buffer +} + +void DMX_Master::setStartCode ( uint8_t value ) +{ + m_frameBuffer[0] = value; // Set the first byte in our frame buffer +} + +void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value ) +{ + if ( channel > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotValue ( channel, value ); +} + +void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotRange ( start, end, value ); +} + + +void DMX_Master::enable ( void ) +{ + __dmx_master = this; + + if ( m_autoBreak ) + ::SetISRMode ( isr::DMXTransmit ); + else + ::SetISRMode ( isr::DMXTransmitManual ); +} + +void DMX_Master::disable ( void ) +{ + ::SetISRMode ( isr::Disabled ); + __dmx_master = NULL; // No active master +} + +void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; } +void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; } +uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; } + + +uint8_t DMX_Master::waitingBreak ( void ) +{ + return ( __isr_txState == isr::DmxBreakManual ); +} + +void DMX_Master::breakAndContinue ( uint8_t breakLength_us ) +{ + // Only execute if we are the controlling master object + if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual ) + { + pinMode ( TX_PIN, OUTPUT ); + digitalWrite ( TX_PIN, LOW ); // Begin BREAK + + for (uint8_t bl=0; bl(*this); +} + + uint8_t DMX_Slave::getChannelValue ( uint16_t channel ) +{ + return getSlotValue ( channel ); +} + + +uint16_t DMX_Slave::getStartAddress ( void ) +{ + return m_startAddress; +} + +void DMX_Slave::setStartAddress ( uint16_t addr ) +{ + m_startAddress = addr; +} + +void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) ) +{ + event_onFrameReceived = func; +} + + +bool DMX_Slave::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + // We could have received less channels then we + // expected.. but still is a complete frame + if (m_state == dmx::dmxData && event_onFrameReceived) + event_onFrameReceived (idx); + + m_state = dmx::dmxStartByte; + } + + switch ( m_state ) + { + case dmx::dmxStartByte: + setSlotValue ( 0, val ); // Store start code + idx = m_startAddress; + m_state = dmx::dmxWaitStartAddress; + + case dmx::dmxWaitStartAddress: + if ( --idx == 0 ) + m_state = dmx::dmxData; + break; + + case dmx::dmxData: + if ( idx++ < getBufferSize() ) + setSlotValue ( idx, val ); + else + { + m_state = dmx::dmxFrameReady; + + // If a onFrameReceived callback is register... + if (event_onFrameReceived) + event_onFrameReceived (idx-2); + + rval = true; + } + break; + } + + return rval; +} + + +uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); } + +uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if ( index < sizeof ( m_msg ) ) + return m_msg.d[index]; + else + return 0x0; +} + + +void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < sizeof ( m_msg ) ) + m_msg.d[index] = value; +} + +void RDM_FrameBuffer::clear ( void ) +{ + memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) ); + m_state = rdm::rdmUnknown; +} + +bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + m_state = rdm::rdmStartByte; + m_csRecv.checksum = (uint16_t) 0x0000; + idx = 0; + } + + // Prevent buffer overflow for large messages + if (idx >= sizeof(m_msg)) + return true; + + switch ( m_state ) + { + case rdm::rdmStartByte: + m_msg.startCode = val; + m_state = rdm::rdmSubStartCode; + break; + + case rdm::rdmSubStartCode: + if ( val != 0x01 ) + { + rval = true; // Stop processing data + break; + } + + m_msg.subStartCode = val; + m_state = rdm::rdmMessageLength; + break; + + case rdm::rdmMessageLength: + m_msg.msgLength = val; + m_state = rdm::rdmData; + m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum + idx = 3; // buffer index for next byte + break; + + case rdm::rdmData: + m_msg.d[idx++] = val; + m_csRecv.checksum += val; + if ( idx >= m_msg.msgLength ) + m_state = rdm::rdmChecksumHigh; + break; + + case rdm::rdmChecksumHigh: + m_csRecv.csh = val; + m_state = rdm::rdmChecksumLow; + + break; + + case rdm::rdmChecksumLow: + m_csRecv.csl = val; + + if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum) + { + m_state = rdm::rdmFrameReady; + + // valid checksum ... start processing + processFrame (); + } + + m_state = rdm::rdmUnknown; + rval = true; + break; + }; + + return rval; +} + +bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first ) +{ + static uint16_t idx; + static uint16_t cs; + bool rval = false; + + + if ( first ) + { + m_state = rdm::rdmData; + cs = 0x0; + idx = 0; + } + + switch ( m_state ) + { + case rdm::rdmData: + cs += m_msg.d[idx]; + *udr = m_msg.d[idx++]; + if ( idx >= m_msg.msgLength ) + { + m_state = rdm::rdmChecksumHigh; + } + break; + + case rdm::rdmChecksumHigh: + *udr = (cs >> 8); + m_state = rdm::rdmChecksumLow; + break; + + case rdm::rdmChecksumLow: + *udr = (cs & 0xff); + m_state = rdm::rdmUnknown; + rval = true; + break; + + } + + return rval; +} + + +void (*RDM_Responder::event_onIdentifyDevice)(bool); +void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t); +void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t); +void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t); + +// +// slave parameter is only used to ensure a slave object is present before +// initializing the rdm responder class +// +RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, DMX_Slave &slave ) +: RDM_FrameBuffer ( ), + m_Personalities (1), // Available personlities + m_Personality (1) // Default personality eq 1. +{ + __rdm_responder = this; + m_devid.Initialize ( m, d1, d2, d3, d4 ); + + // Default software version id = 0x00000000 + memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 ); + + // Rdm responder is disabled by default + m_rdmStatus.enabled = false; +} + +RDM_Responder::~RDM_Responder ( void ) +{ + __rdm_responder = NULL; +} + +void RDM_Responder::onIdentifyDevice ( void (*func)(bool) ) +{ + event_onIdentifyDevice = func; +} + +void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ) +{ + event_onDeviceLabelChanged = func; +} + +void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) ) +{ + event_onDMXStartAddressChanged = func; +} + +void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) ) +{ + event_onDMXPersonalityChanged = func; +} + +void RDM_Responder::setDeviceLabel ( const char *label, size_t len ) +{ + if ( len > RDM_MAX_DEVICELABEL_LENGTH ) + len = RDM_MAX_DEVICELABEL_LENGTH; + + memcpy ( (void *)m_deviceLabel, (void *)label, len ); +} + +#define UID_0 0x12 //ESTA device ID +#define UID_1 0x34 +#define UID_2 0x56 +#define UID_3 0x88 +#define UID_4 0x00 +#define UID_5 0x00 + +#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5) + +void RDM_Responder::repondDiscUniqueBranch ( void ) +{ + #if defined(UCSRB) + UCSRB = (1<>8) | 0xaa; + response [21] = (cs>>8) | 0x55; + response [22] = (cs&0xff) | 0xaa; + response [23] = (cs&0xff) | 0x55; + + + // Table 3-2 ANSI_E1-20-2010 <2ms + _delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + + // Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with + // section 3.2.3 + // Set shield to transmit mode (turn arround) + digitalWrite ( __re_pin, HIGH ); + + + for ( int i=0; i<24; i++ ) + { + // Wait until data register is empty + #if defined (UCSR0A) && defined (UDRE0) + while((UCSR0A & (1 <(m_msg.PD); + + pd->protocolVersionMajor = 0x01; + pd->protocolVersionMinor = 0x00; + pd->deviceModelId = BSWAP_16(m_DeviceModelId); + pd->ProductCategory = BSWAP_16(m_ProductCategory); + memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 ); + pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte + pd->DMX512CurrentPersonality = m_Personality; + pd->DMX512NumberPersonalities = m_Personalities; + pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress()); + + pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library + pd->SensorCount = 0x0; // Sensors are not yet supported + + m_msg.PDL = sizeof (RDM__DeviceInfoPD); +} + +const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics"; + +void RDM_Responder::processFrame ( void ) +{ + // If packet is a general broadcast + if ( + m_msg.dstUid.isBroadcast (m_devid.m_id) || + m_devid == m_msg.dstUid + ) + { + // Set default response type + m_msg.portId = rdm::ResponseTypeAck; + + switch ( BSWAP_16(m_msg.PID) ) + { + case rdm::DiscUniqueBranch: + // Check if we are inside the given unique branch... + if ( !m_rdmStatus.mute && + reinterpret_cast(m_msg.PD)->lbound < m_devid && + reinterpret_cast(m_msg.PD)->hbound > m_devid ) + { + // Discovery messages are responded with data only and no breaks + repondDiscUniqueBranch (); + } + break; + + case rdm::DiscMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = true; + break; + + case rdm::DiscUnMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = false; + break; + + case rdm::SupportedParameters: + // + // Temporary solution... this will become dynamic + // in a later version... + // + m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB + m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB + + m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality); + m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality); + + m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel); + m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel); + + m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel); + m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel); + + m_msg.PDL = 0x6; + break; + + // Only for manufacturer specific parameters + // case rdm::ParameterDescription: + // break; + + case rdm::DeviceInfo: + if ( m_msg.CC == rdm::GetCommand ) + populateDeviceInfo (); + break; + + case rdm::DmxStartAddress: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ()); + m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ()); + m_msg.PDL = 0x2; + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + __dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + m_msg.PDL = 0x0; + + if ( event_onDMXStartAddressChanged ) + event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + } + break; + + case rdm::DmxPersonality: + if ( m_msg.CC == rdm::GetCommand ) + { + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personality; + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; + m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + m_Personality = reinterpret_cast + (m_msg.PD)->DMX512Personality; + m_msg.PDL = 0x0; + + if ( event_onDMXPersonalityChanged ) + event_onDMXPersonalityChanged ( m_Personality ); + } + break; + + case rdm::IdentifyDevice: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0); + m_msg.PDL = 0x1; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + // Look into first byte to see whether identification + // is turned on or off + m_rdmStatus.ident = m_msg.PD[0] ? true : false; + if ( event_onIdentifyDevice ) + event_onIdentifyDevice ( m_rdmStatus.ident ); + + m_msg.PDL = 0x0; + } + break; + + case rdm::ManufacturerLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) ); + m_msg.PDL = sizeof ( ManufacturerLabel_P ); + } + break; + + case rdm::DeviceLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 ); + m_msg.PDL = 32; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + memset ( (void*) m_deviceLabel, ' ', 32 ); + memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) ); + m_msg.PDL = 0; + + // Notify application + if ( event_onDeviceLabelChanged ) + event_onDeviceLabelChanged ( m_deviceLabel, 32 ); + } + break; + + + default: + // Unknown parameter ID response + m_msg.portId = rdm::ResponseTypeNackReason; + m_msg.PD[0] = rdm::UnknownPid; + m_msg.PD[1] = 0x0; + m_msg.PDL = 0x2; + break; + }; + } + + // + // Only respond if this this message + // was destined to us only + if ( m_msg.dstUid == m_devid ) + { + m_msg.startCode = RDM_START_CODE; + m_msg.subStartCode = 0x01; + m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL; + m_msg.msgCount = 0; + + /* + switch ( m_msg.msg.CC ) + { + case rdm::DiscoveryCommand: + m_msg.msg.CC = rdm::DiscoveryCommandResponse; + break; + case rdm::GetCommand: + m_msg.msg.CC = rdm::GetCommandResponse; + break; + case rdm::SetCommand: + m_msg.msg.CC = rdm::SetCommandResponse; + break; + } + */ + /* Above replaced by next line */ + m_msg.CC++; + + m_msg.dstUid.copy ( m_msg.srcUid ); + m_msg.srcUid.copy ( m_devid ); + + //_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + SetISRMode ( isr::RDMTransmit ); + + } +} + + +void SetISRMode ( isr::isrMode mode ) +{ + uint8_t readEnable; + +#if defined(USE_DMX_SERIAL_0) + #if defined(UCSRB) && defined (UCSRC) + UCSRC |= (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Prepare before kicking off ISR + //DMX_UDR = 0x0; + __isr_rxState = isr::Idle; + readEnable = LOW; + DMX_UCSRB = (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UDR = 0x0; + DMX_UCSRB = 0x0; + readEnable = HIGH; + __isr_txState = isr::DmxBreakManual; + break; + + case isr::RDMTransmit: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + //DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + //DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UCSRB = (1< -1) + digitalWrite ( __re_pin, readEnable ); + +} + +// +// TX UART (DMX Transmission ISR) +// +ISR (USART_TX) +{ + static uint16_t current_slot; + + switch ( __isr_txState ) + { + case isr::DmxBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + if ( __isr_txState == isr::DmxBreak ) + __isr_txState = isr::DmxStartByte; + + break; + + case isr::DmxStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + current_slot = 0; + DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ]; + __isr_txState = isr::DmxTransmitData; + break; + + + case isr::DmxTransmitData: + // NOTE: we always send full frames of 513 bytes, this will bring us + // close to 40 frames / sec with no interslot delays + #ifdef DMX_IBG + _delay_us (DMX_IBG); + #endif + + DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ ); + + // Send 512 channels + if ( current_slot >= DMX_MAX_FRAMESIZE ) + { + if ( __dmx_master->autoBreakEnabled () ) + __isr_txState = isr::DmxBreak; + else + SetISRMode ( isr::DMXTransmitManual ); + } + + break; + +/* case isr::RdmBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + __isr_txState = isr::RdmStartByte; + + break; +*/ + + case isr::RdmStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Write start byte + __rdm_responder->fetchOutgoing ( &DMX_UDR, true ); + __isr_txState = isr::RdmTransmitData; + + break; + + case isr::RdmTransmitData: + // Write rest of data + if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) ) + __isr_txState = isr::RDMTransmitComplete; + break; + + case isr::RDMTransmitComplete: + SetISRMode ( isr::Receive ); // Start waitin for new data + __isr_txState = isr::Idle; // No tx state + break; + + } +} + + + +// +// RX UART (DMX Reception ISR) +// +ISR (USART_RX) +{ + uint8_t usart_state = DMX_UCSRA; + uint8_t usart_data = DMX_UDR; + + // + // Check for framing error and reset if found + // A framing most likely* indicate a break in our ocasion + // + if ( usart_state & (1<processIncoming ( usart_data, true ); + __isr_rxState = isr::DmxRecordData; + } + else if ( __rdm_responder && + usart_data == RDM_START_CODE && + __rdm_responder->m_rdmStatus.enabled ) + { + // __rdm_responder->clear (); + __rdm_responder->processIncoming ( usart_data, true ); + __isr_rxState = isr::RdmRecordData; + } + else + { + __isr_rxState = isr::Idle; + } + break; + + // Process DMX Data + case isr::DmxRecordData: + if ( __dmx_slave->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + // Process RDM Data + case isr::RdmRecordData: + if ( __rdm_responder->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + } + +} + diff --git a/DMX_NeoPixels/examples/DMX_Master/Conceptinetics.h b/DMX_NeoPixels/examples/DMX_Master/Conceptinetics.h new file mode 100644 index 000000000..0f03da619 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master/Conceptinetics.h @@ -0,0 +1,383 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.h - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino / Genuino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino Leonardo using a CTC-DRA-13-R2 ISOLATED DMX-RDM SHIELD + + - CTC-DRA-10-1 and CTC-DRA-10-R2 is the Non-isolated costs effective DMX-RDM shield +*/ + + +#ifndef CONCEPTINETICS_H_ +#define CONCEPTINETICS_H_ + +#include +#include + +#include "Rdm_Uid.h" +#include "Rdm_Defines.h" + +#define DMX_MAX_FRAMESIZE 513 // Startbyte + 512 Slots +#define DMX_MIN_FRAMESIZE 2 // Startbyte + 1 Slot + +#define DMX_MAX_FRAMECHANNELS 512 // Maxmim number of channer per frame + +#define DMX_STARTCODE_SIZE 1 // Size of startcode in bytes + +#define DMX_START_CODE 0x0 // Start code for a DMX frame +#define RDM_START_CODE 0xcc // Start code for a RDM frame + +// Uncomment to enable Inter slot delay ) (avg < 76uSec) ... +// mimum is zero according to specification +// #define DMX_IBG 10 // Inter slot time + +// Speed your Arduino is running on in Hz. +#define F_OSC 16000000UL + +// DMX Baudrate, this should be 250000 +#define DMX_BAUD_RATE 250000 + +// The baudrate used to automaticly generate a break within +// your ISR.. make it lower to generate longer breaks +//#define DMX_BREAK_RATE 99900 + +// 2017, Feb 28: Set to appox 176us +#define DMX_BREAK_RATE 49950 + +// Tabel 3-2 ANSI_E1-20-2010 +// Minimum time to allow the datalink to 'turn arround' +#define MIN_RESPONDER_PACKET_SPACING_USEC 176 /*176*/ + +// Define which serial port to use as DMX port, only one can be +// selected at the time by uncommenting one of the following +// lines +#define USE_DMX_SERIAL_0 +//#define USE_DMX_SERIAL_1 +//#define USE_DMX_SERIAL_2 +//#define USE_DMX_SERIAL_3 + +namespace dmx +{ + enum dmxState + { + dmxUnknown, + dmxStartByte, + dmxWaitStartAddress, + dmxData, + dmxFrameReady, + }; +}; + +namespace rdm +{ + enum rdmState + { + rdmUnknown, + rdmStartByte, + rdmSubStartCode, + rdmMessageLength, + rdmData, + rdmChecksumHigh, + rdmChecksumLow, + rdmFrameReady, + }; +}; + +struct IFrameBuffer +{ + virtual uint16_t getBufferSize ( void ) = 0; + + virtual uint8_t getSlotValue ( uint16_t index ) = 0; + virtual void setSlotValue ( uint16_t index, uint8_t value ) = 0; +}; + +class DMX_FrameBuffer : IFrameBuffer +{ + public: + // + // Constructor buffersize = 1-513 + // + DMX_FrameBuffer ( uint16_t buffer_size ); + DMX_FrameBuffer ( DMX_FrameBuffer &buffer ); + ~DMX_FrameBuffer ( void ); + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void setSlotRange ( uint16_t start, uint16_t end, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + private: + + uint8_t *m_refcount; + uint16_t m_bufferSize; + uint8_t *m_buffer; +}; + + +// +// DMX Master controller +// +class DMX_Master +{ + public: + // Run the DMX master from a pre allocated frame buffer which + // you have fully under your own control + DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ); + + // Run the DMX master by giving a predefined maximum number of + // channels to support + DMX_Master ( uint16_t maxChannel, int readEnablePin ); + + ~DMX_Master ( void ); + + void enable ( void ); // Start transmitting + void disable ( void ); // Stop transmitting + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + // Update channel values + void setChannelValue ( uint16_t channel, uint8_t value ); + void setChannelRange ( uint16_t start, uint16_t end, uint8_t value ); + + public: + // + // Manual control over the break period + // + void setAutoBreakMode ( void ); // Generated from ISR + void setManualBreakMode ( void ); // Generate manually + + uint8_t autoBreakEnabled ( void ); + + // We are waiting for a manual break to be generated + uint8_t waitingBreak ( void ); + + // Generate break and start transmission of frame + void breakAndContinue ( uint8_t breakLength_us = 100 ); + + + protected: + void setStartCode ( uint8_t value ); + + + private: + DMX_FrameBuffer m_frameBuffer; + uint8_t m_autoBreak; +}; + + +// +// DMX Slave controller +// +class DMX_Slave : public DMX_FrameBuffer +{ + public: + DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin = -1 ); + + // nrChannels is the consecutive DMX512 slots required + // to operate this slave device + DMX_Slave ( uint16_t nrChannels, int readEnablePin = -1 ); + + ~DMX_Slave ( void ); + + void enable ( void ); // Enable receiver + void disable ( void ); // Disable receiver + + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + uint8_t getChannelValue ( uint16_t channel ); + + uint16_t getStartAddress ( void ); + void setStartAddress ( uint16_t ); + + + // Process incoming byte from USART + bool processIncoming ( uint8_t val, bool first = false ); + + // Register on receive complete callback in case + // of time critical applications + void onReceiveComplete ( void (*func)(unsigned short) ); + + protected: + + + private: + uint16_t m_startAddress; // Slave start address + dmx::dmxState m_state; + + static void (*event_onFrameReceived)(unsigned short channelsReceived); +}; + + +class RDM_FrameBuffer : public IFrameBuffer +{ + public: + // + // Constructor + // + RDM_FrameBuffer ( void ) {}; + ~RDM_FrameBuffer ( void ) {}; + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + public: // functions to provide access from USART + // Process incoming byte from USART, + // returns false when no more data is accepted + bool processIncoming ( uint8_t val, bool first = false ); + + // Process outgoing byte to USART + // returns false when no more data is available + bool fetchOutgoing ( volatile uint8_t *udr, bool first = false ); + + protected: + // Process received frame + virtual void processFrame ( void ) = 0; + + //private: + protected: + rdm::rdmState m_state; // State for pushing the message in + RDM_Message m_msg; + RDM_Checksum m_csRecv; // Checksum received in rdm message +}; + +// +// RDM_Responder +// +class RDM_Responder : public RDM_FrameBuffer +{ + public: + // + // m = manufacturer id (16bits) + // d1-d4 = device id (32bits) + // + RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, DMX_Slave &slave); + ~RDM_Responder ( void ); + + void setDeviceInfo + ( + uint16_t deviceModelId, + rdm::RdmProductCategory productCategory, + uint8_t personalities = 1, + uint8_t personality = 1 + ) + { + m_DeviceModelId = deviceModelId; + m_ProductCategory = productCategory; + m_Personalities = personalities; + m_Personality = personality; + }; + + // + // Set vendor software version id + // + // v1 = MOST SIGNIFICANT + // v2... + // v3... + // v4 = LEAST SIGNIFICANT + // + void setSoftwareVersionId ( uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4 ) + { + m_SoftwareVersionId[0] = v1; + m_SoftwareVersionId[1] = v2; + m_SoftwareVersionId[2] = v3; + m_SoftwareVersionId[3] = v4; + } + + // Currently no sensors and subdevices supported + // void AddSensor ( void ); + // void AddSubDevice ( void ); + + uint8_t getPersonality ( void ) { return m_Personality; }; + void setPersonality ( uint8_t personality ) { m_Personality = personality; }; + + // Register on identify device event handler + void onIdentifyDevice ( void (*func)(bool) ); + void onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ); + void onDMXStartAddressChanged ( void (*func) (uint16_t) ); + void onDMXPersonalityChanged ( void (*func) (uint8_t) ); + + + // Set the device label + void setDeviceLabel ( const char *label, size_t len ); + + // Enable, Disable rdm responder + void enable ( void ) { m_rdmStatus.enabled = true; m_rdmStatus.mute = false; }; + void disable ( void ) { m_rdmStatus.enabled = false; }; + + union + { + uint8_t raw; + struct + { + uint8_t mute:1; + uint8_t ident:1; + uint8_t enabled:1; // Rdm responder enable/disable + }; + } m_rdmStatus; + + + protected: + virtual void processFrame ( void ); + + // Discovery to unque brach packets only requires + // the data part of the packet to be transmitted + // without breaks or header + void repondDiscUniqueBranch ( void ); + + // Helpers for generating response packets which + // have larger datafields + void populateDeviceInfo ( void ); + + private: + RDM_Uid m_devid; // Holds our unique device ID + uint8_t m_Personalities; // The total number of supported personalities + uint8_t m_Personality; // The currently active personality + uint16_t m_DeviceModelId; + uint8_t m_SoftwareVersionId[4]; // 32 bit Software version + rdm::RdmProductCategory m_ProductCategory; + + char m_deviceLabel[32]; // Device label + + static void (*event_onIdentifyDevice)(bool); + static void (*event_onDeviceLabelChanged)(const char*, uint8_t); + static void (*event_onDMXStartAddressChanged)(uint16_t); + static void (*event_onDMXPersonalityChanged)(uint8_t); +}; + + +#endif /* CONCEPTINETICS_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.cpp b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.cpp new file mode 100644 index 000000000..36ada5924 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.cpp @@ -0,0 +1,1233 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.cpp - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD +*/ + + +#include "pins_arduino.h" +#include "Conceptinetics.h" + +#include +#include + +#include +#include + +#include + + +#if defined (USE_DMX_SERIAL_0) + + #if defined (USART__TXC_vect) + #define USART_TX USART__TXC_vect + #elif defined(USART_TX_vect) + #define USART_TX USART_TX_vect + #elif defined(USART0_TX_vect) + #define USART_TX USART0_TX_vect + #endif + + #if defined (USART__RXC_vect) + #define USART_RX USART__RXC_vect + #elif defined(USART_RX_vect) + #define USART_RX USART_RX_vect + #elif defined(USART0_RX_vect) + #define USART_RX USART0_RX_vect + #endif + + #if defined UDR + #define DMX_UDR UDR + #elif defined UDR0 + #define DMX_UDR UDR0 + #endif + + #if defined(UBRRH) && defined(UBRRL) + #define DMX_UBRRH UBRRH + #define DMX_UBRRL UBRRL + #elif defined(UBRR0H) && defined(UBRR0L) + #define DMX_UBRRH UBRR0H + #define DMX_UBRRL UBRR0L + #endif + + #if defined(UCSRA) + #define DMX_UCSRA UCSRA + #elif defined(UCSR0A) + #define DMX_UCSRA UCSR0A + #endif + + #if defined(UCSRB) + #define DMX_UCSRB UCSRB + #elif defined (UCSR0B) + #define DMX_UCSRB UCSR0B + #endif + + #if defined(TXEN) && defined(TXCIE) + #define DMX_TXEN TXEN + #define DMX_TXCIE TXCIE + #elif defined(TXEN0) && defined(TXCIE0) + #define DMX_TXEN TXEN0 + #define DMX_TXCIE TXCIE0 + #endif + + #if defined(RXEN) && defined(RXCIE) + #define DMX_RXEN RXEN + #define DMX_RXCIE RXCIE + #elif defined(RXEN0) && defined(RXCIE0) + #define DMX_RXEN RXEN0 + #define DMX_RXCIE RXCIE0 + #endif + + #if defined(FE) + #define DMX_FE FE + #elif defined(FE0) + #define DMX_FE FE0 + #endif + + #define RX_PIN 0 + #define TX_PIN 1 + +#elif defined (USE_DMX_SERIAL_1) + #define USART_RX USART1_RX_vect + #define USART_TX USART1_TX_vect + #define DMX_UDR UDR1 + #define DMX_UBRRH UBRR1H + #define DMX_UBRRL UBRR1L + #define DMX_UCSRA UCSR1A + #define DMX_UCSRB UCSR1B + #define DMX_TXEN TXEN1 + #define DMX_TXCIE TXCIE1 + #define DMX_RXEN RXEN1 + #define DMX_RXCIE RXCIE1 + #define DMX_FE FE1 + #define RX_PIN 19 + #define TX_PIN 18 +#elif defined (USE_DMX_SERIAL_2) + #define USART_RX USART2_RX_vect + #define USART_TX USART2_TX_vect + #define DMX_UDR UDR2 + #define DMX_UBRRH UBRR2H + #define DMX_UBRRL UBRR2L + #define DMX_UCSRA UCSR2A + #define DMX_UCSRB UCSR2B + #define DMX_TXEN TXEN2 + #define DMX_TXCIE TXCIE2 + #define DMX_RXEN RXEN2 + #define DMX_RXCIE RXCIE2 + #define DMX_FE FE2 + #define RX_PIN 17 + #define TX_PIN 16 +#elif defined (USE_DMX_SERIAL_3) + #define USART_RX USART3_RX_vect + #define USART_TX USART3_TX_vect + #define DMX_UDR UDR3 + #define DMX_UBRRH UBRR3H + #define DMX_UBRRL UBRR3L + #define DMX_UCSRA UCSR3A + #define DMX_UCSRB UCSR3B + #define DMX_TXEN TXEN3 + #define DMX_TXCIE TXCIE3 + #define DMX_RXEN RXEN3 + #define DMX_RXCIE RXCIE3 + #define DMX_FE FE3 + #define RX_PIN 14 + #define TX_PIN 15 +#endif + + +#define LOWBYTE(v) ((uint8_t) (v)) +#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8)) + +#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 ) + +namespace isr +{ + enum isrState + { + Idle, + Break, + DmxBreak, + DmxBreakManual, + DmxStartByte, + DmxRecordData, + DmxTransmitData, + RdmBreak, + RdmStartByte, + RdmRecordData, + RdmTransmitData, + RDMTransmitComplete, + }; + + enum isrMode + { + Disabled, + Receive, + DMXTransmit, + DMXTransmitManual, /* Manual break... */ + RDMTransmit, + RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */ + }; +}; + + +DMX_Master *__dmx_master; +DMX_Slave *__dmx_slave; +RDM_Responder *__rdm_responder; + +int8_t __re_pin; // R/W Pin on shield + +isr::isrState __isr_txState; // TX ISR state +isr::isrState __isr_rxState; // RX ISR state + + +void SetISRMode ( isr::isrMode ); + + +DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size ) +{ + m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) ); + + if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE ) + { + m_buffer = (uint8_t*) malloc ( buffer_size ); + if ( m_buffer != NULL ) + { + memset ( (void *)m_buffer, 0x0, buffer_size ); + m_bufferSize = buffer_size; + } + else + m_buffer = 0x0; + } + else + m_bufferSize = 0x0; + + *m_refcount++; +} + +DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer ) +{ + // Copy references and make sure the parent object does not dispose our + // buffer when deleted and we are still active + this->m_refcount = buffer.m_refcount; + (*this->m_refcount)++; + + this->m_buffer = buffer.m_buffer; + this->m_bufferSize = buffer.m_bufferSize; +} + +DMX_FrameBuffer::~DMX_FrameBuffer ( void ) +{ + // If we are the last object using the + // allocated buffer then free it together + // with the refcounter + if ( --(*m_refcount) == 0 ) + { + if ( m_buffer ) + free ( m_buffer ); + + free ( m_refcount ); + } +} + +uint16_t DMX_FrameBuffer::getBufferSize ( void ) +{ + return m_bufferSize; +} + + +uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if (index < m_bufferSize) + return m_buffer[index]; + else + return 0x0; +} + + +void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < m_bufferSize ) + m_buffer[index] = value; +} + + +void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start < m_bufferSize && end < m_bufferSize && start < end ) + memset ( (void *) &m_buffer[start], value, end-start+1 ); +} + +void DMX_FrameBuffer::clear ( void ) +{ + memset ( (void *) m_buffer, 0x0, m_bufferSize ); +} + +uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index ) +{ + return m_buffer[index]; +} + + +DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ) +: m_frameBuffer ( buffer ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin ) +: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::~DMX_Master ( void ) +{ + disable (); // Stop sending + __dmx_master = NULL; +} + +DMX_FrameBuffer &DMX_Master::getBuffer ( void ) +{ + return m_frameBuffer; // Return reference to frame buffer +} + +void DMX_Master::setStartCode ( uint8_t value ) +{ + m_frameBuffer[0] = value; // Set the first byte in our frame buffer +} + +void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value ) +{ + if ( channel > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotValue ( channel, value ); +} + +void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotRange ( start, end, value ); +} + + +void DMX_Master::enable ( void ) +{ + __dmx_master = this; + + if ( m_autoBreak ) + ::SetISRMode ( isr::DMXTransmit ); + else + ::SetISRMode ( isr::DMXTransmitManual ); +} + +void DMX_Master::disable ( void ) +{ + ::SetISRMode ( isr::Disabled ); + __dmx_master = NULL; // No active master +} + +void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; } +void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; } +uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; } + + +uint8_t DMX_Master::waitingBreak ( void ) +{ + return ( __isr_txState == isr::DmxBreakManual ); +} + +void DMX_Master::breakAndContinue ( uint8_t breakLength_us ) +{ + // Only execute if we are the controlling master object + if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual ) + { + pinMode ( TX_PIN, OUTPUT ); + digitalWrite ( TX_PIN, LOW ); // Begin BREAK + + for (uint8_t bl=0; bl(*this); +} + + uint8_t DMX_Slave::getChannelValue ( uint16_t channel ) +{ + return getSlotValue ( channel ); +} + + +uint16_t DMX_Slave::getStartAddress ( void ) +{ + return m_startAddress; +} + +void DMX_Slave::setStartAddress ( uint16_t addr ) +{ + m_startAddress = addr; +} + +void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) ) +{ + event_onFrameReceived = func; +} + + +bool DMX_Slave::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + // We could have received less channels then we + // expected.. but still is a complete frame + if (m_state == dmx::dmxData && event_onFrameReceived) + event_onFrameReceived (idx); + + m_state = dmx::dmxStartByte; + } + + switch ( m_state ) + { + case dmx::dmxStartByte: + setSlotValue ( 0, val ); // Store start code + idx = m_startAddress; + m_state = dmx::dmxWaitStartAddress; + + case dmx::dmxWaitStartAddress: + if ( --idx == 0 ) + m_state = dmx::dmxData; + break; + + case dmx::dmxData: + if ( idx++ < getBufferSize() ) + setSlotValue ( idx, val ); + else + { + m_state = dmx::dmxFrameReady; + + // If a onFrameReceived callback is register... + if (event_onFrameReceived) + event_onFrameReceived (idx-2); + + rval = true; + } + break; + } + + return rval; +} + + +uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); } + +uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if ( index < sizeof ( m_msg ) ) + return m_msg.d[index]; + else + return 0x0; +} + + +void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < sizeof ( m_msg ) ) + m_msg.d[index] = value; +} + +void RDM_FrameBuffer::clear ( void ) +{ + memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) ); + m_state = rdm::rdmUnknown; +} + +bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + m_state = rdm::rdmStartByte; + m_csRecv.checksum = (uint16_t) 0x0000; + idx = 0; + } + + // Prevent buffer overflow for large messages + if (idx >= sizeof(m_msg)) + return true; + + switch ( m_state ) + { + case rdm::rdmStartByte: + m_msg.startCode = val; + m_state = rdm::rdmSubStartCode; + break; + + case rdm::rdmSubStartCode: + if ( val != 0x01 ) + { + rval = true; // Stop processing data + break; + } + + m_msg.subStartCode = val; + m_state = rdm::rdmMessageLength; + break; + + case rdm::rdmMessageLength: + m_msg.msgLength = val; + m_state = rdm::rdmData; + m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum + idx = 3; // buffer index for next byte + break; + + case rdm::rdmData: + m_msg.d[idx++] = val; + m_csRecv.checksum += val; + if ( idx >= m_msg.msgLength ) + m_state = rdm::rdmChecksumHigh; + break; + + case rdm::rdmChecksumHigh: + m_csRecv.csh = val; + m_state = rdm::rdmChecksumLow; + + break; + + case rdm::rdmChecksumLow: + m_csRecv.csl = val; + + if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum) + { + m_state = rdm::rdmFrameReady; + + // valid checksum ... start processing + processFrame (); + } + + m_state = rdm::rdmUnknown; + rval = true; + break; + }; + + return rval; +} + +bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first ) +{ + static uint16_t idx; + static uint16_t cs; + bool rval = false; + + + if ( first ) + { + m_state = rdm::rdmData; + cs = 0x0; + idx = 0; + } + + switch ( m_state ) + { + case rdm::rdmData: + cs += m_msg.d[idx]; + *udr = m_msg.d[idx++]; + if ( idx >= m_msg.msgLength ) + { + m_state = rdm::rdmChecksumHigh; + } + break; + + case rdm::rdmChecksumHigh: + *udr = (cs >> 8); + m_state = rdm::rdmChecksumLow; + break; + + case rdm::rdmChecksumLow: + *udr = (cs & 0xff); + m_state = rdm::rdmUnknown; + rval = true; + break; + + } + + return rval; +} + + +void (*RDM_Responder::event_onIdentifyDevice)(bool); +void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t); +void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t); +void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t); + +// +// slave parameter is only used to ensure a slave object is present before +// initializing the rdm responder class +// +RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, DMX_Slave &slave ) +: RDM_FrameBuffer ( ), + m_Personalities (1), // Available personlities + m_Personality (1) // Default personality eq 1. +{ + __rdm_responder = this; + m_devid.Initialize ( m, d1, d2, d3, d4 ); + + // Default software version id = 0x00000000 + memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 ); + + // Rdm responder is disabled by default + m_rdmStatus.enabled = false; +} + +RDM_Responder::~RDM_Responder ( void ) +{ + __rdm_responder = NULL; +} + +void RDM_Responder::onIdentifyDevice ( void (*func)(bool) ) +{ + event_onIdentifyDevice = func; +} + +void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ) +{ + event_onDeviceLabelChanged = func; +} + +void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) ) +{ + event_onDMXStartAddressChanged = func; +} + +void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) ) +{ + event_onDMXPersonalityChanged = func; +} + +void RDM_Responder::setDeviceLabel ( const char *label, size_t len ) +{ + if ( len > RDM_MAX_DEVICELABEL_LENGTH ) + len = RDM_MAX_DEVICELABEL_LENGTH; + + memcpy ( (void *)m_deviceLabel, (void *)label, len ); +} + +#define UID_0 0x12 //ESTA device ID +#define UID_1 0x34 +#define UID_2 0x56 +#define UID_3 0x88 +#define UID_4 0x00 +#define UID_5 0x00 + +#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5) + +void RDM_Responder::repondDiscUniqueBranch ( void ) +{ + #if defined(UCSRB) + UCSRB = (1<>8) | 0xaa; + response [21] = (cs>>8) | 0x55; + response [22] = (cs&0xff) | 0xaa; + response [23] = (cs&0xff) | 0x55; + + + // Table 3-2 ANSI_E1-20-2010 <2ms + _delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + + // Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with + // section 3.2.3 + // Set shield to transmit mode (turn arround) + digitalWrite ( __re_pin, HIGH ); + + + for ( int i=0; i<24; i++ ) + { + // Wait until data register is empty + #if defined (UCSR0A) && defined (UDRE0) + while((UCSR0A & (1 <(m_msg.PD); + + pd->protocolVersionMajor = 0x01; + pd->protocolVersionMinor = 0x00; + pd->deviceModelId = BSWAP_16(m_DeviceModelId); + pd->ProductCategory = BSWAP_16(m_ProductCategory); + memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 ); + pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte + pd->DMX512CurrentPersonality = m_Personality; + pd->DMX512NumberPersonalities = m_Personalities; + pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress()); + + pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library + pd->SensorCount = 0x0; // Sensors are not yet supported + + m_msg.PDL = sizeof (RDM__DeviceInfoPD); +} + +const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics"; + +void RDM_Responder::processFrame ( void ) +{ + // If packet is a general broadcast + if ( + m_msg.dstUid.isBroadcast (m_devid.m_id) || + m_devid == m_msg.dstUid + ) + { + // Set default response type + m_msg.portId = rdm::ResponseTypeAck; + + switch ( BSWAP_16(m_msg.PID) ) + { + case rdm::DiscUniqueBranch: + // Check if we are inside the given unique branch... + if ( !m_rdmStatus.mute && + reinterpret_cast(m_msg.PD)->lbound < m_devid && + reinterpret_cast(m_msg.PD)->hbound > m_devid ) + { + // Discovery messages are responded with data only and no breaks + repondDiscUniqueBranch (); + } + break; + + case rdm::DiscMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = true; + break; + + case rdm::DiscUnMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = false; + break; + + case rdm::SupportedParameters: + // + // Temporary solution... this will become dynamic + // in a later version... + // + m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB + m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB + + m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality); + m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality); + + m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel); + m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel); + + m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel); + m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel); + + m_msg.PDL = 0x6; + break; + + // Only for manufacturer specific parameters + // case rdm::ParameterDescription: + // break; + + case rdm::DeviceInfo: + if ( m_msg.CC == rdm::GetCommand ) + populateDeviceInfo (); + break; + + case rdm::DmxStartAddress: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ()); + m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ()); + m_msg.PDL = 0x2; + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + __dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + m_msg.PDL = 0x0; + + if ( event_onDMXStartAddressChanged ) + event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + } + break; + + case rdm::DmxPersonality: + if ( m_msg.CC == rdm::GetCommand ) + { + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personality; + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; + m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + m_Personality = reinterpret_cast + (m_msg.PD)->DMX512Personality; + m_msg.PDL = 0x0; + + if ( event_onDMXPersonalityChanged ) + event_onDMXPersonalityChanged ( m_Personality ); + } + break; + + case rdm::IdentifyDevice: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0); + m_msg.PDL = 0x1; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + // Look into first byte to see whether identification + // is turned on or off + m_rdmStatus.ident = m_msg.PD[0] ? true : false; + if ( event_onIdentifyDevice ) + event_onIdentifyDevice ( m_rdmStatus.ident ); + + m_msg.PDL = 0x0; + } + break; + + case rdm::ManufacturerLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) ); + m_msg.PDL = sizeof ( ManufacturerLabel_P ); + } + break; + + case rdm::DeviceLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 ); + m_msg.PDL = 32; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + memset ( (void*) m_deviceLabel, ' ', 32 ); + memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) ); + m_msg.PDL = 0; + + // Notify application + if ( event_onDeviceLabelChanged ) + event_onDeviceLabelChanged ( m_deviceLabel, 32 ); + } + break; + + + default: + // Unknown parameter ID response + m_msg.portId = rdm::ResponseTypeNackReason; + m_msg.PD[0] = rdm::UnknownPid; + m_msg.PD[1] = 0x0; + m_msg.PDL = 0x2; + break; + }; + } + + // + // Only respond if this this message + // was destined to us only + if ( m_msg.dstUid == m_devid ) + { + m_msg.startCode = RDM_START_CODE; + m_msg.subStartCode = 0x01; + m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL; + m_msg.msgCount = 0; + + /* + switch ( m_msg.msg.CC ) + { + case rdm::DiscoveryCommand: + m_msg.msg.CC = rdm::DiscoveryCommandResponse; + break; + case rdm::GetCommand: + m_msg.msg.CC = rdm::GetCommandResponse; + break; + case rdm::SetCommand: + m_msg.msg.CC = rdm::SetCommandResponse; + break; + } + */ + /* Above replaced by next line */ + m_msg.CC++; + + m_msg.dstUid.copy ( m_msg.srcUid ); + m_msg.srcUid.copy ( m_devid ); + + //_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + SetISRMode ( isr::RDMTransmit ); + + } +} + + +void SetISRMode ( isr::isrMode mode ) +{ + uint8_t readEnable; + +#if defined(USE_DMX_SERIAL_0) + #if defined(UCSRB) && defined (UCSRC) + UCSRC |= (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Prepare before kicking off ISR + //DMX_UDR = 0x0; + __isr_rxState = isr::Idle; + readEnable = LOW; + DMX_UCSRB = (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UDR = 0x0; + DMX_UCSRB = 0x0; + readEnable = HIGH; + __isr_txState = isr::DmxBreakManual; + break; + + case isr::RDMTransmit: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + //DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + //DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UCSRB = (1< -1) + digitalWrite ( __re_pin, readEnable ); + +} + +// +// TX UART (DMX Transmission ISR) +// +ISR (USART_TX) +{ + static uint16_t current_slot; + + switch ( __isr_txState ) + { + case isr::DmxBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + if ( __isr_txState == isr::DmxBreak ) + __isr_txState = isr::DmxStartByte; + + break; + + case isr::DmxStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + current_slot = 0; + DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ]; + __isr_txState = isr::DmxTransmitData; + break; + + + case isr::DmxTransmitData: + // NOTE: we always send full frames of 513 bytes, this will bring us + // close to 40 frames / sec with no interslot delays + #ifdef DMX_IBG + _delay_us (DMX_IBG); + #endif + + DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ ); + + // Send 512 channels + if ( current_slot >= DMX_MAX_FRAMESIZE ) + { + if ( __dmx_master->autoBreakEnabled () ) + __isr_txState = isr::DmxBreak; + else + SetISRMode ( isr::DMXTransmitManual ); + } + + break; + +/* case isr::RdmBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + __isr_txState = isr::RdmStartByte; + + break; +*/ + + case isr::RdmStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Write start byte + __rdm_responder->fetchOutgoing ( &DMX_UDR, true ); + __isr_txState = isr::RdmTransmitData; + + break; + + case isr::RdmTransmitData: + // Write rest of data + if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) ) + __isr_txState = isr::RDMTransmitComplete; + break; + + case isr::RDMTransmitComplete: + SetISRMode ( isr::Receive ); // Start waitin for new data + __isr_txState = isr::Idle; // No tx state + break; + + } +} + + + +// +// RX UART (DMX Reception ISR) +// +ISR (USART_RX) +{ + uint8_t usart_state = DMX_UCSRA; + uint8_t usart_data = DMX_UDR; + + // + // Check for framing error and reset if found + // A framing most likely* indicate a break in our ocasion + // + if ( usart_state & (1<processIncoming ( usart_data, true ); + __isr_rxState = isr::DmxRecordData; + } + else if ( __rdm_responder && + usart_data == RDM_START_CODE && + __rdm_responder->m_rdmStatus.enabled ) + { + // __rdm_responder->clear (); + __rdm_responder->processIncoming ( usart_data, true ); + __isr_rxState = isr::RdmRecordData; + } + else + { + __isr_rxState = isr::Idle; + } + break; + + // Process DMX Data + case isr::DmxRecordData: + if ( __dmx_slave->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + // Process RDM Data + case isr::RdmRecordData: + if ( __rdm_responder->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + } + +} + diff --git a/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.h b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.h new file mode 100644 index 000000000..0f03da619 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Conceptinetics.h @@ -0,0 +1,383 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.h - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino / Genuino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino Leonardo using a CTC-DRA-13-R2 ISOLATED DMX-RDM SHIELD + + - CTC-DRA-10-1 and CTC-DRA-10-R2 is the Non-isolated costs effective DMX-RDM shield +*/ + + +#ifndef CONCEPTINETICS_H_ +#define CONCEPTINETICS_H_ + +#include +#include + +#include "Rdm_Uid.h" +#include "Rdm_Defines.h" + +#define DMX_MAX_FRAMESIZE 513 // Startbyte + 512 Slots +#define DMX_MIN_FRAMESIZE 2 // Startbyte + 1 Slot + +#define DMX_MAX_FRAMECHANNELS 512 // Maxmim number of channer per frame + +#define DMX_STARTCODE_SIZE 1 // Size of startcode in bytes + +#define DMX_START_CODE 0x0 // Start code for a DMX frame +#define RDM_START_CODE 0xcc // Start code for a RDM frame + +// Uncomment to enable Inter slot delay ) (avg < 76uSec) ... +// mimum is zero according to specification +// #define DMX_IBG 10 // Inter slot time + +// Speed your Arduino is running on in Hz. +#define F_OSC 16000000UL + +// DMX Baudrate, this should be 250000 +#define DMX_BAUD_RATE 250000 + +// The baudrate used to automaticly generate a break within +// your ISR.. make it lower to generate longer breaks +//#define DMX_BREAK_RATE 99900 + +// 2017, Feb 28: Set to appox 176us +#define DMX_BREAK_RATE 49950 + +// Tabel 3-2 ANSI_E1-20-2010 +// Minimum time to allow the datalink to 'turn arround' +#define MIN_RESPONDER_PACKET_SPACING_USEC 176 /*176*/ + +// Define which serial port to use as DMX port, only one can be +// selected at the time by uncommenting one of the following +// lines +#define USE_DMX_SERIAL_0 +//#define USE_DMX_SERIAL_1 +//#define USE_DMX_SERIAL_2 +//#define USE_DMX_SERIAL_3 + +namespace dmx +{ + enum dmxState + { + dmxUnknown, + dmxStartByte, + dmxWaitStartAddress, + dmxData, + dmxFrameReady, + }; +}; + +namespace rdm +{ + enum rdmState + { + rdmUnknown, + rdmStartByte, + rdmSubStartCode, + rdmMessageLength, + rdmData, + rdmChecksumHigh, + rdmChecksumLow, + rdmFrameReady, + }; +}; + +struct IFrameBuffer +{ + virtual uint16_t getBufferSize ( void ) = 0; + + virtual uint8_t getSlotValue ( uint16_t index ) = 0; + virtual void setSlotValue ( uint16_t index, uint8_t value ) = 0; +}; + +class DMX_FrameBuffer : IFrameBuffer +{ + public: + // + // Constructor buffersize = 1-513 + // + DMX_FrameBuffer ( uint16_t buffer_size ); + DMX_FrameBuffer ( DMX_FrameBuffer &buffer ); + ~DMX_FrameBuffer ( void ); + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void setSlotRange ( uint16_t start, uint16_t end, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + private: + + uint8_t *m_refcount; + uint16_t m_bufferSize; + uint8_t *m_buffer; +}; + + +// +// DMX Master controller +// +class DMX_Master +{ + public: + // Run the DMX master from a pre allocated frame buffer which + // you have fully under your own control + DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ); + + // Run the DMX master by giving a predefined maximum number of + // channels to support + DMX_Master ( uint16_t maxChannel, int readEnablePin ); + + ~DMX_Master ( void ); + + void enable ( void ); // Start transmitting + void disable ( void ); // Stop transmitting + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + // Update channel values + void setChannelValue ( uint16_t channel, uint8_t value ); + void setChannelRange ( uint16_t start, uint16_t end, uint8_t value ); + + public: + // + // Manual control over the break period + // + void setAutoBreakMode ( void ); // Generated from ISR + void setManualBreakMode ( void ); // Generate manually + + uint8_t autoBreakEnabled ( void ); + + // We are waiting for a manual break to be generated + uint8_t waitingBreak ( void ); + + // Generate break and start transmission of frame + void breakAndContinue ( uint8_t breakLength_us = 100 ); + + + protected: + void setStartCode ( uint8_t value ); + + + private: + DMX_FrameBuffer m_frameBuffer; + uint8_t m_autoBreak; +}; + + +// +// DMX Slave controller +// +class DMX_Slave : public DMX_FrameBuffer +{ + public: + DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin = -1 ); + + // nrChannels is the consecutive DMX512 slots required + // to operate this slave device + DMX_Slave ( uint16_t nrChannels, int readEnablePin = -1 ); + + ~DMX_Slave ( void ); + + void enable ( void ); // Enable receiver + void disable ( void ); // Disable receiver + + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + uint8_t getChannelValue ( uint16_t channel ); + + uint16_t getStartAddress ( void ); + void setStartAddress ( uint16_t ); + + + // Process incoming byte from USART + bool processIncoming ( uint8_t val, bool first = false ); + + // Register on receive complete callback in case + // of time critical applications + void onReceiveComplete ( void (*func)(unsigned short) ); + + protected: + + + private: + uint16_t m_startAddress; // Slave start address + dmx::dmxState m_state; + + static void (*event_onFrameReceived)(unsigned short channelsReceived); +}; + + +class RDM_FrameBuffer : public IFrameBuffer +{ + public: + // + // Constructor + // + RDM_FrameBuffer ( void ) {}; + ~RDM_FrameBuffer ( void ) {}; + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + public: // functions to provide access from USART + // Process incoming byte from USART, + // returns false when no more data is accepted + bool processIncoming ( uint8_t val, bool first = false ); + + // Process outgoing byte to USART + // returns false when no more data is available + bool fetchOutgoing ( volatile uint8_t *udr, bool first = false ); + + protected: + // Process received frame + virtual void processFrame ( void ) = 0; + + //private: + protected: + rdm::rdmState m_state; // State for pushing the message in + RDM_Message m_msg; + RDM_Checksum m_csRecv; // Checksum received in rdm message +}; + +// +// RDM_Responder +// +class RDM_Responder : public RDM_FrameBuffer +{ + public: + // + // m = manufacturer id (16bits) + // d1-d4 = device id (32bits) + // + RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, DMX_Slave &slave); + ~RDM_Responder ( void ); + + void setDeviceInfo + ( + uint16_t deviceModelId, + rdm::RdmProductCategory productCategory, + uint8_t personalities = 1, + uint8_t personality = 1 + ) + { + m_DeviceModelId = deviceModelId; + m_ProductCategory = productCategory; + m_Personalities = personalities; + m_Personality = personality; + }; + + // + // Set vendor software version id + // + // v1 = MOST SIGNIFICANT + // v2... + // v3... + // v4 = LEAST SIGNIFICANT + // + void setSoftwareVersionId ( uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4 ) + { + m_SoftwareVersionId[0] = v1; + m_SoftwareVersionId[1] = v2; + m_SoftwareVersionId[2] = v3; + m_SoftwareVersionId[3] = v4; + } + + // Currently no sensors and subdevices supported + // void AddSensor ( void ); + // void AddSubDevice ( void ); + + uint8_t getPersonality ( void ) { return m_Personality; }; + void setPersonality ( uint8_t personality ) { m_Personality = personality; }; + + // Register on identify device event handler + void onIdentifyDevice ( void (*func)(bool) ); + void onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ); + void onDMXStartAddressChanged ( void (*func) (uint16_t) ); + void onDMXPersonalityChanged ( void (*func) (uint8_t) ); + + + // Set the device label + void setDeviceLabel ( const char *label, size_t len ); + + // Enable, Disable rdm responder + void enable ( void ) { m_rdmStatus.enabled = true; m_rdmStatus.mute = false; }; + void disable ( void ) { m_rdmStatus.enabled = false; }; + + union + { + uint8_t raw; + struct + { + uint8_t mute:1; + uint8_t ident:1; + uint8_t enabled:1; // Rdm responder enable/disable + }; + } m_rdmStatus; + + + protected: + virtual void processFrame ( void ); + + // Discovery to unque brach packets only requires + // the data part of the packet to be transmitted + // without breaks or header + void repondDiscUniqueBranch ( void ); + + // Helpers for generating response packets which + // have larger datafields + void populateDeviceInfo ( void ); + + private: + RDM_Uid m_devid; // Holds our unique device ID + uint8_t m_Personalities; // The total number of supported personalities + uint8_t m_Personality; // The currently active personality + uint16_t m_DeviceModelId; + uint8_t m_SoftwareVersionId[4]; // 32 bit Software version + rdm::RdmProductCategory m_ProductCategory; + + char m_deviceLabel[32]; // Device label + + static void (*event_onIdentifyDevice)(bool); + static void (*event_onDeviceLabelChanged)(const char*, uint8_t); + static void (*event_onDMXStartAddressChanged)(uint16_t); + static void (*event_onDMXPersonalityChanged)(uint8_t); +}; + + +#endif /* CONCEPTINETICS_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.cpp b/DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.cpp new file mode 100644 index 000000000..36ada5924 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.cpp @@ -0,0 +1,1233 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.cpp - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD +*/ + + +#include "pins_arduino.h" +#include "Conceptinetics.h" + +#include +#include + +#include +#include + +#include + + +#if defined (USE_DMX_SERIAL_0) + + #if defined (USART__TXC_vect) + #define USART_TX USART__TXC_vect + #elif defined(USART_TX_vect) + #define USART_TX USART_TX_vect + #elif defined(USART0_TX_vect) + #define USART_TX USART0_TX_vect + #endif + + #if defined (USART__RXC_vect) + #define USART_RX USART__RXC_vect + #elif defined(USART_RX_vect) + #define USART_RX USART_RX_vect + #elif defined(USART0_RX_vect) + #define USART_RX USART0_RX_vect + #endif + + #if defined UDR + #define DMX_UDR UDR + #elif defined UDR0 + #define DMX_UDR UDR0 + #endif + + #if defined(UBRRH) && defined(UBRRL) + #define DMX_UBRRH UBRRH + #define DMX_UBRRL UBRRL + #elif defined(UBRR0H) && defined(UBRR0L) + #define DMX_UBRRH UBRR0H + #define DMX_UBRRL UBRR0L + #endif + + #if defined(UCSRA) + #define DMX_UCSRA UCSRA + #elif defined(UCSR0A) + #define DMX_UCSRA UCSR0A + #endif + + #if defined(UCSRB) + #define DMX_UCSRB UCSRB + #elif defined (UCSR0B) + #define DMX_UCSRB UCSR0B + #endif + + #if defined(TXEN) && defined(TXCIE) + #define DMX_TXEN TXEN + #define DMX_TXCIE TXCIE + #elif defined(TXEN0) && defined(TXCIE0) + #define DMX_TXEN TXEN0 + #define DMX_TXCIE TXCIE0 + #endif + + #if defined(RXEN) && defined(RXCIE) + #define DMX_RXEN RXEN + #define DMX_RXCIE RXCIE + #elif defined(RXEN0) && defined(RXCIE0) + #define DMX_RXEN RXEN0 + #define DMX_RXCIE RXCIE0 + #endif + + #if defined(FE) + #define DMX_FE FE + #elif defined(FE0) + #define DMX_FE FE0 + #endif + + #define RX_PIN 0 + #define TX_PIN 1 + +#elif defined (USE_DMX_SERIAL_1) + #define USART_RX USART1_RX_vect + #define USART_TX USART1_TX_vect + #define DMX_UDR UDR1 + #define DMX_UBRRH UBRR1H + #define DMX_UBRRL UBRR1L + #define DMX_UCSRA UCSR1A + #define DMX_UCSRB UCSR1B + #define DMX_TXEN TXEN1 + #define DMX_TXCIE TXCIE1 + #define DMX_RXEN RXEN1 + #define DMX_RXCIE RXCIE1 + #define DMX_FE FE1 + #define RX_PIN 19 + #define TX_PIN 18 +#elif defined (USE_DMX_SERIAL_2) + #define USART_RX USART2_RX_vect + #define USART_TX USART2_TX_vect + #define DMX_UDR UDR2 + #define DMX_UBRRH UBRR2H + #define DMX_UBRRL UBRR2L + #define DMX_UCSRA UCSR2A + #define DMX_UCSRB UCSR2B + #define DMX_TXEN TXEN2 + #define DMX_TXCIE TXCIE2 + #define DMX_RXEN RXEN2 + #define DMX_RXCIE RXCIE2 + #define DMX_FE FE2 + #define RX_PIN 17 + #define TX_PIN 16 +#elif defined (USE_DMX_SERIAL_3) + #define USART_RX USART3_RX_vect + #define USART_TX USART3_TX_vect + #define DMX_UDR UDR3 + #define DMX_UBRRH UBRR3H + #define DMX_UBRRL UBRR3L + #define DMX_UCSRA UCSR3A + #define DMX_UCSRB UCSR3B + #define DMX_TXEN TXEN3 + #define DMX_TXCIE TXCIE3 + #define DMX_RXEN RXEN3 + #define DMX_RXCIE RXCIE3 + #define DMX_FE FE3 + #define RX_PIN 14 + #define TX_PIN 15 +#endif + + +#define LOWBYTE(v) ((uint8_t) (v)) +#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8)) + +#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 ) + +namespace isr +{ + enum isrState + { + Idle, + Break, + DmxBreak, + DmxBreakManual, + DmxStartByte, + DmxRecordData, + DmxTransmitData, + RdmBreak, + RdmStartByte, + RdmRecordData, + RdmTransmitData, + RDMTransmitComplete, + }; + + enum isrMode + { + Disabled, + Receive, + DMXTransmit, + DMXTransmitManual, /* Manual break... */ + RDMTransmit, + RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */ + }; +}; + + +DMX_Master *__dmx_master; +DMX_Slave *__dmx_slave; +RDM_Responder *__rdm_responder; + +int8_t __re_pin; // R/W Pin on shield + +isr::isrState __isr_txState; // TX ISR state +isr::isrState __isr_rxState; // RX ISR state + + +void SetISRMode ( isr::isrMode ); + + +DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size ) +{ + m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) ); + + if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE ) + { + m_buffer = (uint8_t*) malloc ( buffer_size ); + if ( m_buffer != NULL ) + { + memset ( (void *)m_buffer, 0x0, buffer_size ); + m_bufferSize = buffer_size; + } + else + m_buffer = 0x0; + } + else + m_bufferSize = 0x0; + + *m_refcount++; +} + +DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer ) +{ + // Copy references and make sure the parent object does not dispose our + // buffer when deleted and we are still active + this->m_refcount = buffer.m_refcount; + (*this->m_refcount)++; + + this->m_buffer = buffer.m_buffer; + this->m_bufferSize = buffer.m_bufferSize; +} + +DMX_FrameBuffer::~DMX_FrameBuffer ( void ) +{ + // If we are the last object using the + // allocated buffer then free it together + // with the refcounter + if ( --(*m_refcount) == 0 ) + { + if ( m_buffer ) + free ( m_buffer ); + + free ( m_refcount ); + } +} + +uint16_t DMX_FrameBuffer::getBufferSize ( void ) +{ + return m_bufferSize; +} + + +uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if (index < m_bufferSize) + return m_buffer[index]; + else + return 0x0; +} + + +void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < m_bufferSize ) + m_buffer[index] = value; +} + + +void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start < m_bufferSize && end < m_bufferSize && start < end ) + memset ( (void *) &m_buffer[start], value, end-start+1 ); +} + +void DMX_FrameBuffer::clear ( void ) +{ + memset ( (void *) m_buffer, 0x0, m_bufferSize ); +} + +uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index ) +{ + return m_buffer[index]; +} + + +DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ) +: m_frameBuffer ( buffer ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin ) +: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::~DMX_Master ( void ) +{ + disable (); // Stop sending + __dmx_master = NULL; +} + +DMX_FrameBuffer &DMX_Master::getBuffer ( void ) +{ + return m_frameBuffer; // Return reference to frame buffer +} + +void DMX_Master::setStartCode ( uint8_t value ) +{ + m_frameBuffer[0] = value; // Set the first byte in our frame buffer +} + +void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value ) +{ + if ( channel > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotValue ( channel, value ); +} + +void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotRange ( start, end, value ); +} + + +void DMX_Master::enable ( void ) +{ + __dmx_master = this; + + if ( m_autoBreak ) + ::SetISRMode ( isr::DMXTransmit ); + else + ::SetISRMode ( isr::DMXTransmitManual ); +} + +void DMX_Master::disable ( void ) +{ + ::SetISRMode ( isr::Disabled ); + __dmx_master = NULL; // No active master +} + +void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; } +void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; } +uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; } + + +uint8_t DMX_Master::waitingBreak ( void ) +{ + return ( __isr_txState == isr::DmxBreakManual ); +} + +void DMX_Master::breakAndContinue ( uint8_t breakLength_us ) +{ + // Only execute if we are the controlling master object + if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual ) + { + pinMode ( TX_PIN, OUTPUT ); + digitalWrite ( TX_PIN, LOW ); // Begin BREAK + + for (uint8_t bl=0; bl(*this); +} + + uint8_t DMX_Slave::getChannelValue ( uint16_t channel ) +{ + return getSlotValue ( channel ); +} + + +uint16_t DMX_Slave::getStartAddress ( void ) +{ + return m_startAddress; +} + +void DMX_Slave::setStartAddress ( uint16_t addr ) +{ + m_startAddress = addr; +} + +void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) ) +{ + event_onFrameReceived = func; +} + + +bool DMX_Slave::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + // We could have received less channels then we + // expected.. but still is a complete frame + if (m_state == dmx::dmxData && event_onFrameReceived) + event_onFrameReceived (idx); + + m_state = dmx::dmxStartByte; + } + + switch ( m_state ) + { + case dmx::dmxStartByte: + setSlotValue ( 0, val ); // Store start code + idx = m_startAddress; + m_state = dmx::dmxWaitStartAddress; + + case dmx::dmxWaitStartAddress: + if ( --idx == 0 ) + m_state = dmx::dmxData; + break; + + case dmx::dmxData: + if ( idx++ < getBufferSize() ) + setSlotValue ( idx, val ); + else + { + m_state = dmx::dmxFrameReady; + + // If a onFrameReceived callback is register... + if (event_onFrameReceived) + event_onFrameReceived (idx-2); + + rval = true; + } + break; + } + + return rval; +} + + +uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); } + +uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if ( index < sizeof ( m_msg ) ) + return m_msg.d[index]; + else + return 0x0; +} + + +void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < sizeof ( m_msg ) ) + m_msg.d[index] = value; +} + +void RDM_FrameBuffer::clear ( void ) +{ + memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) ); + m_state = rdm::rdmUnknown; +} + +bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + m_state = rdm::rdmStartByte; + m_csRecv.checksum = (uint16_t) 0x0000; + idx = 0; + } + + // Prevent buffer overflow for large messages + if (idx >= sizeof(m_msg)) + return true; + + switch ( m_state ) + { + case rdm::rdmStartByte: + m_msg.startCode = val; + m_state = rdm::rdmSubStartCode; + break; + + case rdm::rdmSubStartCode: + if ( val != 0x01 ) + { + rval = true; // Stop processing data + break; + } + + m_msg.subStartCode = val; + m_state = rdm::rdmMessageLength; + break; + + case rdm::rdmMessageLength: + m_msg.msgLength = val; + m_state = rdm::rdmData; + m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum + idx = 3; // buffer index for next byte + break; + + case rdm::rdmData: + m_msg.d[idx++] = val; + m_csRecv.checksum += val; + if ( idx >= m_msg.msgLength ) + m_state = rdm::rdmChecksumHigh; + break; + + case rdm::rdmChecksumHigh: + m_csRecv.csh = val; + m_state = rdm::rdmChecksumLow; + + break; + + case rdm::rdmChecksumLow: + m_csRecv.csl = val; + + if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum) + { + m_state = rdm::rdmFrameReady; + + // valid checksum ... start processing + processFrame (); + } + + m_state = rdm::rdmUnknown; + rval = true; + break; + }; + + return rval; +} + +bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first ) +{ + static uint16_t idx; + static uint16_t cs; + bool rval = false; + + + if ( first ) + { + m_state = rdm::rdmData; + cs = 0x0; + idx = 0; + } + + switch ( m_state ) + { + case rdm::rdmData: + cs += m_msg.d[idx]; + *udr = m_msg.d[idx++]; + if ( idx >= m_msg.msgLength ) + { + m_state = rdm::rdmChecksumHigh; + } + break; + + case rdm::rdmChecksumHigh: + *udr = (cs >> 8); + m_state = rdm::rdmChecksumLow; + break; + + case rdm::rdmChecksumLow: + *udr = (cs & 0xff); + m_state = rdm::rdmUnknown; + rval = true; + break; + + } + + return rval; +} + + +void (*RDM_Responder::event_onIdentifyDevice)(bool); +void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t); +void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t); +void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t); + +// +// slave parameter is only used to ensure a slave object is present before +// initializing the rdm responder class +// +RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, DMX_Slave &slave ) +: RDM_FrameBuffer ( ), + m_Personalities (1), // Available personlities + m_Personality (1) // Default personality eq 1. +{ + __rdm_responder = this; + m_devid.Initialize ( m, d1, d2, d3, d4 ); + + // Default software version id = 0x00000000 + memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 ); + + // Rdm responder is disabled by default + m_rdmStatus.enabled = false; +} + +RDM_Responder::~RDM_Responder ( void ) +{ + __rdm_responder = NULL; +} + +void RDM_Responder::onIdentifyDevice ( void (*func)(bool) ) +{ + event_onIdentifyDevice = func; +} + +void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ) +{ + event_onDeviceLabelChanged = func; +} + +void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) ) +{ + event_onDMXStartAddressChanged = func; +} + +void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) ) +{ + event_onDMXPersonalityChanged = func; +} + +void RDM_Responder::setDeviceLabel ( const char *label, size_t len ) +{ + if ( len > RDM_MAX_DEVICELABEL_LENGTH ) + len = RDM_MAX_DEVICELABEL_LENGTH; + + memcpy ( (void *)m_deviceLabel, (void *)label, len ); +} + +#define UID_0 0x12 //ESTA device ID +#define UID_1 0x34 +#define UID_2 0x56 +#define UID_3 0x88 +#define UID_4 0x00 +#define UID_5 0x00 + +#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5) + +void RDM_Responder::repondDiscUniqueBranch ( void ) +{ + #if defined(UCSRB) + UCSRB = (1<>8) | 0xaa; + response [21] = (cs>>8) | 0x55; + response [22] = (cs&0xff) | 0xaa; + response [23] = (cs&0xff) | 0x55; + + + // Table 3-2 ANSI_E1-20-2010 <2ms + _delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + + // Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with + // section 3.2.3 + // Set shield to transmit mode (turn arround) + digitalWrite ( __re_pin, HIGH ); + + + for ( int i=0; i<24; i++ ) + { + // Wait until data register is empty + #if defined (UCSR0A) && defined (UDRE0) + while((UCSR0A & (1 <(m_msg.PD); + + pd->protocolVersionMajor = 0x01; + pd->protocolVersionMinor = 0x00; + pd->deviceModelId = BSWAP_16(m_DeviceModelId); + pd->ProductCategory = BSWAP_16(m_ProductCategory); + memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 ); + pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte + pd->DMX512CurrentPersonality = m_Personality; + pd->DMX512NumberPersonalities = m_Personalities; + pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress()); + + pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library + pd->SensorCount = 0x0; // Sensors are not yet supported + + m_msg.PDL = sizeof (RDM__DeviceInfoPD); +} + +const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics"; + +void RDM_Responder::processFrame ( void ) +{ + // If packet is a general broadcast + if ( + m_msg.dstUid.isBroadcast (m_devid.m_id) || + m_devid == m_msg.dstUid + ) + { + // Set default response type + m_msg.portId = rdm::ResponseTypeAck; + + switch ( BSWAP_16(m_msg.PID) ) + { + case rdm::DiscUniqueBranch: + // Check if we are inside the given unique branch... + if ( !m_rdmStatus.mute && + reinterpret_cast(m_msg.PD)->lbound < m_devid && + reinterpret_cast(m_msg.PD)->hbound > m_devid ) + { + // Discovery messages are responded with data only and no breaks + repondDiscUniqueBranch (); + } + break; + + case rdm::DiscMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = true; + break; + + case rdm::DiscUnMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = false; + break; + + case rdm::SupportedParameters: + // + // Temporary solution... this will become dynamic + // in a later version... + // + m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB + m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB + + m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality); + m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality); + + m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel); + m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel); + + m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel); + m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel); + + m_msg.PDL = 0x6; + break; + + // Only for manufacturer specific parameters + // case rdm::ParameterDescription: + // break; + + case rdm::DeviceInfo: + if ( m_msg.CC == rdm::GetCommand ) + populateDeviceInfo (); + break; + + case rdm::DmxStartAddress: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ()); + m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ()); + m_msg.PDL = 0x2; + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + __dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + m_msg.PDL = 0x0; + + if ( event_onDMXStartAddressChanged ) + event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + } + break; + + case rdm::DmxPersonality: + if ( m_msg.CC == rdm::GetCommand ) + { + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personality; + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; + m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + m_Personality = reinterpret_cast + (m_msg.PD)->DMX512Personality; + m_msg.PDL = 0x0; + + if ( event_onDMXPersonalityChanged ) + event_onDMXPersonalityChanged ( m_Personality ); + } + break; + + case rdm::IdentifyDevice: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0); + m_msg.PDL = 0x1; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + // Look into first byte to see whether identification + // is turned on or off + m_rdmStatus.ident = m_msg.PD[0] ? true : false; + if ( event_onIdentifyDevice ) + event_onIdentifyDevice ( m_rdmStatus.ident ); + + m_msg.PDL = 0x0; + } + break; + + case rdm::ManufacturerLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) ); + m_msg.PDL = sizeof ( ManufacturerLabel_P ); + } + break; + + case rdm::DeviceLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 ); + m_msg.PDL = 32; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + memset ( (void*) m_deviceLabel, ' ', 32 ); + memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) ); + m_msg.PDL = 0; + + // Notify application + if ( event_onDeviceLabelChanged ) + event_onDeviceLabelChanged ( m_deviceLabel, 32 ); + } + break; + + + default: + // Unknown parameter ID response + m_msg.portId = rdm::ResponseTypeNackReason; + m_msg.PD[0] = rdm::UnknownPid; + m_msg.PD[1] = 0x0; + m_msg.PDL = 0x2; + break; + }; + } + + // + // Only respond if this this message + // was destined to us only + if ( m_msg.dstUid == m_devid ) + { + m_msg.startCode = RDM_START_CODE; + m_msg.subStartCode = 0x01; + m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL; + m_msg.msgCount = 0; + + /* + switch ( m_msg.msg.CC ) + { + case rdm::DiscoveryCommand: + m_msg.msg.CC = rdm::DiscoveryCommandResponse; + break; + case rdm::GetCommand: + m_msg.msg.CC = rdm::GetCommandResponse; + break; + case rdm::SetCommand: + m_msg.msg.CC = rdm::SetCommandResponse; + break; + } + */ + /* Above replaced by next line */ + m_msg.CC++; + + m_msg.dstUid.copy ( m_msg.srcUid ); + m_msg.srcUid.copy ( m_devid ); + + //_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + SetISRMode ( isr::RDMTransmit ); + + } +} + + +void SetISRMode ( isr::isrMode mode ) +{ + uint8_t readEnable; + +#if defined(USE_DMX_SERIAL_0) + #if defined(UCSRB) && defined (UCSRC) + UCSRC |= (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Prepare before kicking off ISR + //DMX_UDR = 0x0; + __isr_rxState = isr::Idle; + readEnable = LOW; + DMX_UCSRB = (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UDR = 0x0; + DMX_UCSRB = 0x0; + readEnable = HIGH; + __isr_txState = isr::DmxBreakManual; + break; + + case isr::RDMTransmit: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + //DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + //DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UCSRB = (1< -1) + digitalWrite ( __re_pin, readEnable ); + +} + +// +// TX UART (DMX Transmission ISR) +// +ISR (USART_TX) +{ + static uint16_t current_slot; + + switch ( __isr_txState ) + { + case isr::DmxBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + if ( __isr_txState == isr::DmxBreak ) + __isr_txState = isr::DmxStartByte; + + break; + + case isr::DmxStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + current_slot = 0; + DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ]; + __isr_txState = isr::DmxTransmitData; + break; + + + case isr::DmxTransmitData: + // NOTE: we always send full frames of 513 bytes, this will bring us + // close to 40 frames / sec with no interslot delays + #ifdef DMX_IBG + _delay_us (DMX_IBG); + #endif + + DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ ); + + // Send 512 channels + if ( current_slot >= DMX_MAX_FRAMESIZE ) + { + if ( __dmx_master->autoBreakEnabled () ) + __isr_txState = isr::DmxBreak; + else + SetISRMode ( isr::DMXTransmitManual ); + } + + break; + +/* case isr::RdmBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + __isr_txState = isr::RdmStartByte; + + break; +*/ + + case isr::RdmStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Write start byte + __rdm_responder->fetchOutgoing ( &DMX_UDR, true ); + __isr_txState = isr::RdmTransmitData; + + break; + + case isr::RdmTransmitData: + // Write rest of data + if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) ) + __isr_txState = isr::RDMTransmitComplete; + break; + + case isr::RDMTransmitComplete: + SetISRMode ( isr::Receive ); // Start waitin for new data + __isr_txState = isr::Idle; // No tx state + break; + + } +} + + + +// +// RX UART (DMX Reception ISR) +// +ISR (USART_RX) +{ + uint8_t usart_state = DMX_UCSRA; + uint8_t usart_data = DMX_UDR; + + // + // Check for framing error and reset if found + // A framing most likely* indicate a break in our ocasion + // + if ( usart_state & (1<processIncoming ( usart_data, true ); + __isr_rxState = isr::DmxRecordData; + } + else if ( __rdm_responder && + usart_data == RDM_START_CODE && + __rdm_responder->m_rdmStatus.enabled ) + { + // __rdm_responder->clear (); + __rdm_responder->processIncoming ( usart_data, true ); + __isr_rxState = isr::RdmRecordData; + } + else + { + __isr_rxState = isr::Idle; + } + break; + + // Process DMX Data + case isr::DmxRecordData: + if ( __dmx_slave->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + // Process RDM Data + case isr::RdmRecordData: + if ( __rdm_responder->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + } + +} + diff --git a/DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.h b/DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.h new file mode 100644 index 000000000..0f03da619 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave/Conceptinetics.h @@ -0,0 +1,383 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.h - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino / Genuino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino Leonardo using a CTC-DRA-13-R2 ISOLATED DMX-RDM SHIELD + + - CTC-DRA-10-1 and CTC-DRA-10-R2 is the Non-isolated costs effective DMX-RDM shield +*/ + + +#ifndef CONCEPTINETICS_H_ +#define CONCEPTINETICS_H_ + +#include +#include + +#include "Rdm_Uid.h" +#include "Rdm_Defines.h" + +#define DMX_MAX_FRAMESIZE 513 // Startbyte + 512 Slots +#define DMX_MIN_FRAMESIZE 2 // Startbyte + 1 Slot + +#define DMX_MAX_FRAMECHANNELS 512 // Maxmim number of channer per frame + +#define DMX_STARTCODE_SIZE 1 // Size of startcode in bytes + +#define DMX_START_CODE 0x0 // Start code for a DMX frame +#define RDM_START_CODE 0xcc // Start code for a RDM frame + +// Uncomment to enable Inter slot delay ) (avg < 76uSec) ... +// mimum is zero according to specification +// #define DMX_IBG 10 // Inter slot time + +// Speed your Arduino is running on in Hz. +#define F_OSC 16000000UL + +// DMX Baudrate, this should be 250000 +#define DMX_BAUD_RATE 250000 + +// The baudrate used to automaticly generate a break within +// your ISR.. make it lower to generate longer breaks +//#define DMX_BREAK_RATE 99900 + +// 2017, Feb 28: Set to appox 176us +#define DMX_BREAK_RATE 49950 + +// Tabel 3-2 ANSI_E1-20-2010 +// Minimum time to allow the datalink to 'turn arround' +#define MIN_RESPONDER_PACKET_SPACING_USEC 176 /*176*/ + +// Define which serial port to use as DMX port, only one can be +// selected at the time by uncommenting one of the following +// lines +#define USE_DMX_SERIAL_0 +//#define USE_DMX_SERIAL_1 +//#define USE_DMX_SERIAL_2 +//#define USE_DMX_SERIAL_3 + +namespace dmx +{ + enum dmxState + { + dmxUnknown, + dmxStartByte, + dmxWaitStartAddress, + dmxData, + dmxFrameReady, + }; +}; + +namespace rdm +{ + enum rdmState + { + rdmUnknown, + rdmStartByte, + rdmSubStartCode, + rdmMessageLength, + rdmData, + rdmChecksumHigh, + rdmChecksumLow, + rdmFrameReady, + }; +}; + +struct IFrameBuffer +{ + virtual uint16_t getBufferSize ( void ) = 0; + + virtual uint8_t getSlotValue ( uint16_t index ) = 0; + virtual void setSlotValue ( uint16_t index, uint8_t value ) = 0; +}; + +class DMX_FrameBuffer : IFrameBuffer +{ + public: + // + // Constructor buffersize = 1-513 + // + DMX_FrameBuffer ( uint16_t buffer_size ); + DMX_FrameBuffer ( DMX_FrameBuffer &buffer ); + ~DMX_FrameBuffer ( void ); + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void setSlotRange ( uint16_t start, uint16_t end, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + private: + + uint8_t *m_refcount; + uint16_t m_bufferSize; + uint8_t *m_buffer; +}; + + +// +// DMX Master controller +// +class DMX_Master +{ + public: + // Run the DMX master from a pre allocated frame buffer which + // you have fully under your own control + DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ); + + // Run the DMX master by giving a predefined maximum number of + // channels to support + DMX_Master ( uint16_t maxChannel, int readEnablePin ); + + ~DMX_Master ( void ); + + void enable ( void ); // Start transmitting + void disable ( void ); // Stop transmitting + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + // Update channel values + void setChannelValue ( uint16_t channel, uint8_t value ); + void setChannelRange ( uint16_t start, uint16_t end, uint8_t value ); + + public: + // + // Manual control over the break period + // + void setAutoBreakMode ( void ); // Generated from ISR + void setManualBreakMode ( void ); // Generate manually + + uint8_t autoBreakEnabled ( void ); + + // We are waiting for a manual break to be generated + uint8_t waitingBreak ( void ); + + // Generate break and start transmission of frame + void breakAndContinue ( uint8_t breakLength_us = 100 ); + + + protected: + void setStartCode ( uint8_t value ); + + + private: + DMX_FrameBuffer m_frameBuffer; + uint8_t m_autoBreak; +}; + + +// +// DMX Slave controller +// +class DMX_Slave : public DMX_FrameBuffer +{ + public: + DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin = -1 ); + + // nrChannels is the consecutive DMX512 slots required + // to operate this slave device + DMX_Slave ( uint16_t nrChannels, int readEnablePin = -1 ); + + ~DMX_Slave ( void ); + + void enable ( void ); // Enable receiver + void disable ( void ); // Disable receiver + + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + uint8_t getChannelValue ( uint16_t channel ); + + uint16_t getStartAddress ( void ); + void setStartAddress ( uint16_t ); + + + // Process incoming byte from USART + bool processIncoming ( uint8_t val, bool first = false ); + + // Register on receive complete callback in case + // of time critical applications + void onReceiveComplete ( void (*func)(unsigned short) ); + + protected: + + + private: + uint16_t m_startAddress; // Slave start address + dmx::dmxState m_state; + + static void (*event_onFrameReceived)(unsigned short channelsReceived); +}; + + +class RDM_FrameBuffer : public IFrameBuffer +{ + public: + // + // Constructor + // + RDM_FrameBuffer ( void ) {}; + ~RDM_FrameBuffer ( void ) {}; + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + public: // functions to provide access from USART + // Process incoming byte from USART, + // returns false when no more data is accepted + bool processIncoming ( uint8_t val, bool first = false ); + + // Process outgoing byte to USART + // returns false when no more data is available + bool fetchOutgoing ( volatile uint8_t *udr, bool first = false ); + + protected: + // Process received frame + virtual void processFrame ( void ) = 0; + + //private: + protected: + rdm::rdmState m_state; // State for pushing the message in + RDM_Message m_msg; + RDM_Checksum m_csRecv; // Checksum received in rdm message +}; + +// +// RDM_Responder +// +class RDM_Responder : public RDM_FrameBuffer +{ + public: + // + // m = manufacturer id (16bits) + // d1-d4 = device id (32bits) + // + RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, DMX_Slave &slave); + ~RDM_Responder ( void ); + + void setDeviceInfo + ( + uint16_t deviceModelId, + rdm::RdmProductCategory productCategory, + uint8_t personalities = 1, + uint8_t personality = 1 + ) + { + m_DeviceModelId = deviceModelId; + m_ProductCategory = productCategory; + m_Personalities = personalities; + m_Personality = personality; + }; + + // + // Set vendor software version id + // + // v1 = MOST SIGNIFICANT + // v2... + // v3... + // v4 = LEAST SIGNIFICANT + // + void setSoftwareVersionId ( uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4 ) + { + m_SoftwareVersionId[0] = v1; + m_SoftwareVersionId[1] = v2; + m_SoftwareVersionId[2] = v3; + m_SoftwareVersionId[3] = v4; + } + + // Currently no sensors and subdevices supported + // void AddSensor ( void ); + // void AddSubDevice ( void ); + + uint8_t getPersonality ( void ) { return m_Personality; }; + void setPersonality ( uint8_t personality ) { m_Personality = personality; }; + + // Register on identify device event handler + void onIdentifyDevice ( void (*func)(bool) ); + void onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ); + void onDMXStartAddressChanged ( void (*func) (uint16_t) ); + void onDMXPersonalityChanged ( void (*func) (uint8_t) ); + + + // Set the device label + void setDeviceLabel ( const char *label, size_t len ); + + // Enable, Disable rdm responder + void enable ( void ) { m_rdmStatus.enabled = true; m_rdmStatus.mute = false; }; + void disable ( void ) { m_rdmStatus.enabled = false; }; + + union + { + uint8_t raw; + struct + { + uint8_t mute:1; + uint8_t ident:1; + uint8_t enabled:1; // Rdm responder enable/disable + }; + } m_rdmStatus; + + + protected: + virtual void processFrame ( void ); + + // Discovery to unque brach packets only requires + // the data part of the packet to be transmitted + // without breaks or header + void repondDiscUniqueBranch ( void ); + + // Helpers for generating response packets which + // have larger datafields + void populateDeviceInfo ( void ); + + private: + RDM_Uid m_devid; // Holds our unique device ID + uint8_t m_Personalities; // The total number of supported personalities + uint8_t m_Personality; // The currently active personality + uint16_t m_DeviceModelId; + uint8_t m_SoftwareVersionId[4]; // 32 bit Software version + rdm::RdmProductCategory m_ProductCategory; + + char m_deviceLabel[32]; // Device label + + static void (*event_onIdentifyDevice)(bool); + static void (*event_onDeviceLabelChanged)(const char*, uint8_t); + static void (*event_onDMXStartAddressChanged)(uint16_t); + static void (*event_onDMXPersonalityChanged)(uint8_t); +}; + + +#endif /* CONCEPTINETICS_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.cpp b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.cpp new file mode 100644 index 000000000..36ada5924 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.cpp @@ -0,0 +1,1233 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.cpp - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD +*/ + + +#include "pins_arduino.h" +#include "Conceptinetics.h" + +#include +#include + +#include +#include + +#include + + +#if defined (USE_DMX_SERIAL_0) + + #if defined (USART__TXC_vect) + #define USART_TX USART__TXC_vect + #elif defined(USART_TX_vect) + #define USART_TX USART_TX_vect + #elif defined(USART0_TX_vect) + #define USART_TX USART0_TX_vect + #endif + + #if defined (USART__RXC_vect) + #define USART_RX USART__RXC_vect + #elif defined(USART_RX_vect) + #define USART_RX USART_RX_vect + #elif defined(USART0_RX_vect) + #define USART_RX USART0_RX_vect + #endif + + #if defined UDR + #define DMX_UDR UDR + #elif defined UDR0 + #define DMX_UDR UDR0 + #endif + + #if defined(UBRRH) && defined(UBRRL) + #define DMX_UBRRH UBRRH + #define DMX_UBRRL UBRRL + #elif defined(UBRR0H) && defined(UBRR0L) + #define DMX_UBRRH UBRR0H + #define DMX_UBRRL UBRR0L + #endif + + #if defined(UCSRA) + #define DMX_UCSRA UCSRA + #elif defined(UCSR0A) + #define DMX_UCSRA UCSR0A + #endif + + #if defined(UCSRB) + #define DMX_UCSRB UCSRB + #elif defined (UCSR0B) + #define DMX_UCSRB UCSR0B + #endif + + #if defined(TXEN) && defined(TXCIE) + #define DMX_TXEN TXEN + #define DMX_TXCIE TXCIE + #elif defined(TXEN0) && defined(TXCIE0) + #define DMX_TXEN TXEN0 + #define DMX_TXCIE TXCIE0 + #endif + + #if defined(RXEN) && defined(RXCIE) + #define DMX_RXEN RXEN + #define DMX_RXCIE RXCIE + #elif defined(RXEN0) && defined(RXCIE0) + #define DMX_RXEN RXEN0 + #define DMX_RXCIE RXCIE0 + #endif + + #if defined(FE) + #define DMX_FE FE + #elif defined(FE0) + #define DMX_FE FE0 + #endif + + #define RX_PIN 0 + #define TX_PIN 1 + +#elif defined (USE_DMX_SERIAL_1) + #define USART_RX USART1_RX_vect + #define USART_TX USART1_TX_vect + #define DMX_UDR UDR1 + #define DMX_UBRRH UBRR1H + #define DMX_UBRRL UBRR1L + #define DMX_UCSRA UCSR1A + #define DMX_UCSRB UCSR1B + #define DMX_TXEN TXEN1 + #define DMX_TXCIE TXCIE1 + #define DMX_RXEN RXEN1 + #define DMX_RXCIE RXCIE1 + #define DMX_FE FE1 + #define RX_PIN 19 + #define TX_PIN 18 +#elif defined (USE_DMX_SERIAL_2) + #define USART_RX USART2_RX_vect + #define USART_TX USART2_TX_vect + #define DMX_UDR UDR2 + #define DMX_UBRRH UBRR2H + #define DMX_UBRRL UBRR2L + #define DMX_UCSRA UCSR2A + #define DMX_UCSRB UCSR2B + #define DMX_TXEN TXEN2 + #define DMX_TXCIE TXCIE2 + #define DMX_RXEN RXEN2 + #define DMX_RXCIE RXCIE2 + #define DMX_FE FE2 + #define RX_PIN 17 + #define TX_PIN 16 +#elif defined (USE_DMX_SERIAL_3) + #define USART_RX USART3_RX_vect + #define USART_TX USART3_TX_vect + #define DMX_UDR UDR3 + #define DMX_UBRRH UBRR3H + #define DMX_UBRRL UBRR3L + #define DMX_UCSRA UCSR3A + #define DMX_UCSRB UCSR3B + #define DMX_TXEN TXEN3 + #define DMX_TXCIE TXCIE3 + #define DMX_RXEN RXEN3 + #define DMX_RXCIE RXCIE3 + #define DMX_FE FE3 + #define RX_PIN 14 + #define TX_PIN 15 +#endif + + +#define LOWBYTE(v) ((uint8_t) (v)) +#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8)) + +#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 ) + +namespace isr +{ + enum isrState + { + Idle, + Break, + DmxBreak, + DmxBreakManual, + DmxStartByte, + DmxRecordData, + DmxTransmitData, + RdmBreak, + RdmStartByte, + RdmRecordData, + RdmTransmitData, + RDMTransmitComplete, + }; + + enum isrMode + { + Disabled, + Receive, + DMXTransmit, + DMXTransmitManual, /* Manual break... */ + RDMTransmit, + RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */ + }; +}; + + +DMX_Master *__dmx_master; +DMX_Slave *__dmx_slave; +RDM_Responder *__rdm_responder; + +int8_t __re_pin; // R/W Pin on shield + +isr::isrState __isr_txState; // TX ISR state +isr::isrState __isr_rxState; // RX ISR state + + +void SetISRMode ( isr::isrMode ); + + +DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size ) +{ + m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) ); + + if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE ) + { + m_buffer = (uint8_t*) malloc ( buffer_size ); + if ( m_buffer != NULL ) + { + memset ( (void *)m_buffer, 0x0, buffer_size ); + m_bufferSize = buffer_size; + } + else + m_buffer = 0x0; + } + else + m_bufferSize = 0x0; + + *m_refcount++; +} + +DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer ) +{ + // Copy references and make sure the parent object does not dispose our + // buffer when deleted and we are still active + this->m_refcount = buffer.m_refcount; + (*this->m_refcount)++; + + this->m_buffer = buffer.m_buffer; + this->m_bufferSize = buffer.m_bufferSize; +} + +DMX_FrameBuffer::~DMX_FrameBuffer ( void ) +{ + // If we are the last object using the + // allocated buffer then free it together + // with the refcounter + if ( --(*m_refcount) == 0 ) + { + if ( m_buffer ) + free ( m_buffer ); + + free ( m_refcount ); + } +} + +uint16_t DMX_FrameBuffer::getBufferSize ( void ) +{ + return m_bufferSize; +} + + +uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if (index < m_bufferSize) + return m_buffer[index]; + else + return 0x0; +} + + +void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < m_bufferSize ) + m_buffer[index] = value; +} + + +void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start < m_bufferSize && end < m_bufferSize && start < end ) + memset ( (void *) &m_buffer[start], value, end-start+1 ); +} + +void DMX_FrameBuffer::clear ( void ) +{ + memset ( (void *) m_buffer, 0x0, m_bufferSize ); +} + +uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index ) +{ + return m_buffer[index]; +} + + +DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ) +: m_frameBuffer ( buffer ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin ) +: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::~DMX_Master ( void ) +{ + disable (); // Stop sending + __dmx_master = NULL; +} + +DMX_FrameBuffer &DMX_Master::getBuffer ( void ) +{ + return m_frameBuffer; // Return reference to frame buffer +} + +void DMX_Master::setStartCode ( uint8_t value ) +{ + m_frameBuffer[0] = value; // Set the first byte in our frame buffer +} + +void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value ) +{ + if ( channel > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotValue ( channel, value ); +} + +void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotRange ( start, end, value ); +} + + +void DMX_Master::enable ( void ) +{ + __dmx_master = this; + + if ( m_autoBreak ) + ::SetISRMode ( isr::DMXTransmit ); + else + ::SetISRMode ( isr::DMXTransmitManual ); +} + +void DMX_Master::disable ( void ) +{ + ::SetISRMode ( isr::Disabled ); + __dmx_master = NULL; // No active master +} + +void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; } +void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; } +uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; } + + +uint8_t DMX_Master::waitingBreak ( void ) +{ + return ( __isr_txState == isr::DmxBreakManual ); +} + +void DMX_Master::breakAndContinue ( uint8_t breakLength_us ) +{ + // Only execute if we are the controlling master object + if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual ) + { + pinMode ( TX_PIN, OUTPUT ); + digitalWrite ( TX_PIN, LOW ); // Begin BREAK + + for (uint8_t bl=0; bl(*this); +} + + uint8_t DMX_Slave::getChannelValue ( uint16_t channel ) +{ + return getSlotValue ( channel ); +} + + +uint16_t DMX_Slave::getStartAddress ( void ) +{ + return m_startAddress; +} + +void DMX_Slave::setStartAddress ( uint16_t addr ) +{ + m_startAddress = addr; +} + +void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) ) +{ + event_onFrameReceived = func; +} + + +bool DMX_Slave::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + // We could have received less channels then we + // expected.. but still is a complete frame + if (m_state == dmx::dmxData && event_onFrameReceived) + event_onFrameReceived (idx); + + m_state = dmx::dmxStartByte; + } + + switch ( m_state ) + { + case dmx::dmxStartByte: + setSlotValue ( 0, val ); // Store start code + idx = m_startAddress; + m_state = dmx::dmxWaitStartAddress; + + case dmx::dmxWaitStartAddress: + if ( --idx == 0 ) + m_state = dmx::dmxData; + break; + + case dmx::dmxData: + if ( idx++ < getBufferSize() ) + setSlotValue ( idx, val ); + else + { + m_state = dmx::dmxFrameReady; + + // If a onFrameReceived callback is register... + if (event_onFrameReceived) + event_onFrameReceived (idx-2); + + rval = true; + } + break; + } + + return rval; +} + + +uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); } + +uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if ( index < sizeof ( m_msg ) ) + return m_msg.d[index]; + else + return 0x0; +} + + +void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < sizeof ( m_msg ) ) + m_msg.d[index] = value; +} + +void RDM_FrameBuffer::clear ( void ) +{ + memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) ); + m_state = rdm::rdmUnknown; +} + +bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + m_state = rdm::rdmStartByte; + m_csRecv.checksum = (uint16_t) 0x0000; + idx = 0; + } + + // Prevent buffer overflow for large messages + if (idx >= sizeof(m_msg)) + return true; + + switch ( m_state ) + { + case rdm::rdmStartByte: + m_msg.startCode = val; + m_state = rdm::rdmSubStartCode; + break; + + case rdm::rdmSubStartCode: + if ( val != 0x01 ) + { + rval = true; // Stop processing data + break; + } + + m_msg.subStartCode = val; + m_state = rdm::rdmMessageLength; + break; + + case rdm::rdmMessageLength: + m_msg.msgLength = val; + m_state = rdm::rdmData; + m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum + idx = 3; // buffer index for next byte + break; + + case rdm::rdmData: + m_msg.d[idx++] = val; + m_csRecv.checksum += val; + if ( idx >= m_msg.msgLength ) + m_state = rdm::rdmChecksumHigh; + break; + + case rdm::rdmChecksumHigh: + m_csRecv.csh = val; + m_state = rdm::rdmChecksumLow; + + break; + + case rdm::rdmChecksumLow: + m_csRecv.csl = val; + + if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum) + { + m_state = rdm::rdmFrameReady; + + // valid checksum ... start processing + processFrame (); + } + + m_state = rdm::rdmUnknown; + rval = true; + break; + }; + + return rval; +} + +bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first ) +{ + static uint16_t idx; + static uint16_t cs; + bool rval = false; + + + if ( first ) + { + m_state = rdm::rdmData; + cs = 0x0; + idx = 0; + } + + switch ( m_state ) + { + case rdm::rdmData: + cs += m_msg.d[idx]; + *udr = m_msg.d[idx++]; + if ( idx >= m_msg.msgLength ) + { + m_state = rdm::rdmChecksumHigh; + } + break; + + case rdm::rdmChecksumHigh: + *udr = (cs >> 8); + m_state = rdm::rdmChecksumLow; + break; + + case rdm::rdmChecksumLow: + *udr = (cs & 0xff); + m_state = rdm::rdmUnknown; + rval = true; + break; + + } + + return rval; +} + + +void (*RDM_Responder::event_onIdentifyDevice)(bool); +void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t); +void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t); +void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t); + +// +// slave parameter is only used to ensure a slave object is present before +// initializing the rdm responder class +// +RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, DMX_Slave &slave ) +: RDM_FrameBuffer ( ), + m_Personalities (1), // Available personlities + m_Personality (1) // Default personality eq 1. +{ + __rdm_responder = this; + m_devid.Initialize ( m, d1, d2, d3, d4 ); + + // Default software version id = 0x00000000 + memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 ); + + // Rdm responder is disabled by default + m_rdmStatus.enabled = false; +} + +RDM_Responder::~RDM_Responder ( void ) +{ + __rdm_responder = NULL; +} + +void RDM_Responder::onIdentifyDevice ( void (*func)(bool) ) +{ + event_onIdentifyDevice = func; +} + +void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ) +{ + event_onDeviceLabelChanged = func; +} + +void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) ) +{ + event_onDMXStartAddressChanged = func; +} + +void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) ) +{ + event_onDMXPersonalityChanged = func; +} + +void RDM_Responder::setDeviceLabel ( const char *label, size_t len ) +{ + if ( len > RDM_MAX_DEVICELABEL_LENGTH ) + len = RDM_MAX_DEVICELABEL_LENGTH; + + memcpy ( (void *)m_deviceLabel, (void *)label, len ); +} + +#define UID_0 0x12 //ESTA device ID +#define UID_1 0x34 +#define UID_2 0x56 +#define UID_3 0x88 +#define UID_4 0x00 +#define UID_5 0x00 + +#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5) + +void RDM_Responder::repondDiscUniqueBranch ( void ) +{ + #if defined(UCSRB) + UCSRB = (1<>8) | 0xaa; + response [21] = (cs>>8) | 0x55; + response [22] = (cs&0xff) | 0xaa; + response [23] = (cs&0xff) | 0x55; + + + // Table 3-2 ANSI_E1-20-2010 <2ms + _delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + + // Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with + // section 3.2.3 + // Set shield to transmit mode (turn arround) + digitalWrite ( __re_pin, HIGH ); + + + for ( int i=0; i<24; i++ ) + { + // Wait until data register is empty + #if defined (UCSR0A) && defined (UDRE0) + while((UCSR0A & (1 <(m_msg.PD); + + pd->protocolVersionMajor = 0x01; + pd->protocolVersionMinor = 0x00; + pd->deviceModelId = BSWAP_16(m_DeviceModelId); + pd->ProductCategory = BSWAP_16(m_ProductCategory); + memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 ); + pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte + pd->DMX512CurrentPersonality = m_Personality; + pd->DMX512NumberPersonalities = m_Personalities; + pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress()); + + pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library + pd->SensorCount = 0x0; // Sensors are not yet supported + + m_msg.PDL = sizeof (RDM__DeviceInfoPD); +} + +const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics"; + +void RDM_Responder::processFrame ( void ) +{ + // If packet is a general broadcast + if ( + m_msg.dstUid.isBroadcast (m_devid.m_id) || + m_devid == m_msg.dstUid + ) + { + // Set default response type + m_msg.portId = rdm::ResponseTypeAck; + + switch ( BSWAP_16(m_msg.PID) ) + { + case rdm::DiscUniqueBranch: + // Check if we are inside the given unique branch... + if ( !m_rdmStatus.mute && + reinterpret_cast(m_msg.PD)->lbound < m_devid && + reinterpret_cast(m_msg.PD)->hbound > m_devid ) + { + // Discovery messages are responded with data only and no breaks + repondDiscUniqueBranch (); + } + break; + + case rdm::DiscMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = true; + break; + + case rdm::DiscUnMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = false; + break; + + case rdm::SupportedParameters: + // + // Temporary solution... this will become dynamic + // in a later version... + // + m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB + m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB + + m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality); + m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality); + + m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel); + m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel); + + m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel); + m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel); + + m_msg.PDL = 0x6; + break; + + // Only for manufacturer specific parameters + // case rdm::ParameterDescription: + // break; + + case rdm::DeviceInfo: + if ( m_msg.CC == rdm::GetCommand ) + populateDeviceInfo (); + break; + + case rdm::DmxStartAddress: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ()); + m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ()); + m_msg.PDL = 0x2; + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + __dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + m_msg.PDL = 0x0; + + if ( event_onDMXStartAddressChanged ) + event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + } + break; + + case rdm::DmxPersonality: + if ( m_msg.CC == rdm::GetCommand ) + { + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personality; + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; + m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + m_Personality = reinterpret_cast + (m_msg.PD)->DMX512Personality; + m_msg.PDL = 0x0; + + if ( event_onDMXPersonalityChanged ) + event_onDMXPersonalityChanged ( m_Personality ); + } + break; + + case rdm::IdentifyDevice: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0); + m_msg.PDL = 0x1; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + // Look into first byte to see whether identification + // is turned on or off + m_rdmStatus.ident = m_msg.PD[0] ? true : false; + if ( event_onIdentifyDevice ) + event_onIdentifyDevice ( m_rdmStatus.ident ); + + m_msg.PDL = 0x0; + } + break; + + case rdm::ManufacturerLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) ); + m_msg.PDL = sizeof ( ManufacturerLabel_P ); + } + break; + + case rdm::DeviceLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 ); + m_msg.PDL = 32; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + memset ( (void*) m_deviceLabel, ' ', 32 ); + memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) ); + m_msg.PDL = 0; + + // Notify application + if ( event_onDeviceLabelChanged ) + event_onDeviceLabelChanged ( m_deviceLabel, 32 ); + } + break; + + + default: + // Unknown parameter ID response + m_msg.portId = rdm::ResponseTypeNackReason; + m_msg.PD[0] = rdm::UnknownPid; + m_msg.PD[1] = 0x0; + m_msg.PDL = 0x2; + break; + }; + } + + // + // Only respond if this this message + // was destined to us only + if ( m_msg.dstUid == m_devid ) + { + m_msg.startCode = RDM_START_CODE; + m_msg.subStartCode = 0x01; + m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL; + m_msg.msgCount = 0; + + /* + switch ( m_msg.msg.CC ) + { + case rdm::DiscoveryCommand: + m_msg.msg.CC = rdm::DiscoveryCommandResponse; + break; + case rdm::GetCommand: + m_msg.msg.CC = rdm::GetCommandResponse; + break; + case rdm::SetCommand: + m_msg.msg.CC = rdm::SetCommandResponse; + break; + } + */ + /* Above replaced by next line */ + m_msg.CC++; + + m_msg.dstUid.copy ( m_msg.srcUid ); + m_msg.srcUid.copy ( m_devid ); + + //_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + SetISRMode ( isr::RDMTransmit ); + + } +} + + +void SetISRMode ( isr::isrMode mode ) +{ + uint8_t readEnable; + +#if defined(USE_DMX_SERIAL_0) + #if defined(UCSRB) && defined (UCSRC) + UCSRC |= (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Prepare before kicking off ISR + //DMX_UDR = 0x0; + __isr_rxState = isr::Idle; + readEnable = LOW; + DMX_UCSRB = (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UDR = 0x0; + DMX_UCSRB = 0x0; + readEnable = HIGH; + __isr_txState = isr::DmxBreakManual; + break; + + case isr::RDMTransmit: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + //DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + //DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UCSRB = (1< -1) + digitalWrite ( __re_pin, readEnable ); + +} + +// +// TX UART (DMX Transmission ISR) +// +ISR (USART_TX) +{ + static uint16_t current_slot; + + switch ( __isr_txState ) + { + case isr::DmxBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + if ( __isr_txState == isr::DmxBreak ) + __isr_txState = isr::DmxStartByte; + + break; + + case isr::DmxStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + current_slot = 0; + DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ]; + __isr_txState = isr::DmxTransmitData; + break; + + + case isr::DmxTransmitData: + // NOTE: we always send full frames of 513 bytes, this will bring us + // close to 40 frames / sec with no interslot delays + #ifdef DMX_IBG + _delay_us (DMX_IBG); + #endif + + DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ ); + + // Send 512 channels + if ( current_slot >= DMX_MAX_FRAMESIZE ) + { + if ( __dmx_master->autoBreakEnabled () ) + __isr_txState = isr::DmxBreak; + else + SetISRMode ( isr::DMXTransmitManual ); + } + + break; + +/* case isr::RdmBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + __isr_txState = isr::RdmStartByte; + + break; +*/ + + case isr::RdmStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Write start byte + __rdm_responder->fetchOutgoing ( &DMX_UDR, true ); + __isr_txState = isr::RdmTransmitData; + + break; + + case isr::RdmTransmitData: + // Write rest of data + if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) ) + __isr_txState = isr::RDMTransmitComplete; + break; + + case isr::RDMTransmitComplete: + SetISRMode ( isr::Receive ); // Start waitin for new data + __isr_txState = isr::Idle; // No tx state + break; + + } +} + + + +// +// RX UART (DMX Reception ISR) +// +ISR (USART_RX) +{ + uint8_t usart_state = DMX_UCSRA; + uint8_t usart_data = DMX_UDR; + + // + // Check for framing error and reset if found + // A framing most likely* indicate a break in our ocasion + // + if ( usart_state & (1<processIncoming ( usart_data, true ); + __isr_rxState = isr::DmxRecordData; + } + else if ( __rdm_responder && + usart_data == RDM_START_CODE && + __rdm_responder->m_rdmStatus.enabled ) + { + // __rdm_responder->clear (); + __rdm_responder->processIncoming ( usart_data, true ); + __isr_rxState = isr::RdmRecordData; + } + else + { + __isr_rxState = isr::Idle; + } + break; + + // Process DMX Data + case isr::DmxRecordData: + if ( __dmx_slave->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + // Process RDM Data + case isr::RdmRecordData: + if ( __rdm_responder->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + } + +} + diff --git a/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.h b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.h new file mode 100644 index 000000000..0f03da619 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Conceptinetics.h @@ -0,0 +1,383 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.h - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino / Genuino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino Leonardo using a CTC-DRA-13-R2 ISOLATED DMX-RDM SHIELD + + - CTC-DRA-10-1 and CTC-DRA-10-R2 is the Non-isolated costs effective DMX-RDM shield +*/ + + +#ifndef CONCEPTINETICS_H_ +#define CONCEPTINETICS_H_ + +#include +#include + +#include "Rdm_Uid.h" +#include "Rdm_Defines.h" + +#define DMX_MAX_FRAMESIZE 513 // Startbyte + 512 Slots +#define DMX_MIN_FRAMESIZE 2 // Startbyte + 1 Slot + +#define DMX_MAX_FRAMECHANNELS 512 // Maxmim number of channer per frame + +#define DMX_STARTCODE_SIZE 1 // Size of startcode in bytes + +#define DMX_START_CODE 0x0 // Start code for a DMX frame +#define RDM_START_CODE 0xcc // Start code for a RDM frame + +// Uncomment to enable Inter slot delay ) (avg < 76uSec) ... +// mimum is zero according to specification +// #define DMX_IBG 10 // Inter slot time + +// Speed your Arduino is running on in Hz. +#define F_OSC 16000000UL + +// DMX Baudrate, this should be 250000 +#define DMX_BAUD_RATE 250000 + +// The baudrate used to automaticly generate a break within +// your ISR.. make it lower to generate longer breaks +//#define DMX_BREAK_RATE 99900 + +// 2017, Feb 28: Set to appox 176us +#define DMX_BREAK_RATE 49950 + +// Tabel 3-2 ANSI_E1-20-2010 +// Minimum time to allow the datalink to 'turn arround' +#define MIN_RESPONDER_PACKET_SPACING_USEC 176 /*176*/ + +// Define which serial port to use as DMX port, only one can be +// selected at the time by uncommenting one of the following +// lines +#define USE_DMX_SERIAL_0 +//#define USE_DMX_SERIAL_1 +//#define USE_DMX_SERIAL_2 +//#define USE_DMX_SERIAL_3 + +namespace dmx +{ + enum dmxState + { + dmxUnknown, + dmxStartByte, + dmxWaitStartAddress, + dmxData, + dmxFrameReady, + }; +}; + +namespace rdm +{ + enum rdmState + { + rdmUnknown, + rdmStartByte, + rdmSubStartCode, + rdmMessageLength, + rdmData, + rdmChecksumHigh, + rdmChecksumLow, + rdmFrameReady, + }; +}; + +struct IFrameBuffer +{ + virtual uint16_t getBufferSize ( void ) = 0; + + virtual uint8_t getSlotValue ( uint16_t index ) = 0; + virtual void setSlotValue ( uint16_t index, uint8_t value ) = 0; +}; + +class DMX_FrameBuffer : IFrameBuffer +{ + public: + // + // Constructor buffersize = 1-513 + // + DMX_FrameBuffer ( uint16_t buffer_size ); + DMX_FrameBuffer ( DMX_FrameBuffer &buffer ); + ~DMX_FrameBuffer ( void ); + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void setSlotRange ( uint16_t start, uint16_t end, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + private: + + uint8_t *m_refcount; + uint16_t m_bufferSize; + uint8_t *m_buffer; +}; + + +// +// DMX Master controller +// +class DMX_Master +{ + public: + // Run the DMX master from a pre allocated frame buffer which + // you have fully under your own control + DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ); + + // Run the DMX master by giving a predefined maximum number of + // channels to support + DMX_Master ( uint16_t maxChannel, int readEnablePin ); + + ~DMX_Master ( void ); + + void enable ( void ); // Start transmitting + void disable ( void ); // Stop transmitting + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + // Update channel values + void setChannelValue ( uint16_t channel, uint8_t value ); + void setChannelRange ( uint16_t start, uint16_t end, uint8_t value ); + + public: + // + // Manual control over the break period + // + void setAutoBreakMode ( void ); // Generated from ISR + void setManualBreakMode ( void ); // Generate manually + + uint8_t autoBreakEnabled ( void ); + + // We are waiting for a manual break to be generated + uint8_t waitingBreak ( void ); + + // Generate break and start transmission of frame + void breakAndContinue ( uint8_t breakLength_us = 100 ); + + + protected: + void setStartCode ( uint8_t value ); + + + private: + DMX_FrameBuffer m_frameBuffer; + uint8_t m_autoBreak; +}; + + +// +// DMX Slave controller +// +class DMX_Slave : public DMX_FrameBuffer +{ + public: + DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin = -1 ); + + // nrChannels is the consecutive DMX512 slots required + // to operate this slave device + DMX_Slave ( uint16_t nrChannels, int readEnablePin = -1 ); + + ~DMX_Slave ( void ); + + void enable ( void ); // Enable receiver + void disable ( void ); // Disable receiver + + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + uint8_t getChannelValue ( uint16_t channel ); + + uint16_t getStartAddress ( void ); + void setStartAddress ( uint16_t ); + + + // Process incoming byte from USART + bool processIncoming ( uint8_t val, bool first = false ); + + // Register on receive complete callback in case + // of time critical applications + void onReceiveComplete ( void (*func)(unsigned short) ); + + protected: + + + private: + uint16_t m_startAddress; // Slave start address + dmx::dmxState m_state; + + static void (*event_onFrameReceived)(unsigned short channelsReceived); +}; + + +class RDM_FrameBuffer : public IFrameBuffer +{ + public: + // + // Constructor + // + RDM_FrameBuffer ( void ) {}; + ~RDM_FrameBuffer ( void ) {}; + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + public: // functions to provide access from USART + // Process incoming byte from USART, + // returns false when no more data is accepted + bool processIncoming ( uint8_t val, bool first = false ); + + // Process outgoing byte to USART + // returns false when no more data is available + bool fetchOutgoing ( volatile uint8_t *udr, bool first = false ); + + protected: + // Process received frame + virtual void processFrame ( void ) = 0; + + //private: + protected: + rdm::rdmState m_state; // State for pushing the message in + RDM_Message m_msg; + RDM_Checksum m_csRecv; // Checksum received in rdm message +}; + +// +// RDM_Responder +// +class RDM_Responder : public RDM_FrameBuffer +{ + public: + // + // m = manufacturer id (16bits) + // d1-d4 = device id (32bits) + // + RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, DMX_Slave &slave); + ~RDM_Responder ( void ); + + void setDeviceInfo + ( + uint16_t deviceModelId, + rdm::RdmProductCategory productCategory, + uint8_t personalities = 1, + uint8_t personality = 1 + ) + { + m_DeviceModelId = deviceModelId; + m_ProductCategory = productCategory; + m_Personalities = personalities; + m_Personality = personality; + }; + + // + // Set vendor software version id + // + // v1 = MOST SIGNIFICANT + // v2... + // v3... + // v4 = LEAST SIGNIFICANT + // + void setSoftwareVersionId ( uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4 ) + { + m_SoftwareVersionId[0] = v1; + m_SoftwareVersionId[1] = v2; + m_SoftwareVersionId[2] = v3; + m_SoftwareVersionId[3] = v4; + } + + // Currently no sensors and subdevices supported + // void AddSensor ( void ); + // void AddSubDevice ( void ); + + uint8_t getPersonality ( void ) { return m_Personality; }; + void setPersonality ( uint8_t personality ) { m_Personality = personality; }; + + // Register on identify device event handler + void onIdentifyDevice ( void (*func)(bool) ); + void onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ); + void onDMXStartAddressChanged ( void (*func) (uint16_t) ); + void onDMXPersonalityChanged ( void (*func) (uint8_t) ); + + + // Set the device label + void setDeviceLabel ( const char *label, size_t len ); + + // Enable, Disable rdm responder + void enable ( void ) { m_rdmStatus.enabled = true; m_rdmStatus.mute = false; }; + void disable ( void ) { m_rdmStatus.enabled = false; }; + + union + { + uint8_t raw; + struct + { + uint8_t mute:1; + uint8_t ident:1; + uint8_t enabled:1; // Rdm responder enable/disable + }; + } m_rdmStatus; + + + protected: + virtual void processFrame ( void ); + + // Discovery to unque brach packets only requires + // the data part of the packet to be transmitted + // without breaks or header + void repondDiscUniqueBranch ( void ); + + // Helpers for generating response packets which + // have larger datafields + void populateDeviceInfo ( void ); + + private: + RDM_Uid m_devid; // Holds our unique device ID + uint8_t m_Personalities; // The total number of supported personalities + uint8_t m_Personality; // The currently active personality + uint16_t m_DeviceModelId; + uint8_t m_SoftwareVersionId[4]; // 32 bit Software version + rdm::RdmProductCategory m_ProductCategory; + + char m_deviceLabel[32]; // Device label + + static void (*event_onIdentifyDevice)(bool); + static void (*event_onDeviceLabelChanged)(const char*, uint8_t); + static void (*event_onDMXStartAddressChanged)(uint16_t); + static void (*event_onDMXPersonalityChanged)(uint8_t); +}; + + +#endif /* CONCEPTINETICS_H_ */ diff --git a/DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.cpp b/DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.cpp new file mode 100644 index 000000000..36ada5924 --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.cpp @@ -0,0 +1,1233 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.cpp - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD +*/ + + +#include "pins_arduino.h" +#include "Conceptinetics.h" + +#include +#include + +#include +#include + +#include + + +#if defined (USE_DMX_SERIAL_0) + + #if defined (USART__TXC_vect) + #define USART_TX USART__TXC_vect + #elif defined(USART_TX_vect) + #define USART_TX USART_TX_vect + #elif defined(USART0_TX_vect) + #define USART_TX USART0_TX_vect + #endif + + #if defined (USART__RXC_vect) + #define USART_RX USART__RXC_vect + #elif defined(USART_RX_vect) + #define USART_RX USART_RX_vect + #elif defined(USART0_RX_vect) + #define USART_RX USART0_RX_vect + #endif + + #if defined UDR + #define DMX_UDR UDR + #elif defined UDR0 + #define DMX_UDR UDR0 + #endif + + #if defined(UBRRH) && defined(UBRRL) + #define DMX_UBRRH UBRRH + #define DMX_UBRRL UBRRL + #elif defined(UBRR0H) && defined(UBRR0L) + #define DMX_UBRRH UBRR0H + #define DMX_UBRRL UBRR0L + #endif + + #if defined(UCSRA) + #define DMX_UCSRA UCSRA + #elif defined(UCSR0A) + #define DMX_UCSRA UCSR0A + #endif + + #if defined(UCSRB) + #define DMX_UCSRB UCSRB + #elif defined (UCSR0B) + #define DMX_UCSRB UCSR0B + #endif + + #if defined(TXEN) && defined(TXCIE) + #define DMX_TXEN TXEN + #define DMX_TXCIE TXCIE + #elif defined(TXEN0) && defined(TXCIE0) + #define DMX_TXEN TXEN0 + #define DMX_TXCIE TXCIE0 + #endif + + #if defined(RXEN) && defined(RXCIE) + #define DMX_RXEN RXEN + #define DMX_RXCIE RXCIE + #elif defined(RXEN0) && defined(RXCIE0) + #define DMX_RXEN RXEN0 + #define DMX_RXCIE RXCIE0 + #endif + + #if defined(FE) + #define DMX_FE FE + #elif defined(FE0) + #define DMX_FE FE0 + #endif + + #define RX_PIN 0 + #define TX_PIN 1 + +#elif defined (USE_DMX_SERIAL_1) + #define USART_RX USART1_RX_vect + #define USART_TX USART1_TX_vect + #define DMX_UDR UDR1 + #define DMX_UBRRH UBRR1H + #define DMX_UBRRL UBRR1L + #define DMX_UCSRA UCSR1A + #define DMX_UCSRB UCSR1B + #define DMX_TXEN TXEN1 + #define DMX_TXCIE TXCIE1 + #define DMX_RXEN RXEN1 + #define DMX_RXCIE RXCIE1 + #define DMX_FE FE1 + #define RX_PIN 19 + #define TX_PIN 18 +#elif defined (USE_DMX_SERIAL_2) + #define USART_RX USART2_RX_vect + #define USART_TX USART2_TX_vect + #define DMX_UDR UDR2 + #define DMX_UBRRH UBRR2H + #define DMX_UBRRL UBRR2L + #define DMX_UCSRA UCSR2A + #define DMX_UCSRB UCSR2B + #define DMX_TXEN TXEN2 + #define DMX_TXCIE TXCIE2 + #define DMX_RXEN RXEN2 + #define DMX_RXCIE RXCIE2 + #define DMX_FE FE2 + #define RX_PIN 17 + #define TX_PIN 16 +#elif defined (USE_DMX_SERIAL_3) + #define USART_RX USART3_RX_vect + #define USART_TX USART3_TX_vect + #define DMX_UDR UDR3 + #define DMX_UBRRH UBRR3H + #define DMX_UBRRL UBRR3L + #define DMX_UCSRA UCSR3A + #define DMX_UCSRB UCSR3B + #define DMX_TXEN TXEN3 + #define DMX_TXCIE TXCIE3 + #define DMX_RXEN RXEN3 + #define DMX_RXCIE RXCIE3 + #define DMX_FE FE3 + #define RX_PIN 14 + #define TX_PIN 15 +#endif + + +#define LOWBYTE(v) ((uint8_t) (v)) +#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8)) + +#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 ) + +namespace isr +{ + enum isrState + { + Idle, + Break, + DmxBreak, + DmxBreakManual, + DmxStartByte, + DmxRecordData, + DmxTransmitData, + RdmBreak, + RdmStartByte, + RdmRecordData, + RdmTransmitData, + RDMTransmitComplete, + }; + + enum isrMode + { + Disabled, + Receive, + DMXTransmit, + DMXTransmitManual, /* Manual break... */ + RDMTransmit, + RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */ + }; +}; + + +DMX_Master *__dmx_master; +DMX_Slave *__dmx_slave; +RDM_Responder *__rdm_responder; + +int8_t __re_pin; // R/W Pin on shield + +isr::isrState __isr_txState; // TX ISR state +isr::isrState __isr_rxState; // RX ISR state + + +void SetISRMode ( isr::isrMode ); + + +DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size ) +{ + m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) ); + + if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE ) + { + m_buffer = (uint8_t*) malloc ( buffer_size ); + if ( m_buffer != NULL ) + { + memset ( (void *)m_buffer, 0x0, buffer_size ); + m_bufferSize = buffer_size; + } + else + m_buffer = 0x0; + } + else + m_bufferSize = 0x0; + + *m_refcount++; +} + +DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer ) +{ + // Copy references and make sure the parent object does not dispose our + // buffer when deleted and we are still active + this->m_refcount = buffer.m_refcount; + (*this->m_refcount)++; + + this->m_buffer = buffer.m_buffer; + this->m_bufferSize = buffer.m_bufferSize; +} + +DMX_FrameBuffer::~DMX_FrameBuffer ( void ) +{ + // If we are the last object using the + // allocated buffer then free it together + // with the refcounter + if ( --(*m_refcount) == 0 ) + { + if ( m_buffer ) + free ( m_buffer ); + + free ( m_refcount ); + } +} + +uint16_t DMX_FrameBuffer::getBufferSize ( void ) +{ + return m_bufferSize; +} + + +uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if (index < m_bufferSize) + return m_buffer[index]; + else + return 0x0; +} + + +void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < m_bufferSize ) + m_buffer[index] = value; +} + + +void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start < m_bufferSize && end < m_bufferSize && start < end ) + memset ( (void *) &m_buffer[start], value, end-start+1 ); +} + +void DMX_FrameBuffer::clear ( void ) +{ + memset ( (void *) m_buffer, 0x0, m_bufferSize ); +} + +uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index ) +{ + return m_buffer[index]; +} + + +DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ) +: m_frameBuffer ( buffer ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin ) +: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::~DMX_Master ( void ) +{ + disable (); // Stop sending + __dmx_master = NULL; +} + +DMX_FrameBuffer &DMX_Master::getBuffer ( void ) +{ + return m_frameBuffer; // Return reference to frame buffer +} + +void DMX_Master::setStartCode ( uint8_t value ) +{ + m_frameBuffer[0] = value; // Set the first byte in our frame buffer +} + +void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value ) +{ + if ( channel > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotValue ( channel, value ); +} + +void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotRange ( start, end, value ); +} + + +void DMX_Master::enable ( void ) +{ + __dmx_master = this; + + if ( m_autoBreak ) + ::SetISRMode ( isr::DMXTransmit ); + else + ::SetISRMode ( isr::DMXTransmitManual ); +} + +void DMX_Master::disable ( void ) +{ + ::SetISRMode ( isr::Disabled ); + __dmx_master = NULL; // No active master +} + +void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; } +void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; } +uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; } + + +uint8_t DMX_Master::waitingBreak ( void ) +{ + return ( __isr_txState == isr::DmxBreakManual ); +} + +void DMX_Master::breakAndContinue ( uint8_t breakLength_us ) +{ + // Only execute if we are the controlling master object + if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual ) + { + pinMode ( TX_PIN, OUTPUT ); + digitalWrite ( TX_PIN, LOW ); // Begin BREAK + + for (uint8_t bl=0; bl(*this); +} + + uint8_t DMX_Slave::getChannelValue ( uint16_t channel ) +{ + return getSlotValue ( channel ); +} + + +uint16_t DMX_Slave::getStartAddress ( void ) +{ + return m_startAddress; +} + +void DMX_Slave::setStartAddress ( uint16_t addr ) +{ + m_startAddress = addr; +} + +void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) ) +{ + event_onFrameReceived = func; +} + + +bool DMX_Slave::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + // We could have received less channels then we + // expected.. but still is a complete frame + if (m_state == dmx::dmxData && event_onFrameReceived) + event_onFrameReceived (idx); + + m_state = dmx::dmxStartByte; + } + + switch ( m_state ) + { + case dmx::dmxStartByte: + setSlotValue ( 0, val ); // Store start code + idx = m_startAddress; + m_state = dmx::dmxWaitStartAddress; + + case dmx::dmxWaitStartAddress: + if ( --idx == 0 ) + m_state = dmx::dmxData; + break; + + case dmx::dmxData: + if ( idx++ < getBufferSize() ) + setSlotValue ( idx, val ); + else + { + m_state = dmx::dmxFrameReady; + + // If a onFrameReceived callback is register... + if (event_onFrameReceived) + event_onFrameReceived (idx-2); + + rval = true; + } + break; + } + + return rval; +} + + +uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); } + +uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if ( index < sizeof ( m_msg ) ) + return m_msg.d[index]; + else + return 0x0; +} + + +void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < sizeof ( m_msg ) ) + m_msg.d[index] = value; +} + +void RDM_FrameBuffer::clear ( void ) +{ + memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) ); + m_state = rdm::rdmUnknown; +} + +bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + m_state = rdm::rdmStartByte; + m_csRecv.checksum = (uint16_t) 0x0000; + idx = 0; + } + + // Prevent buffer overflow for large messages + if (idx >= sizeof(m_msg)) + return true; + + switch ( m_state ) + { + case rdm::rdmStartByte: + m_msg.startCode = val; + m_state = rdm::rdmSubStartCode; + break; + + case rdm::rdmSubStartCode: + if ( val != 0x01 ) + { + rval = true; // Stop processing data + break; + } + + m_msg.subStartCode = val; + m_state = rdm::rdmMessageLength; + break; + + case rdm::rdmMessageLength: + m_msg.msgLength = val; + m_state = rdm::rdmData; + m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum + idx = 3; // buffer index for next byte + break; + + case rdm::rdmData: + m_msg.d[idx++] = val; + m_csRecv.checksum += val; + if ( idx >= m_msg.msgLength ) + m_state = rdm::rdmChecksumHigh; + break; + + case rdm::rdmChecksumHigh: + m_csRecv.csh = val; + m_state = rdm::rdmChecksumLow; + + break; + + case rdm::rdmChecksumLow: + m_csRecv.csl = val; + + if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum) + { + m_state = rdm::rdmFrameReady; + + // valid checksum ... start processing + processFrame (); + } + + m_state = rdm::rdmUnknown; + rval = true; + break; + }; + + return rval; +} + +bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first ) +{ + static uint16_t idx; + static uint16_t cs; + bool rval = false; + + + if ( first ) + { + m_state = rdm::rdmData; + cs = 0x0; + idx = 0; + } + + switch ( m_state ) + { + case rdm::rdmData: + cs += m_msg.d[idx]; + *udr = m_msg.d[idx++]; + if ( idx >= m_msg.msgLength ) + { + m_state = rdm::rdmChecksumHigh; + } + break; + + case rdm::rdmChecksumHigh: + *udr = (cs >> 8); + m_state = rdm::rdmChecksumLow; + break; + + case rdm::rdmChecksumLow: + *udr = (cs & 0xff); + m_state = rdm::rdmUnknown; + rval = true; + break; + + } + + return rval; +} + + +void (*RDM_Responder::event_onIdentifyDevice)(bool); +void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t); +void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t); +void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t); + +// +// slave parameter is only used to ensure a slave object is present before +// initializing the rdm responder class +// +RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, DMX_Slave &slave ) +: RDM_FrameBuffer ( ), + m_Personalities (1), // Available personlities + m_Personality (1) // Default personality eq 1. +{ + __rdm_responder = this; + m_devid.Initialize ( m, d1, d2, d3, d4 ); + + // Default software version id = 0x00000000 + memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 ); + + // Rdm responder is disabled by default + m_rdmStatus.enabled = false; +} + +RDM_Responder::~RDM_Responder ( void ) +{ + __rdm_responder = NULL; +} + +void RDM_Responder::onIdentifyDevice ( void (*func)(bool) ) +{ + event_onIdentifyDevice = func; +} + +void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ) +{ + event_onDeviceLabelChanged = func; +} + +void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) ) +{ + event_onDMXStartAddressChanged = func; +} + +void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) ) +{ + event_onDMXPersonalityChanged = func; +} + +void RDM_Responder::setDeviceLabel ( const char *label, size_t len ) +{ + if ( len > RDM_MAX_DEVICELABEL_LENGTH ) + len = RDM_MAX_DEVICELABEL_LENGTH; + + memcpy ( (void *)m_deviceLabel, (void *)label, len ); +} + +#define UID_0 0x12 //ESTA device ID +#define UID_1 0x34 +#define UID_2 0x56 +#define UID_3 0x88 +#define UID_4 0x00 +#define UID_5 0x00 + +#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5) + +void RDM_Responder::repondDiscUniqueBranch ( void ) +{ + #if defined(UCSRB) + UCSRB = (1<>8) | 0xaa; + response [21] = (cs>>8) | 0x55; + response [22] = (cs&0xff) | 0xaa; + response [23] = (cs&0xff) | 0x55; + + + // Table 3-2 ANSI_E1-20-2010 <2ms + _delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + + // Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with + // section 3.2.3 + // Set shield to transmit mode (turn arround) + digitalWrite ( __re_pin, HIGH ); + + + for ( int i=0; i<24; i++ ) + { + // Wait until data register is empty + #if defined (UCSR0A) && defined (UDRE0) + while((UCSR0A & (1 <(m_msg.PD); + + pd->protocolVersionMajor = 0x01; + pd->protocolVersionMinor = 0x00; + pd->deviceModelId = BSWAP_16(m_DeviceModelId); + pd->ProductCategory = BSWAP_16(m_ProductCategory); + memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 ); + pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte + pd->DMX512CurrentPersonality = m_Personality; + pd->DMX512NumberPersonalities = m_Personalities; + pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress()); + + pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library + pd->SensorCount = 0x0; // Sensors are not yet supported + + m_msg.PDL = sizeof (RDM__DeviceInfoPD); +} + +const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics"; + +void RDM_Responder::processFrame ( void ) +{ + // If packet is a general broadcast + if ( + m_msg.dstUid.isBroadcast (m_devid.m_id) || + m_devid == m_msg.dstUid + ) + { + // Set default response type + m_msg.portId = rdm::ResponseTypeAck; + + switch ( BSWAP_16(m_msg.PID) ) + { + case rdm::DiscUniqueBranch: + // Check if we are inside the given unique branch... + if ( !m_rdmStatus.mute && + reinterpret_cast(m_msg.PD)->lbound < m_devid && + reinterpret_cast(m_msg.PD)->hbound > m_devid ) + { + // Discovery messages are responded with data only and no breaks + repondDiscUniqueBranch (); + } + break; + + case rdm::DiscMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = true; + break; + + case rdm::DiscUnMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = false; + break; + + case rdm::SupportedParameters: + // + // Temporary solution... this will become dynamic + // in a later version... + // + m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB + m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB + + m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality); + m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality); + + m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel); + m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel); + + m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel); + m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel); + + m_msg.PDL = 0x6; + break; + + // Only for manufacturer specific parameters + // case rdm::ParameterDescription: + // break; + + case rdm::DeviceInfo: + if ( m_msg.CC == rdm::GetCommand ) + populateDeviceInfo (); + break; + + case rdm::DmxStartAddress: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ()); + m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ()); + m_msg.PDL = 0x2; + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + __dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + m_msg.PDL = 0x0; + + if ( event_onDMXStartAddressChanged ) + event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + } + break; + + case rdm::DmxPersonality: + if ( m_msg.CC == rdm::GetCommand ) + { + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personality; + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; + m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + m_Personality = reinterpret_cast + (m_msg.PD)->DMX512Personality; + m_msg.PDL = 0x0; + + if ( event_onDMXPersonalityChanged ) + event_onDMXPersonalityChanged ( m_Personality ); + } + break; + + case rdm::IdentifyDevice: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0); + m_msg.PDL = 0x1; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + // Look into first byte to see whether identification + // is turned on or off + m_rdmStatus.ident = m_msg.PD[0] ? true : false; + if ( event_onIdentifyDevice ) + event_onIdentifyDevice ( m_rdmStatus.ident ); + + m_msg.PDL = 0x0; + } + break; + + case rdm::ManufacturerLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) ); + m_msg.PDL = sizeof ( ManufacturerLabel_P ); + } + break; + + case rdm::DeviceLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 ); + m_msg.PDL = 32; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + memset ( (void*) m_deviceLabel, ' ', 32 ); + memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) ); + m_msg.PDL = 0; + + // Notify application + if ( event_onDeviceLabelChanged ) + event_onDeviceLabelChanged ( m_deviceLabel, 32 ); + } + break; + + + default: + // Unknown parameter ID response + m_msg.portId = rdm::ResponseTypeNackReason; + m_msg.PD[0] = rdm::UnknownPid; + m_msg.PD[1] = 0x0; + m_msg.PDL = 0x2; + break; + }; + } + + // + // Only respond if this this message + // was destined to us only + if ( m_msg.dstUid == m_devid ) + { + m_msg.startCode = RDM_START_CODE; + m_msg.subStartCode = 0x01; + m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL; + m_msg.msgCount = 0; + + /* + switch ( m_msg.msg.CC ) + { + case rdm::DiscoveryCommand: + m_msg.msg.CC = rdm::DiscoveryCommandResponse; + break; + case rdm::GetCommand: + m_msg.msg.CC = rdm::GetCommandResponse; + break; + case rdm::SetCommand: + m_msg.msg.CC = rdm::SetCommandResponse; + break; + } + */ + /* Above replaced by next line */ + m_msg.CC++; + + m_msg.dstUid.copy ( m_msg.srcUid ); + m_msg.srcUid.copy ( m_devid ); + + //_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + SetISRMode ( isr::RDMTransmit ); + + } +} + + +void SetISRMode ( isr::isrMode mode ) +{ + uint8_t readEnable; + +#if defined(USE_DMX_SERIAL_0) + #if defined(UCSRB) && defined (UCSRC) + UCSRC |= (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Prepare before kicking off ISR + //DMX_UDR = 0x0; + __isr_rxState = isr::Idle; + readEnable = LOW; + DMX_UCSRB = (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UDR = 0x0; + DMX_UCSRB = 0x0; + readEnable = HIGH; + __isr_txState = isr::DmxBreakManual; + break; + + case isr::RDMTransmit: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + //DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + //DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UCSRB = (1< -1) + digitalWrite ( __re_pin, readEnable ); + +} + +// +// TX UART (DMX Transmission ISR) +// +ISR (USART_TX) +{ + static uint16_t current_slot; + + switch ( __isr_txState ) + { + case isr::DmxBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + if ( __isr_txState == isr::DmxBreak ) + __isr_txState = isr::DmxStartByte; + + break; + + case isr::DmxStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + current_slot = 0; + DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ]; + __isr_txState = isr::DmxTransmitData; + break; + + + case isr::DmxTransmitData: + // NOTE: we always send full frames of 513 bytes, this will bring us + // close to 40 frames / sec with no interslot delays + #ifdef DMX_IBG + _delay_us (DMX_IBG); + #endif + + DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ ); + + // Send 512 channels + if ( current_slot >= DMX_MAX_FRAMESIZE ) + { + if ( __dmx_master->autoBreakEnabled () ) + __isr_txState = isr::DmxBreak; + else + SetISRMode ( isr::DMXTransmitManual ); + } + + break; + +/* case isr::RdmBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + __isr_txState = isr::RdmStartByte; + + break; +*/ + + case isr::RdmStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Write start byte + __rdm_responder->fetchOutgoing ( &DMX_UDR, true ); + __isr_txState = isr::RdmTransmitData; + + break; + + case isr::RdmTransmitData: + // Write rest of data + if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) ) + __isr_txState = isr::RDMTransmitComplete; + break; + + case isr::RDMTransmitComplete: + SetISRMode ( isr::Receive ); // Start waitin for new data + __isr_txState = isr::Idle; // No tx state + break; + + } +} + + + +// +// RX UART (DMX Reception ISR) +// +ISR (USART_RX) +{ + uint8_t usart_state = DMX_UCSRA; + uint8_t usart_data = DMX_UDR; + + // + // Check for framing error and reset if found + // A framing most likely* indicate a break in our ocasion + // + if ( usart_state & (1<processIncoming ( usart_data, true ); + __isr_rxState = isr::DmxRecordData; + } + else if ( __rdm_responder && + usart_data == RDM_START_CODE && + __rdm_responder->m_rdmStatus.enabled ) + { + // __rdm_responder->clear (); + __rdm_responder->processIncoming ( usart_data, true ); + __isr_rxState = isr::RdmRecordData; + } + else + { + __isr_rxState = isr::Idle; + } + break; + + // Process DMX Data + case isr::DmxRecordData: + if ( __dmx_slave->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + // Process RDM Data + case isr::RdmRecordData: + if ( __rdm_responder->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + } + +} + diff --git a/DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.h b/DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.h new file mode 100644 index 000000000..0f03da619 --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave/Conceptinetics.h @@ -0,0 +1,383 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.h - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino / Genuino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino Leonardo using a CTC-DRA-13-R2 ISOLATED DMX-RDM SHIELD + + - CTC-DRA-10-1 and CTC-DRA-10-R2 is the Non-isolated costs effective DMX-RDM shield +*/ + + +#ifndef CONCEPTINETICS_H_ +#define CONCEPTINETICS_H_ + +#include +#include + +#include "Rdm_Uid.h" +#include "Rdm_Defines.h" + +#define DMX_MAX_FRAMESIZE 513 // Startbyte + 512 Slots +#define DMX_MIN_FRAMESIZE 2 // Startbyte + 1 Slot + +#define DMX_MAX_FRAMECHANNELS 512 // Maxmim number of channer per frame + +#define DMX_STARTCODE_SIZE 1 // Size of startcode in bytes + +#define DMX_START_CODE 0x0 // Start code for a DMX frame +#define RDM_START_CODE 0xcc // Start code for a RDM frame + +// Uncomment to enable Inter slot delay ) (avg < 76uSec) ... +// mimum is zero according to specification +// #define DMX_IBG 10 // Inter slot time + +// Speed your Arduino is running on in Hz. +#define F_OSC 16000000UL + +// DMX Baudrate, this should be 250000 +#define DMX_BAUD_RATE 250000 + +// The baudrate used to automaticly generate a break within +// your ISR.. make it lower to generate longer breaks +//#define DMX_BREAK_RATE 99900 + +// 2017, Feb 28: Set to appox 176us +#define DMX_BREAK_RATE 49950 + +// Tabel 3-2 ANSI_E1-20-2010 +// Minimum time to allow the datalink to 'turn arround' +#define MIN_RESPONDER_PACKET_SPACING_USEC 176 /*176*/ + +// Define which serial port to use as DMX port, only one can be +// selected at the time by uncommenting one of the following +// lines +#define USE_DMX_SERIAL_0 +//#define USE_DMX_SERIAL_1 +//#define USE_DMX_SERIAL_2 +//#define USE_DMX_SERIAL_3 + +namespace dmx +{ + enum dmxState + { + dmxUnknown, + dmxStartByte, + dmxWaitStartAddress, + dmxData, + dmxFrameReady, + }; +}; + +namespace rdm +{ + enum rdmState + { + rdmUnknown, + rdmStartByte, + rdmSubStartCode, + rdmMessageLength, + rdmData, + rdmChecksumHigh, + rdmChecksumLow, + rdmFrameReady, + }; +}; + +struct IFrameBuffer +{ + virtual uint16_t getBufferSize ( void ) = 0; + + virtual uint8_t getSlotValue ( uint16_t index ) = 0; + virtual void setSlotValue ( uint16_t index, uint8_t value ) = 0; +}; + +class DMX_FrameBuffer : IFrameBuffer +{ + public: + // + // Constructor buffersize = 1-513 + // + DMX_FrameBuffer ( uint16_t buffer_size ); + DMX_FrameBuffer ( DMX_FrameBuffer &buffer ); + ~DMX_FrameBuffer ( void ); + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void setSlotRange ( uint16_t start, uint16_t end, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + private: + + uint8_t *m_refcount; + uint16_t m_bufferSize; + uint8_t *m_buffer; +}; + + +// +// DMX Master controller +// +class DMX_Master +{ + public: + // Run the DMX master from a pre allocated frame buffer which + // you have fully under your own control + DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ); + + // Run the DMX master by giving a predefined maximum number of + // channels to support + DMX_Master ( uint16_t maxChannel, int readEnablePin ); + + ~DMX_Master ( void ); + + void enable ( void ); // Start transmitting + void disable ( void ); // Stop transmitting + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + // Update channel values + void setChannelValue ( uint16_t channel, uint8_t value ); + void setChannelRange ( uint16_t start, uint16_t end, uint8_t value ); + + public: + // + // Manual control over the break period + // + void setAutoBreakMode ( void ); // Generated from ISR + void setManualBreakMode ( void ); // Generate manually + + uint8_t autoBreakEnabled ( void ); + + // We are waiting for a manual break to be generated + uint8_t waitingBreak ( void ); + + // Generate break and start transmission of frame + void breakAndContinue ( uint8_t breakLength_us = 100 ); + + + protected: + void setStartCode ( uint8_t value ); + + + private: + DMX_FrameBuffer m_frameBuffer; + uint8_t m_autoBreak; +}; + + +// +// DMX Slave controller +// +class DMX_Slave : public DMX_FrameBuffer +{ + public: + DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin = -1 ); + + // nrChannels is the consecutive DMX512 slots required + // to operate this slave device + DMX_Slave ( uint16_t nrChannels, int readEnablePin = -1 ); + + ~DMX_Slave ( void ); + + void enable ( void ); // Enable receiver + void disable ( void ); // Disable receiver + + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + uint8_t getChannelValue ( uint16_t channel ); + + uint16_t getStartAddress ( void ); + void setStartAddress ( uint16_t ); + + + // Process incoming byte from USART + bool processIncoming ( uint8_t val, bool first = false ); + + // Register on receive complete callback in case + // of time critical applications + void onReceiveComplete ( void (*func)(unsigned short) ); + + protected: + + + private: + uint16_t m_startAddress; // Slave start address + dmx::dmxState m_state; + + static void (*event_onFrameReceived)(unsigned short channelsReceived); +}; + + +class RDM_FrameBuffer : public IFrameBuffer +{ + public: + // + // Constructor + // + RDM_FrameBuffer ( void ) {}; + ~RDM_FrameBuffer ( void ) {}; + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + public: // functions to provide access from USART + // Process incoming byte from USART, + // returns false when no more data is accepted + bool processIncoming ( uint8_t val, bool first = false ); + + // Process outgoing byte to USART + // returns false when no more data is available + bool fetchOutgoing ( volatile uint8_t *udr, bool first = false ); + + protected: + // Process received frame + virtual void processFrame ( void ) = 0; + + //private: + protected: + rdm::rdmState m_state; // State for pushing the message in + RDM_Message m_msg; + RDM_Checksum m_csRecv; // Checksum received in rdm message +}; + +// +// RDM_Responder +// +class RDM_Responder : public RDM_FrameBuffer +{ + public: + // + // m = manufacturer id (16bits) + // d1-d4 = device id (32bits) + // + RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, DMX_Slave &slave); + ~RDM_Responder ( void ); + + void setDeviceInfo + ( + uint16_t deviceModelId, + rdm::RdmProductCategory productCategory, + uint8_t personalities = 1, + uint8_t personality = 1 + ) + { + m_DeviceModelId = deviceModelId; + m_ProductCategory = productCategory; + m_Personalities = personalities; + m_Personality = personality; + }; + + // + // Set vendor software version id + // + // v1 = MOST SIGNIFICANT + // v2... + // v3... + // v4 = LEAST SIGNIFICANT + // + void setSoftwareVersionId ( uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4 ) + { + m_SoftwareVersionId[0] = v1; + m_SoftwareVersionId[1] = v2; + m_SoftwareVersionId[2] = v3; + m_SoftwareVersionId[3] = v4; + } + + // Currently no sensors and subdevices supported + // void AddSensor ( void ); + // void AddSubDevice ( void ); + + uint8_t getPersonality ( void ) { return m_Personality; }; + void setPersonality ( uint8_t personality ) { m_Personality = personality; }; + + // Register on identify device event handler + void onIdentifyDevice ( void (*func)(bool) ); + void onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ); + void onDMXStartAddressChanged ( void (*func) (uint16_t) ); + void onDMXPersonalityChanged ( void (*func) (uint8_t) ); + + + // Set the device label + void setDeviceLabel ( const char *label, size_t len ); + + // Enable, Disable rdm responder + void enable ( void ) { m_rdmStatus.enabled = true; m_rdmStatus.mute = false; }; + void disable ( void ) { m_rdmStatus.enabled = false; }; + + union + { + uint8_t raw; + struct + { + uint8_t mute:1; + uint8_t ident:1; + uint8_t enabled:1; // Rdm responder enable/disable + }; + } m_rdmStatus; + + + protected: + virtual void processFrame ( void ); + + // Discovery to unque brach packets only requires + // the data part of the packet to be transmitted + // without breaks or header + void repondDiscUniqueBranch ( void ); + + // Helpers for generating response packets which + // have larger datafields + void populateDeviceInfo ( void ); + + private: + RDM_Uid m_devid; // Holds our unique device ID + uint8_t m_Personalities; // The total number of supported personalities + uint8_t m_Personality; // The currently active personality + uint16_t m_DeviceModelId; + uint8_t m_SoftwareVersionId[4]; // 32 bit Software version + rdm::RdmProductCategory m_ProductCategory; + + char m_deviceLabel[32]; // Device label + + static void (*event_onIdentifyDevice)(bool); + static void (*event_onDeviceLabelChanged)(const char*, uint8_t); + static void (*event_onDMXStartAddressChanged)(uint16_t); + static void (*event_onDMXPersonalityChanged)(uint8_t); +}; + + +#endif /* CONCEPTINETICS_H_ */ diff --git a/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.cpp b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.cpp new file mode 100644 index 000000000..36ada5924 --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.cpp @@ -0,0 +1,1233 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.cpp - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD +*/ + + +#include "pins_arduino.h" +#include "Conceptinetics.h" + +#include +#include + +#include +#include + +#include + + +#if defined (USE_DMX_SERIAL_0) + + #if defined (USART__TXC_vect) + #define USART_TX USART__TXC_vect + #elif defined(USART_TX_vect) + #define USART_TX USART_TX_vect + #elif defined(USART0_TX_vect) + #define USART_TX USART0_TX_vect + #endif + + #if defined (USART__RXC_vect) + #define USART_RX USART__RXC_vect + #elif defined(USART_RX_vect) + #define USART_RX USART_RX_vect + #elif defined(USART0_RX_vect) + #define USART_RX USART0_RX_vect + #endif + + #if defined UDR + #define DMX_UDR UDR + #elif defined UDR0 + #define DMX_UDR UDR0 + #endif + + #if defined(UBRRH) && defined(UBRRL) + #define DMX_UBRRH UBRRH + #define DMX_UBRRL UBRRL + #elif defined(UBRR0H) && defined(UBRR0L) + #define DMX_UBRRH UBRR0H + #define DMX_UBRRL UBRR0L + #endif + + #if defined(UCSRA) + #define DMX_UCSRA UCSRA + #elif defined(UCSR0A) + #define DMX_UCSRA UCSR0A + #endif + + #if defined(UCSRB) + #define DMX_UCSRB UCSRB + #elif defined (UCSR0B) + #define DMX_UCSRB UCSR0B + #endif + + #if defined(TXEN) && defined(TXCIE) + #define DMX_TXEN TXEN + #define DMX_TXCIE TXCIE + #elif defined(TXEN0) && defined(TXCIE0) + #define DMX_TXEN TXEN0 + #define DMX_TXCIE TXCIE0 + #endif + + #if defined(RXEN) && defined(RXCIE) + #define DMX_RXEN RXEN + #define DMX_RXCIE RXCIE + #elif defined(RXEN0) && defined(RXCIE0) + #define DMX_RXEN RXEN0 + #define DMX_RXCIE RXCIE0 + #endif + + #if defined(FE) + #define DMX_FE FE + #elif defined(FE0) + #define DMX_FE FE0 + #endif + + #define RX_PIN 0 + #define TX_PIN 1 + +#elif defined (USE_DMX_SERIAL_1) + #define USART_RX USART1_RX_vect + #define USART_TX USART1_TX_vect + #define DMX_UDR UDR1 + #define DMX_UBRRH UBRR1H + #define DMX_UBRRL UBRR1L + #define DMX_UCSRA UCSR1A + #define DMX_UCSRB UCSR1B + #define DMX_TXEN TXEN1 + #define DMX_TXCIE TXCIE1 + #define DMX_RXEN RXEN1 + #define DMX_RXCIE RXCIE1 + #define DMX_FE FE1 + #define RX_PIN 19 + #define TX_PIN 18 +#elif defined (USE_DMX_SERIAL_2) + #define USART_RX USART2_RX_vect + #define USART_TX USART2_TX_vect + #define DMX_UDR UDR2 + #define DMX_UBRRH UBRR2H + #define DMX_UBRRL UBRR2L + #define DMX_UCSRA UCSR2A + #define DMX_UCSRB UCSR2B + #define DMX_TXEN TXEN2 + #define DMX_TXCIE TXCIE2 + #define DMX_RXEN RXEN2 + #define DMX_RXCIE RXCIE2 + #define DMX_FE FE2 + #define RX_PIN 17 + #define TX_PIN 16 +#elif defined (USE_DMX_SERIAL_3) + #define USART_RX USART3_RX_vect + #define USART_TX USART3_TX_vect + #define DMX_UDR UDR3 + #define DMX_UBRRH UBRR3H + #define DMX_UBRRL UBRR3L + #define DMX_UCSRA UCSR3A + #define DMX_UCSRB UCSR3B + #define DMX_TXEN TXEN3 + #define DMX_TXCIE TXCIE3 + #define DMX_RXEN RXEN3 + #define DMX_RXCIE RXCIE3 + #define DMX_FE FE3 + #define RX_PIN 14 + #define TX_PIN 15 +#endif + + +#define LOWBYTE(v) ((uint8_t) (v)) +#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8)) + +#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 ) + +namespace isr +{ + enum isrState + { + Idle, + Break, + DmxBreak, + DmxBreakManual, + DmxStartByte, + DmxRecordData, + DmxTransmitData, + RdmBreak, + RdmStartByte, + RdmRecordData, + RdmTransmitData, + RDMTransmitComplete, + }; + + enum isrMode + { + Disabled, + Receive, + DMXTransmit, + DMXTransmitManual, /* Manual break... */ + RDMTransmit, + RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */ + }; +}; + + +DMX_Master *__dmx_master; +DMX_Slave *__dmx_slave; +RDM_Responder *__rdm_responder; + +int8_t __re_pin; // R/W Pin on shield + +isr::isrState __isr_txState; // TX ISR state +isr::isrState __isr_rxState; // RX ISR state + + +void SetISRMode ( isr::isrMode ); + + +DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size ) +{ + m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) ); + + if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE ) + { + m_buffer = (uint8_t*) malloc ( buffer_size ); + if ( m_buffer != NULL ) + { + memset ( (void *)m_buffer, 0x0, buffer_size ); + m_bufferSize = buffer_size; + } + else + m_buffer = 0x0; + } + else + m_bufferSize = 0x0; + + *m_refcount++; +} + +DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer ) +{ + // Copy references and make sure the parent object does not dispose our + // buffer when deleted and we are still active + this->m_refcount = buffer.m_refcount; + (*this->m_refcount)++; + + this->m_buffer = buffer.m_buffer; + this->m_bufferSize = buffer.m_bufferSize; +} + +DMX_FrameBuffer::~DMX_FrameBuffer ( void ) +{ + // If we are the last object using the + // allocated buffer then free it together + // with the refcounter + if ( --(*m_refcount) == 0 ) + { + if ( m_buffer ) + free ( m_buffer ); + + free ( m_refcount ); + } +} + +uint16_t DMX_FrameBuffer::getBufferSize ( void ) +{ + return m_bufferSize; +} + + +uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if (index < m_bufferSize) + return m_buffer[index]; + else + return 0x0; +} + + +void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < m_bufferSize ) + m_buffer[index] = value; +} + + +void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start < m_bufferSize && end < m_bufferSize && start < end ) + memset ( (void *) &m_buffer[start], value, end-start+1 ); +} + +void DMX_FrameBuffer::clear ( void ) +{ + memset ( (void *) m_buffer, 0x0, m_bufferSize ); +} + +uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index ) +{ + return m_buffer[index]; +} + + +DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ) +: m_frameBuffer ( buffer ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin ) +: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ), + m_autoBreak ( 1 ) // Autobreak generation is default on +{ + setStartCode ( DMX_START_CODE ); + + __re_pin = readEnablePin; + pinMode ( __re_pin, OUTPUT ); + + ::SetISRMode ( isr::Disabled ); +} + +DMX_Master::~DMX_Master ( void ) +{ + disable (); // Stop sending + __dmx_master = NULL; +} + +DMX_FrameBuffer &DMX_Master::getBuffer ( void ) +{ + return m_frameBuffer; // Return reference to frame buffer +} + +void DMX_Master::setStartCode ( uint8_t value ) +{ + m_frameBuffer[0] = value; // Set the first byte in our frame buffer +} + +void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value ) +{ + if ( channel > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotValue ( channel, value ); +} + +void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value ) +{ + if ( start > 0 ) // Prevent overwriting the start code + m_frameBuffer.setSlotRange ( start, end, value ); +} + + +void DMX_Master::enable ( void ) +{ + __dmx_master = this; + + if ( m_autoBreak ) + ::SetISRMode ( isr::DMXTransmit ); + else + ::SetISRMode ( isr::DMXTransmitManual ); +} + +void DMX_Master::disable ( void ) +{ + ::SetISRMode ( isr::Disabled ); + __dmx_master = NULL; // No active master +} + +void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; } +void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; } +uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; } + + +uint8_t DMX_Master::waitingBreak ( void ) +{ + return ( __isr_txState == isr::DmxBreakManual ); +} + +void DMX_Master::breakAndContinue ( uint8_t breakLength_us ) +{ + // Only execute if we are the controlling master object + if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual ) + { + pinMode ( TX_PIN, OUTPUT ); + digitalWrite ( TX_PIN, LOW ); // Begin BREAK + + for (uint8_t bl=0; bl(*this); +} + + uint8_t DMX_Slave::getChannelValue ( uint16_t channel ) +{ + return getSlotValue ( channel ); +} + + +uint16_t DMX_Slave::getStartAddress ( void ) +{ + return m_startAddress; +} + +void DMX_Slave::setStartAddress ( uint16_t addr ) +{ + m_startAddress = addr; +} + +void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) ) +{ + event_onFrameReceived = func; +} + + +bool DMX_Slave::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + // We could have received less channels then we + // expected.. but still is a complete frame + if (m_state == dmx::dmxData && event_onFrameReceived) + event_onFrameReceived (idx); + + m_state = dmx::dmxStartByte; + } + + switch ( m_state ) + { + case dmx::dmxStartByte: + setSlotValue ( 0, val ); // Store start code + idx = m_startAddress; + m_state = dmx::dmxWaitStartAddress; + + case dmx::dmxWaitStartAddress: + if ( --idx == 0 ) + m_state = dmx::dmxData; + break; + + case dmx::dmxData: + if ( idx++ < getBufferSize() ) + setSlotValue ( idx, val ); + else + { + m_state = dmx::dmxFrameReady; + + // If a onFrameReceived callback is register... + if (event_onFrameReceived) + event_onFrameReceived (idx-2); + + rval = true; + } + break; + } + + return rval; +} + + +uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); } + +uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index ) +{ + if ( index < sizeof ( m_msg ) ) + return m_msg.d[index]; + else + return 0x0; +} + + +void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value ) +{ + if ( index < sizeof ( m_msg ) ) + m_msg.d[index] = value; +} + +void RDM_FrameBuffer::clear ( void ) +{ + memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) ); + m_state = rdm::rdmUnknown; +} + +bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first ) +{ + static uint16_t idx; + bool rval = false; + + if ( first ) + { + m_state = rdm::rdmStartByte; + m_csRecv.checksum = (uint16_t) 0x0000; + idx = 0; + } + + // Prevent buffer overflow for large messages + if (idx >= sizeof(m_msg)) + return true; + + switch ( m_state ) + { + case rdm::rdmStartByte: + m_msg.startCode = val; + m_state = rdm::rdmSubStartCode; + break; + + case rdm::rdmSubStartCode: + if ( val != 0x01 ) + { + rval = true; // Stop processing data + break; + } + + m_msg.subStartCode = val; + m_state = rdm::rdmMessageLength; + break; + + case rdm::rdmMessageLength: + m_msg.msgLength = val; + m_state = rdm::rdmData; + m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum + idx = 3; // buffer index for next byte + break; + + case rdm::rdmData: + m_msg.d[idx++] = val; + m_csRecv.checksum += val; + if ( idx >= m_msg.msgLength ) + m_state = rdm::rdmChecksumHigh; + break; + + case rdm::rdmChecksumHigh: + m_csRecv.csh = val; + m_state = rdm::rdmChecksumLow; + + break; + + case rdm::rdmChecksumLow: + m_csRecv.csl = val; + + if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum) + { + m_state = rdm::rdmFrameReady; + + // valid checksum ... start processing + processFrame (); + } + + m_state = rdm::rdmUnknown; + rval = true; + break; + }; + + return rval; +} + +bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first ) +{ + static uint16_t idx; + static uint16_t cs; + bool rval = false; + + + if ( first ) + { + m_state = rdm::rdmData; + cs = 0x0; + idx = 0; + } + + switch ( m_state ) + { + case rdm::rdmData: + cs += m_msg.d[idx]; + *udr = m_msg.d[idx++]; + if ( idx >= m_msg.msgLength ) + { + m_state = rdm::rdmChecksumHigh; + } + break; + + case rdm::rdmChecksumHigh: + *udr = (cs >> 8); + m_state = rdm::rdmChecksumLow; + break; + + case rdm::rdmChecksumLow: + *udr = (cs & 0xff); + m_state = rdm::rdmUnknown; + rval = true; + break; + + } + + return rval; +} + + +void (*RDM_Responder::event_onIdentifyDevice)(bool); +void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t); +void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t); +void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t); + +// +// slave parameter is only used to ensure a slave object is present before +// initializing the rdm responder class +// +RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, DMX_Slave &slave ) +: RDM_FrameBuffer ( ), + m_Personalities (1), // Available personlities + m_Personality (1) // Default personality eq 1. +{ + __rdm_responder = this; + m_devid.Initialize ( m, d1, d2, d3, d4 ); + + // Default software version id = 0x00000000 + memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 ); + + // Rdm responder is disabled by default + m_rdmStatus.enabled = false; +} + +RDM_Responder::~RDM_Responder ( void ) +{ + __rdm_responder = NULL; +} + +void RDM_Responder::onIdentifyDevice ( void (*func)(bool) ) +{ + event_onIdentifyDevice = func; +} + +void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ) +{ + event_onDeviceLabelChanged = func; +} + +void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) ) +{ + event_onDMXStartAddressChanged = func; +} + +void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) ) +{ + event_onDMXPersonalityChanged = func; +} + +void RDM_Responder::setDeviceLabel ( const char *label, size_t len ) +{ + if ( len > RDM_MAX_DEVICELABEL_LENGTH ) + len = RDM_MAX_DEVICELABEL_LENGTH; + + memcpy ( (void *)m_deviceLabel, (void *)label, len ); +} + +#define UID_0 0x12 //ESTA device ID +#define UID_1 0x34 +#define UID_2 0x56 +#define UID_3 0x88 +#define UID_4 0x00 +#define UID_5 0x00 + +#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5) + +void RDM_Responder::repondDiscUniqueBranch ( void ) +{ + #if defined(UCSRB) + UCSRB = (1<>8) | 0xaa; + response [21] = (cs>>8) | 0x55; + response [22] = (cs&0xff) | 0xaa; + response [23] = (cs&0xff) | 0x55; + + + // Table 3-2 ANSI_E1-20-2010 <2ms + _delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + + // Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with + // section 3.2.3 + // Set shield to transmit mode (turn arround) + digitalWrite ( __re_pin, HIGH ); + + + for ( int i=0; i<24; i++ ) + { + // Wait until data register is empty + #if defined (UCSR0A) && defined (UDRE0) + while((UCSR0A & (1 <(m_msg.PD); + + pd->protocolVersionMajor = 0x01; + pd->protocolVersionMinor = 0x00; + pd->deviceModelId = BSWAP_16(m_DeviceModelId); + pd->ProductCategory = BSWAP_16(m_ProductCategory); + memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 ); + pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte + pd->DMX512CurrentPersonality = m_Personality; + pd->DMX512NumberPersonalities = m_Personalities; + pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress()); + + pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library + pd->SensorCount = 0x0; // Sensors are not yet supported + + m_msg.PDL = sizeof (RDM__DeviceInfoPD); +} + +const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics"; + +void RDM_Responder::processFrame ( void ) +{ + // If packet is a general broadcast + if ( + m_msg.dstUid.isBroadcast (m_devid.m_id) || + m_devid == m_msg.dstUid + ) + { + // Set default response type + m_msg.portId = rdm::ResponseTypeAck; + + switch ( BSWAP_16(m_msg.PID) ) + { + case rdm::DiscUniqueBranch: + // Check if we are inside the given unique branch... + if ( !m_rdmStatus.mute && + reinterpret_cast(m_msg.PD)->lbound < m_devid && + reinterpret_cast(m_msg.PD)->hbound > m_devid ) + { + // Discovery messages are responded with data only and no breaks + repondDiscUniqueBranch (); + } + break; + + case rdm::DiscMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = true; + break; + + case rdm::DiscUnMute: + reinterpret_cast(m_msg.PD)->ctrlField = 0x0; + m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); + m_rdmStatus.mute = false; + break; + + case rdm::SupportedParameters: + // + // Temporary solution... this will become dynamic + // in a later version... + // + m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB + m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB + + m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality); + m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality); + + m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel); + m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel); + + m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel); + m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel); + + m_msg.PDL = 0x6; + break; + + // Only for manufacturer specific parameters + // case rdm::ParameterDescription: + // break; + + case rdm::DeviceInfo: + if ( m_msg.CC == rdm::GetCommand ) + populateDeviceInfo (); + break; + + case rdm::DmxStartAddress: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ()); + m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ()); + m_msg.PDL = 0x2; + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + __dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + m_msg.PDL = 0x0; + + if ( event_onDMXStartAddressChanged ) + event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] ); + } + break; + + case rdm::DmxPersonality: + if ( m_msg.CC == rdm::GetCommand ) + { + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personality; + reinterpret_cast + (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; + m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); + } + else // if ( m_msg.CC == rdm::SetCommand ) + { + m_Personality = reinterpret_cast + (m_msg.PD)->DMX512Personality; + m_msg.PDL = 0x0; + + if ( event_onDMXPersonalityChanged ) + event_onDMXPersonalityChanged ( m_Personality ); + } + break; + + case rdm::IdentifyDevice: + if ( m_msg.CC == rdm::GetCommand ) + { + m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0); + m_msg.PDL = 0x1; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + // Look into first byte to see whether identification + // is turned on or off + m_rdmStatus.ident = m_msg.PD[0] ? true : false; + if ( event_onIdentifyDevice ) + event_onIdentifyDevice ( m_rdmStatus.ident ); + + m_msg.PDL = 0x0; + } + break; + + case rdm::ManufacturerLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) ); + m_msg.PDL = sizeof ( ManufacturerLabel_P ); + } + break; + + case rdm::DeviceLabel: + if ( m_msg.CC == rdm::GetCommand ) + { + memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 ); + m_msg.PDL = 32; + } + else if ( m_msg.CC == rdm::SetCommand ) + { + memset ( (void*) m_deviceLabel, ' ', 32 ); + memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) ); + m_msg.PDL = 0; + + // Notify application + if ( event_onDeviceLabelChanged ) + event_onDeviceLabelChanged ( m_deviceLabel, 32 ); + } + break; + + + default: + // Unknown parameter ID response + m_msg.portId = rdm::ResponseTypeNackReason; + m_msg.PD[0] = rdm::UnknownPid; + m_msg.PD[1] = 0x0; + m_msg.PDL = 0x2; + break; + }; + } + + // + // Only respond if this this message + // was destined to us only + if ( m_msg.dstUid == m_devid ) + { + m_msg.startCode = RDM_START_CODE; + m_msg.subStartCode = 0x01; + m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL; + m_msg.msgCount = 0; + + /* + switch ( m_msg.msg.CC ) + { + case rdm::DiscoveryCommand: + m_msg.msg.CC = rdm::DiscoveryCommandResponse; + break; + case rdm::GetCommand: + m_msg.msg.CC = rdm::GetCommandResponse; + break; + case rdm::SetCommand: + m_msg.msg.CC = rdm::SetCommandResponse; + break; + } + */ + /* Above replaced by next line */ + m_msg.CC++; + + m_msg.dstUid.copy ( m_msg.srcUid ); + m_msg.srcUid.copy ( m_devid ); + + //_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC ); + SetISRMode ( isr::RDMTransmit ); + + } +} + + +void SetISRMode ( isr::isrMode mode ) +{ + uint8_t readEnable; + +#if defined(USE_DMX_SERIAL_0) + #if defined(UCSRB) && defined (UCSRC) + UCSRC |= (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Prepare before kicking off ISR + //DMX_UDR = 0x0; + __isr_rxState = isr::Idle; + readEnable = LOW; + DMX_UCSRB = (1<>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UDR = 0x0; + DMX_UCSRB = 0x0; + readEnable = HIGH; + __isr_txState = isr::DmxBreakManual; + break; + + case isr::RDMTransmit: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + //DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + //DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + DMX_UCSRB = (1< -1) + digitalWrite ( __re_pin, readEnable ); + +} + +// +// TX UART (DMX Transmission ISR) +// +ISR (USART_TX) +{ + static uint16_t current_slot; + + switch ( __isr_txState ) + { + case isr::DmxBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + if ( __isr_txState == isr::DmxBreak ) + __isr_txState = isr::DmxStartByte; + + break; + + case isr::DmxStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + current_slot = 0; + DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ]; + __isr_txState = isr::DmxTransmitData; + break; + + + case isr::DmxTransmitData: + // NOTE: we always send full frames of 513 bytes, this will bring us + // close to 40 frames / sec with no interslot delays + #ifdef DMX_IBG + _delay_us (DMX_IBG); + #endif + + DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ ); + + // Send 512 channels + if ( current_slot >= DMX_MAX_FRAMESIZE ) + { + if ( __dmx_master->autoBreakEnabled () ) + __isr_txState = isr::DmxBreak; + else + SetISRMode ( isr::DMXTransmitManual ); + } + + break; + +/* case isr::RdmBreak: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1); + DMX_UDR = 0x0; + + __isr_txState = isr::RdmStartByte; + + break; +*/ + + case isr::RdmStartByte: + DMX_UCSRA = 0x0; + DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8); + DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1); + + // Write start byte + __rdm_responder->fetchOutgoing ( &DMX_UDR, true ); + __isr_txState = isr::RdmTransmitData; + + break; + + case isr::RdmTransmitData: + // Write rest of data + if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) ) + __isr_txState = isr::RDMTransmitComplete; + break; + + case isr::RDMTransmitComplete: + SetISRMode ( isr::Receive ); // Start waitin for new data + __isr_txState = isr::Idle; // No tx state + break; + + } +} + + + +// +// RX UART (DMX Reception ISR) +// +ISR (USART_RX) +{ + uint8_t usart_state = DMX_UCSRA; + uint8_t usart_data = DMX_UDR; + + // + // Check for framing error and reset if found + // A framing most likely* indicate a break in our ocasion + // + if ( usart_state & (1<processIncoming ( usart_data, true ); + __isr_rxState = isr::DmxRecordData; + } + else if ( __rdm_responder && + usart_data == RDM_START_CODE && + __rdm_responder->m_rdmStatus.enabled ) + { + // __rdm_responder->clear (); + __rdm_responder->processIncoming ( usart_data, true ); + __isr_rxState = isr::RdmRecordData; + } + else + { + __isr_rxState = isr::Idle; + } + break; + + // Process DMX Data + case isr::DmxRecordData: + if ( __dmx_slave->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + // Process RDM Data + case isr::RdmRecordData: + if ( __rdm_responder->processIncoming ( usart_data ) ) + __isr_rxState = isr::Idle; + break; + + } + +} + diff --git a/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.h b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.h new file mode 100644 index 000000000..0f03da619 --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Conceptinetics.h @@ -0,0 +1,383 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Conceptinetics.h - DMX library for Arduino + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This code has been tested using the following hardware: + + - Arduino / Genuino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD + - Arduino / Genuino Leonardo using a CTC-DRA-13-R2 ISOLATED DMX-RDM SHIELD + + - CTC-DRA-10-1 and CTC-DRA-10-R2 is the Non-isolated costs effective DMX-RDM shield +*/ + + +#ifndef CONCEPTINETICS_H_ +#define CONCEPTINETICS_H_ + +#include +#include + +#include "Rdm_Uid.h" +#include "Rdm_Defines.h" + +#define DMX_MAX_FRAMESIZE 513 // Startbyte + 512 Slots +#define DMX_MIN_FRAMESIZE 2 // Startbyte + 1 Slot + +#define DMX_MAX_FRAMECHANNELS 512 // Maxmim number of channer per frame + +#define DMX_STARTCODE_SIZE 1 // Size of startcode in bytes + +#define DMX_START_CODE 0x0 // Start code for a DMX frame +#define RDM_START_CODE 0xcc // Start code for a RDM frame + +// Uncomment to enable Inter slot delay ) (avg < 76uSec) ... +// mimum is zero according to specification +// #define DMX_IBG 10 // Inter slot time + +// Speed your Arduino is running on in Hz. +#define F_OSC 16000000UL + +// DMX Baudrate, this should be 250000 +#define DMX_BAUD_RATE 250000 + +// The baudrate used to automaticly generate a break within +// your ISR.. make it lower to generate longer breaks +//#define DMX_BREAK_RATE 99900 + +// 2017, Feb 28: Set to appox 176us +#define DMX_BREAK_RATE 49950 + +// Tabel 3-2 ANSI_E1-20-2010 +// Minimum time to allow the datalink to 'turn arround' +#define MIN_RESPONDER_PACKET_SPACING_USEC 176 /*176*/ + +// Define which serial port to use as DMX port, only one can be +// selected at the time by uncommenting one of the following +// lines +#define USE_DMX_SERIAL_0 +//#define USE_DMX_SERIAL_1 +//#define USE_DMX_SERIAL_2 +//#define USE_DMX_SERIAL_3 + +namespace dmx +{ + enum dmxState + { + dmxUnknown, + dmxStartByte, + dmxWaitStartAddress, + dmxData, + dmxFrameReady, + }; +}; + +namespace rdm +{ + enum rdmState + { + rdmUnknown, + rdmStartByte, + rdmSubStartCode, + rdmMessageLength, + rdmData, + rdmChecksumHigh, + rdmChecksumLow, + rdmFrameReady, + }; +}; + +struct IFrameBuffer +{ + virtual uint16_t getBufferSize ( void ) = 0; + + virtual uint8_t getSlotValue ( uint16_t index ) = 0; + virtual void setSlotValue ( uint16_t index, uint8_t value ) = 0; +}; + +class DMX_FrameBuffer : IFrameBuffer +{ + public: + // + // Constructor buffersize = 1-513 + // + DMX_FrameBuffer ( uint16_t buffer_size ); + DMX_FrameBuffer ( DMX_FrameBuffer &buffer ); + ~DMX_FrameBuffer ( void ); + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void setSlotRange ( uint16_t start, uint16_t end, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + private: + + uint8_t *m_refcount; + uint16_t m_bufferSize; + uint8_t *m_buffer; +}; + + +// +// DMX Master controller +// +class DMX_Master +{ + public: + // Run the DMX master from a pre allocated frame buffer which + // you have fully under your own control + DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin ); + + // Run the DMX master by giving a predefined maximum number of + // channels to support + DMX_Master ( uint16_t maxChannel, int readEnablePin ); + + ~DMX_Master ( void ); + + void enable ( void ); // Start transmitting + void disable ( void ); // Stop transmitting + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + // Update channel values + void setChannelValue ( uint16_t channel, uint8_t value ); + void setChannelRange ( uint16_t start, uint16_t end, uint8_t value ); + + public: + // + // Manual control over the break period + // + void setAutoBreakMode ( void ); // Generated from ISR + void setManualBreakMode ( void ); // Generate manually + + uint8_t autoBreakEnabled ( void ); + + // We are waiting for a manual break to be generated + uint8_t waitingBreak ( void ); + + // Generate break and start transmission of frame + void breakAndContinue ( uint8_t breakLength_us = 100 ); + + + protected: + void setStartCode ( uint8_t value ); + + + private: + DMX_FrameBuffer m_frameBuffer; + uint8_t m_autoBreak; +}; + + +// +// DMX Slave controller +// +class DMX_Slave : public DMX_FrameBuffer +{ + public: + DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin = -1 ); + + // nrChannels is the consecutive DMX512 slots required + // to operate this slave device + DMX_Slave ( uint16_t nrChannels, int readEnablePin = -1 ); + + ~DMX_Slave ( void ); + + void enable ( void ); // Enable receiver + void disable ( void ); // Disable receiver + + + // Get reference to the internal framebuffer + DMX_FrameBuffer &getBuffer ( void ); + + uint8_t getChannelValue ( uint16_t channel ); + + uint16_t getStartAddress ( void ); + void setStartAddress ( uint16_t ); + + + // Process incoming byte from USART + bool processIncoming ( uint8_t val, bool first = false ); + + // Register on receive complete callback in case + // of time critical applications + void onReceiveComplete ( void (*func)(unsigned short) ); + + protected: + + + private: + uint16_t m_startAddress; // Slave start address + dmx::dmxState m_state; + + static void (*event_onFrameReceived)(unsigned short channelsReceived); +}; + + +class RDM_FrameBuffer : public IFrameBuffer +{ + public: + // + // Constructor + // + RDM_FrameBuffer ( void ) {}; + ~RDM_FrameBuffer ( void ) {}; + + uint16_t getBufferSize ( void ); + + uint8_t getSlotValue ( uint16_t index ); + void setSlotValue ( uint16_t index, uint8_t value ); + void clear ( void ); + + uint8_t &operator[] ( uint16_t index ); + + public: // functions to provide access from USART + // Process incoming byte from USART, + // returns false when no more data is accepted + bool processIncoming ( uint8_t val, bool first = false ); + + // Process outgoing byte to USART + // returns false when no more data is available + bool fetchOutgoing ( volatile uint8_t *udr, bool first = false ); + + protected: + // Process received frame + virtual void processFrame ( void ) = 0; + + //private: + protected: + rdm::rdmState m_state; // State for pushing the message in + RDM_Message m_msg; + RDM_Checksum m_csRecv; // Checksum received in rdm message +}; + +// +// RDM_Responder +// +class RDM_Responder : public RDM_FrameBuffer +{ + public: + // + // m = manufacturer id (16bits) + // d1-d4 = device id (32bits) + // + RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, DMX_Slave &slave); + ~RDM_Responder ( void ); + + void setDeviceInfo + ( + uint16_t deviceModelId, + rdm::RdmProductCategory productCategory, + uint8_t personalities = 1, + uint8_t personality = 1 + ) + { + m_DeviceModelId = deviceModelId; + m_ProductCategory = productCategory; + m_Personalities = personalities; + m_Personality = personality; + }; + + // + // Set vendor software version id + // + // v1 = MOST SIGNIFICANT + // v2... + // v3... + // v4 = LEAST SIGNIFICANT + // + void setSoftwareVersionId ( uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4 ) + { + m_SoftwareVersionId[0] = v1; + m_SoftwareVersionId[1] = v2; + m_SoftwareVersionId[2] = v3; + m_SoftwareVersionId[3] = v4; + } + + // Currently no sensors and subdevices supported + // void AddSensor ( void ); + // void AddSubDevice ( void ); + + uint8_t getPersonality ( void ) { return m_Personality; }; + void setPersonality ( uint8_t personality ) { m_Personality = personality; }; + + // Register on identify device event handler + void onIdentifyDevice ( void (*func)(bool) ); + void onDeviceLabelChanged ( void (*func) (const char*, uint8_t) ); + void onDMXStartAddressChanged ( void (*func) (uint16_t) ); + void onDMXPersonalityChanged ( void (*func) (uint8_t) ); + + + // Set the device label + void setDeviceLabel ( const char *label, size_t len ); + + // Enable, Disable rdm responder + void enable ( void ) { m_rdmStatus.enabled = true; m_rdmStatus.mute = false; }; + void disable ( void ) { m_rdmStatus.enabled = false; }; + + union + { + uint8_t raw; + struct + { + uint8_t mute:1; + uint8_t ident:1; + uint8_t enabled:1; // Rdm responder enable/disable + }; + } m_rdmStatus; + + + protected: + virtual void processFrame ( void ); + + // Discovery to unque brach packets only requires + // the data part of the packet to be transmitted + // without breaks or header + void repondDiscUniqueBranch ( void ); + + // Helpers for generating response packets which + // have larger datafields + void populateDeviceInfo ( void ); + + private: + RDM_Uid m_devid; // Holds our unique device ID + uint8_t m_Personalities; // The total number of supported personalities + uint8_t m_Personality; // The currently active personality + uint16_t m_DeviceModelId; + uint8_t m_SoftwareVersionId[4]; // 32 bit Software version + rdm::RdmProductCategory m_ProductCategory; + + char m_deviceLabel[32]; // Device label + + static void (*event_onIdentifyDevice)(bool); + static void (*event_onDeviceLabelChanged)(const char*, uint8_t); + static void (*event_onDMXStartAddressChanged)(uint16_t); + static void (*event_onDMXPersonalityChanged)(uint8_t); +}; + + +#endif /* CONCEPTINETICS_H_ */ From af36141979260304691759f66085d8c16e0eb92f Mon Sep 17 00:00:00 2001 From: Liz Date: Thu, 27 Feb 2025 15:10:41 -0500 Subject: [PATCH 3/3] RDM files required, change import to local instead of /libraries --- .../examples/DMX_Master/DMX_Master.ino | 2 +- .../examples/DMX_Master/Rdm_Defines.h | 315 ++++++++++++++++++ DMX_NeoPixels/examples/DMX_Master/Rdm_Uid.h | 101 ++++++ .../DMX_Master_Manual_Break.ino | 2 +- .../DMX_Master_Manual_Break/Rdm_Defines.h | 315 ++++++++++++++++++ .../DMX_Master_Manual_Break/Rdm_Uid.h | 101 ++++++ .../examples/DMX_Slave/DMX_Slave.ino | 2 +- .../examples/DMX_Slave/Rdm_Defines.h | 315 ++++++++++++++++++ DMX_NeoPixels/examples/DMX_Slave/Rdm_Uid.h | 101 ++++++ .../DMX_Slave_Signal_Timeout.ino | 2 +- .../DMX_Slave_Signal_Timeout/Rdm_Defines.h | 315 ++++++++++++++++++ .../DMX_Slave_Signal_Timeout/Rdm_Uid.h | 101 ++++++ .../examples/RDM_Slave/RDM_Slave.ino | 2 +- .../examples/RDM_Slave/Rdm_Defines.h | 315 ++++++++++++++++++ DMX_NeoPixels/examples/RDM_Slave/Rdm_Uid.h | 101 ++++++ .../RDM_Slave_Eeprom/RDM_Slave_Eeprom.ino | 2 +- .../examples/RDM_Slave_Eeprom/Rdm_Defines.h | 315 ++++++++++++++++++ .../examples/RDM_Slave_Eeprom/Rdm_Uid.h | 101 ++++++ 18 files changed, 2502 insertions(+), 6 deletions(-) create mode 100644 DMX_NeoPixels/examples/DMX_Master/Rdm_Defines.h create mode 100644 DMX_NeoPixels/examples/DMX_Master/Rdm_Uid.h create mode 100644 DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Defines.h create mode 100644 DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Uid.h create mode 100644 DMX_NeoPixels/examples/DMX_Slave/Rdm_Defines.h create mode 100644 DMX_NeoPixels/examples/DMX_Slave/Rdm_Uid.h create mode 100644 DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Defines.h create mode 100644 DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Uid.h create mode 100644 DMX_NeoPixels/examples/RDM_Slave/Rdm_Defines.h create mode 100644 DMX_NeoPixels/examples/RDM_Slave/Rdm_Uid.h create mode 100644 DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Defines.h create mode 100644 DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Uid.h diff --git a/DMX_NeoPixels/examples/DMX_Master/DMX_Master.ino b/DMX_NeoPixels/examples/DMX_Master/DMX_Master.ino index e6cf3e9af..98d7284e8 100644 --- a/DMX_NeoPixels/examples/DMX_Master/DMX_Master.ino +++ b/DMX_NeoPixels/examples/DMX_Master/DMX_Master.ino @@ -21,7 +21,7 @@ */ -#include +#include "Conceptinetics.h" // diff --git a/DMX_NeoPixels/examples/DMX_Master/Rdm_Defines.h b/DMX_NeoPixels/examples/DMX_Master/Rdm_Defines.h new file mode 100644 index 000000000..0accca63d --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master/Rdm_Defines.h @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Defines.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_DEFINES_H_ +#define RDM_DEFINES_H_ + +#include "Rdm_Uid.h" + +#define RDM_MAX_DEVICELABEL_LENGTH 32 + +namespace rdm +{ + enum RdmCommandClass + { + DiscoveryCommand = 0x10, + DiscoveryCommandResponse, + GetCommand = 0x20, + GetCommandResponse, + SetCommand = 0x30, + SetCommandResponse, + }; + + enum RdmResponseTypes + { + ResponseTypeAck = 0x00, + ResponseTypeAckTimer, + ResponseTypeNackReason, + ResponseTypeAckOverflow, // Additional response data available (see spec) + }; + + enum RdmParameters + { + // Category - Network Management + DiscUniqueBranch = 0x0001, // Required + DiscMute = 0x0002, // Required + DiscUnMute = 0x0003, // Required + + CommsStatus = 0x0015, // Get,Set + + // Category - Status Collection + QueuedMessage = 0x0020, // Get [enum RdmStatusTypes] + StatusMessages = 0x0030, // Get [enum RdmStatusTypes] + StatusIdDescription = 0x0031, // Get + ClearStatusId = 0x0032, // Set + SubDeviceStatusReportThreshold = 0x0033, // Get, Set [enum RdmStatusTypes] + + // Category - RDM Information + // ** Only required if supporting parameters + // beyond the minimum required set + SupportedParameters = 0x0005, // Get, **Required + ParameterDescription = 0x0051, // Get, **Required + + // Category = Product Information + DeviceInfo = 0x0060, // Get, Required + ProductDetailIdList = 0x0070, // Get + DeviceModelDescription = 0x0080, // Get + ManufacturerLabel = 0x0081, // Get + DeviceLabel = 0x0082, // Get, Set + FactoryDefaults = 0x0009, // Get, Set ** + SoftwareVersionLabel = 0x000c, // Get + + // Category - DMX512 Setup + DmxPersonality = 0x00e0, // Get, Set + DmxPersonalityDescription = 0x00e1, // Get + DmxStartAddress = 0x00f0, // Get, Set ** Required if DMX device + SlotInfo = 0x0120, // Get + SlotDescription = 0x0121, // Get + DefaultSlotValue = 0x0122, // Get + + // Category - Sensors + // Category - Dimmer Settings + // Category - Power/Lamp Settings + // Category - Display Settings + // Category - Configuration + + // Category - Control + IdentifyDevice = 0x1000, // Get, Set, Required + ResetDevice = 0x1001, // Set + PowerState = 0x1010, // Get, Set + PerformSelftest = 0x1020, // Get, Set + SelfTestDescription = 0x1021, // Get + }; + + + enum RdmStatusTypes + { + StatusNone = 0x00, + StatusGetLastMessage, + StatusAdvisory, + StatusWarning, + StatusError, + StatusAdvisoryCleared = 0x12, + StatusWarningCleared, + StatusErrorCleared, + }; + + enum RdmProductCategory + { + CategoryNotDeclared = 0x0000, + + // Fixtures - intended as source for illumination + CategoryFixture = 0x0100, + CategoryFixtureFixed = 0x0101, + CategoryFixtureMovingYoke = 0x0102, + CategoryFixtureMovingMirror = 0x0103, + CategoryFixtureOther = 0x01ff, + + // Fixture Accessories - add-ons to fixtures or projectors + CategoryFixtureAccessory = 0x0200, + CategoryFixtureAccessoryColor = 0x0201, + CategoryFixtureAccessoryYoke = 0x0202, + CategoryFixtureAccessoryMirror = 0x0203, + CategoryFixtureAccessoryEffect = 0x0204, + CategoryFixtureAccessoryBeam = 0x0205, + CategoryFixtureAccessoryOther = 0x02ff, + + // Projectors - Light source capable of producing + // realistic images from another media + CategoryProjector = 0x0300, + CategoryProjectorFixed = 0x0301, + CategoryProjectorMovingYoke = 0x0302, + CategoryProjectorMovingMirror = 0x0303, + CategoryProjectorOther = 0x03ff, + + // Atmospheric Effect - earth/wind/fire + CategoryAtmospheric = 0x0400, + CategoryAtmosphericEffect = 0x0401, // Fogger, Hazer, Flame + CategoryAtmosphericPyro = 0x0402, + CategoryAtmosphericOther = 0x04ff, + + // Insensity Control (Specifically dimming equipment) + CategoryDimmer = 0x0500, + CategoryDimmer_AC_Incandescent = 0x0501, + CategoryDimmer_AC_Fluorescent = 0x0502, + CategoryDimmer_AC_Coldcathode = 0x0503, + CategoryDimmer_AC_Nondim = 0x0504, + CategoryDimmer_AC_Elv = 0x0505, + CategoryDimmer_AC_Other = 0x0506, + CategoryDimmer_DC_Level = 0x0507, + CategoryDimmer_DC_PWM = 0x0508, + CategoryDimmer_CS_LED = 0x0509, + CategoryDimmer_Other = 0x05ff, + + // Power control (Other than dimming equipment) + CategoryPower = 0x0600, + CategoryPowerControl = 0x0601, + CategoryPowerSource = 0x0602, + CategoryPowerOther = 0x06ff, + + // Scenic Drive - Including motorized effects + // unrelated to light source + CategoryScenic = 0x0700, + CategoryScenicDrive = 0x0701, + CategoryScenicOther = 0x07ff, + + // DMX Infrastructure, conversion and interfaces + CategoryData = 0x0800, + CategoryDataDistribution = 0x0801, + CategoryDataConversion = 0x0802, + CategoryDataOther = 0x08ff, + + // Audio visual equipment + Category_AV = 0x0900, + Category_AV_Audio = 0x0901, + Category_AV_Video = 0x0902, + Category_AV_Other = 0x09ff, + + // Parameter monitoring equipment + CategoryMonitor = 0x0a00, + CategoryMonitorACLinePower = 0x0a01, + CategoryMonitorDCPower = 0x0a02, + CategoryMonitorEnvironmental = 0x0a03, + CategoryMonitorOther = 0x0aff, + + // Controllers, backup devices + CategoryControl = 0x7000, + CategoryControlController = 0x7001, + CategoryControlBackupdevice = 0x7002, + CategoryControlOther = 0x70ff, + + // Test equipment + CategoryTest = 0x7100, + CategoryTestEquipment = 0x7101, + CategoryTestEquipmentOther = 0x71ff, + + // Miscellaneous + CategoryOther = 0x7fff, + }; + + // + // Product details not yet supported in + // this library + // + enum RdmProductDetail + { + ProductDetailNotDeclared = 0x0000, + }; + + // Only LSB + enum RdmNackReasons + { + UnknownPid = 0x00, + FormatError, + HardwareFault, + ProxyReject, + WriteProtect, + UnsupportedCmdClass, + DataOutOfRange, + BufferFull, + PacketSizeUnsupported, + SubDeviceOutOfRange, + ProxyBufferFull + }; + +}; + + +#define RDM_HDR_LEN 24 // RDM Message header length ** fixed +#define RDM_PD_MAXLEN 32 // RDM Maximum parameter data length 1 - 231 + + +union RDM_Message +{ + uint8_t d[ RDM_HDR_LEN + RDM_PD_MAXLEN ]; + struct + { + uint8_t startCode; // 0 SC_RDM + uint8_t subStartCode; // 1 SC_SUB_MESSAGE + uint8_t msgLength; // 2 Range 24 - 255 + RDM_Uid dstUid; // 3-8 Destination UID + RDM_Uid srcUid; // 9-14 Source UID (sender) + uint8_t TN; // 15 Transaction number + uint8_t portId; // 16 Port ID / Response type + uint8_t msgCount; // 17 + uint16_t subDevice; // 18,19 0=root, 0xffff=all + uint8_t CC; // 20 GET_COMMAND + uint16_t PID; // 21,22 Parameter ID + uint8_t PDL; // 23 Parameter Data length 1-231 + + uint8_t PD[RDM_PD_MAXLEN]; // Parameter Data ... variable length + }; +}; + +union RDM_Checksum +{ + uint16_t checksum; + struct + { + uint8_t csl; + uint8_t csh; + }; +}; + +struct RDM_DiscUniqueBranchPD +{ + RDM_Uid lbound; + RDM_Uid hbound; +}; + +struct RDM_DiscMuteUnMutePD +{ + uint16_t ctrlField; + +// Only for multiple ports +// RDM_Uid bindingUid; +}; + +struct RDM__DeviceInfoPD +{ + uint8_t protocolVersionMajor; + uint8_t protocolVersionMinor; + uint16_t deviceModelId; + uint16_t ProductCategory; // enum RdmProductCategory + uint8_t SoftwareVersionId[4]; + uint16_t DMX512FootPrint; + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; + uint16_t DMX512StartAddress; + uint16_t SubDeviceCount; + uint8_t SensorCount; +}; + +struct RDM_DeviceGetPersonality_PD +{ + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; +}; + +struct RDM_DeviceSetPersonality_PD +{ + uint8_t DMX512Personality; +}; + + +#endif /* RDM_DEFINES_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Master/Rdm_Uid.h b/DMX_NeoPixels/examples/DMX_Master/Rdm_Uid.h new file mode 100644 index 000000000..35c802c63 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master/Rdm_Uid.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Uid.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_UID_H_ +#define RDM_UID_H_ + +#include + +// +//48 bit UID Representation to identify RDM transponders +// +struct RDM_Uid { + + void Initialize ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4 ) + { + m_id[0] = ((uint8_t) (((uint16_t) (m)) >> 8)); + m_id[1] = (uint8_t)m; + m_id[2] = d1; + m_id[3] = d2; + m_id[4] = d3; + m_id[5] = d4; + } + + void copy ( const RDM_Uid &orig ) + { + for ( uint8_t i = 0; i < 6; i++ ) + m_id[i] = orig.m_id[i]; + } + + bool operator == ( const RDM_Uid & orig ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != orig.m_id[i] ) + return false; + + return true; + } + + bool operator != ( const RDM_Uid & orig ) const + { + return !(*this == orig); + } + + bool operator < ( const RDM_Uid & v ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] < v.m_id[i] ); + } + + bool operator > ( const RDM_Uid & v ) + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] > v.m_id[i] ); + } + + // + // match_mid = manufacturer id to match + // + bool isBroadcast ( uint8_t match_mid[2] ) + { + // Check for genuine broadcast on device part + for ( uint8_t i = 2; i < 6; i++ ) + if ( m_id[i] != 0xff ) + return false; + + // Broadcast or manufacturer designated broadcast + if ( (m_id[0] == 0xff && m_id[1] == 0xff) || + (m_id[0] == match_mid[0] && m_id[1] == match_mid[1]) ) + return true; + + // No broadcast + return false; + } + + uint8_t m_id[6]; //16bit manufacturer id + 32 bits device id +}; + + +#endif /* RDM_UID_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Master_Manual_Break/DMX_Master_Manual_Break.ino b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/DMX_Master_Manual_Break.ino index 6d80a78a3..cb51afb53 100644 --- a/DMX_NeoPixels/examples/DMX_Master_Manual_Break/DMX_Master_Manual_Break.ino +++ b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/DMX_Master_Manual_Break.ino @@ -21,7 +21,7 @@ */ -#include +#include "Conceptinetics.h" // // When configuring a DMX_Master it will normally automaticly diff --git a/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Defines.h b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Defines.h new file mode 100644 index 000000000..0accca63d --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Defines.h @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Defines.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_DEFINES_H_ +#define RDM_DEFINES_H_ + +#include "Rdm_Uid.h" + +#define RDM_MAX_DEVICELABEL_LENGTH 32 + +namespace rdm +{ + enum RdmCommandClass + { + DiscoveryCommand = 0x10, + DiscoveryCommandResponse, + GetCommand = 0x20, + GetCommandResponse, + SetCommand = 0x30, + SetCommandResponse, + }; + + enum RdmResponseTypes + { + ResponseTypeAck = 0x00, + ResponseTypeAckTimer, + ResponseTypeNackReason, + ResponseTypeAckOverflow, // Additional response data available (see spec) + }; + + enum RdmParameters + { + // Category - Network Management + DiscUniqueBranch = 0x0001, // Required + DiscMute = 0x0002, // Required + DiscUnMute = 0x0003, // Required + + CommsStatus = 0x0015, // Get,Set + + // Category - Status Collection + QueuedMessage = 0x0020, // Get [enum RdmStatusTypes] + StatusMessages = 0x0030, // Get [enum RdmStatusTypes] + StatusIdDescription = 0x0031, // Get + ClearStatusId = 0x0032, // Set + SubDeviceStatusReportThreshold = 0x0033, // Get, Set [enum RdmStatusTypes] + + // Category - RDM Information + // ** Only required if supporting parameters + // beyond the minimum required set + SupportedParameters = 0x0005, // Get, **Required + ParameterDescription = 0x0051, // Get, **Required + + // Category = Product Information + DeviceInfo = 0x0060, // Get, Required + ProductDetailIdList = 0x0070, // Get + DeviceModelDescription = 0x0080, // Get + ManufacturerLabel = 0x0081, // Get + DeviceLabel = 0x0082, // Get, Set + FactoryDefaults = 0x0009, // Get, Set ** + SoftwareVersionLabel = 0x000c, // Get + + // Category - DMX512 Setup + DmxPersonality = 0x00e0, // Get, Set + DmxPersonalityDescription = 0x00e1, // Get + DmxStartAddress = 0x00f0, // Get, Set ** Required if DMX device + SlotInfo = 0x0120, // Get + SlotDescription = 0x0121, // Get + DefaultSlotValue = 0x0122, // Get + + // Category - Sensors + // Category - Dimmer Settings + // Category - Power/Lamp Settings + // Category - Display Settings + // Category - Configuration + + // Category - Control + IdentifyDevice = 0x1000, // Get, Set, Required + ResetDevice = 0x1001, // Set + PowerState = 0x1010, // Get, Set + PerformSelftest = 0x1020, // Get, Set + SelfTestDescription = 0x1021, // Get + }; + + + enum RdmStatusTypes + { + StatusNone = 0x00, + StatusGetLastMessage, + StatusAdvisory, + StatusWarning, + StatusError, + StatusAdvisoryCleared = 0x12, + StatusWarningCleared, + StatusErrorCleared, + }; + + enum RdmProductCategory + { + CategoryNotDeclared = 0x0000, + + // Fixtures - intended as source for illumination + CategoryFixture = 0x0100, + CategoryFixtureFixed = 0x0101, + CategoryFixtureMovingYoke = 0x0102, + CategoryFixtureMovingMirror = 0x0103, + CategoryFixtureOther = 0x01ff, + + // Fixture Accessories - add-ons to fixtures or projectors + CategoryFixtureAccessory = 0x0200, + CategoryFixtureAccessoryColor = 0x0201, + CategoryFixtureAccessoryYoke = 0x0202, + CategoryFixtureAccessoryMirror = 0x0203, + CategoryFixtureAccessoryEffect = 0x0204, + CategoryFixtureAccessoryBeam = 0x0205, + CategoryFixtureAccessoryOther = 0x02ff, + + // Projectors - Light source capable of producing + // realistic images from another media + CategoryProjector = 0x0300, + CategoryProjectorFixed = 0x0301, + CategoryProjectorMovingYoke = 0x0302, + CategoryProjectorMovingMirror = 0x0303, + CategoryProjectorOther = 0x03ff, + + // Atmospheric Effect - earth/wind/fire + CategoryAtmospheric = 0x0400, + CategoryAtmosphericEffect = 0x0401, // Fogger, Hazer, Flame + CategoryAtmosphericPyro = 0x0402, + CategoryAtmosphericOther = 0x04ff, + + // Insensity Control (Specifically dimming equipment) + CategoryDimmer = 0x0500, + CategoryDimmer_AC_Incandescent = 0x0501, + CategoryDimmer_AC_Fluorescent = 0x0502, + CategoryDimmer_AC_Coldcathode = 0x0503, + CategoryDimmer_AC_Nondim = 0x0504, + CategoryDimmer_AC_Elv = 0x0505, + CategoryDimmer_AC_Other = 0x0506, + CategoryDimmer_DC_Level = 0x0507, + CategoryDimmer_DC_PWM = 0x0508, + CategoryDimmer_CS_LED = 0x0509, + CategoryDimmer_Other = 0x05ff, + + // Power control (Other than dimming equipment) + CategoryPower = 0x0600, + CategoryPowerControl = 0x0601, + CategoryPowerSource = 0x0602, + CategoryPowerOther = 0x06ff, + + // Scenic Drive - Including motorized effects + // unrelated to light source + CategoryScenic = 0x0700, + CategoryScenicDrive = 0x0701, + CategoryScenicOther = 0x07ff, + + // DMX Infrastructure, conversion and interfaces + CategoryData = 0x0800, + CategoryDataDistribution = 0x0801, + CategoryDataConversion = 0x0802, + CategoryDataOther = 0x08ff, + + // Audio visual equipment + Category_AV = 0x0900, + Category_AV_Audio = 0x0901, + Category_AV_Video = 0x0902, + Category_AV_Other = 0x09ff, + + // Parameter monitoring equipment + CategoryMonitor = 0x0a00, + CategoryMonitorACLinePower = 0x0a01, + CategoryMonitorDCPower = 0x0a02, + CategoryMonitorEnvironmental = 0x0a03, + CategoryMonitorOther = 0x0aff, + + // Controllers, backup devices + CategoryControl = 0x7000, + CategoryControlController = 0x7001, + CategoryControlBackupdevice = 0x7002, + CategoryControlOther = 0x70ff, + + // Test equipment + CategoryTest = 0x7100, + CategoryTestEquipment = 0x7101, + CategoryTestEquipmentOther = 0x71ff, + + // Miscellaneous + CategoryOther = 0x7fff, + }; + + // + // Product details not yet supported in + // this library + // + enum RdmProductDetail + { + ProductDetailNotDeclared = 0x0000, + }; + + // Only LSB + enum RdmNackReasons + { + UnknownPid = 0x00, + FormatError, + HardwareFault, + ProxyReject, + WriteProtect, + UnsupportedCmdClass, + DataOutOfRange, + BufferFull, + PacketSizeUnsupported, + SubDeviceOutOfRange, + ProxyBufferFull + }; + +}; + + +#define RDM_HDR_LEN 24 // RDM Message header length ** fixed +#define RDM_PD_MAXLEN 32 // RDM Maximum parameter data length 1 - 231 + + +union RDM_Message +{ + uint8_t d[ RDM_HDR_LEN + RDM_PD_MAXLEN ]; + struct + { + uint8_t startCode; // 0 SC_RDM + uint8_t subStartCode; // 1 SC_SUB_MESSAGE + uint8_t msgLength; // 2 Range 24 - 255 + RDM_Uid dstUid; // 3-8 Destination UID + RDM_Uid srcUid; // 9-14 Source UID (sender) + uint8_t TN; // 15 Transaction number + uint8_t portId; // 16 Port ID / Response type + uint8_t msgCount; // 17 + uint16_t subDevice; // 18,19 0=root, 0xffff=all + uint8_t CC; // 20 GET_COMMAND + uint16_t PID; // 21,22 Parameter ID + uint8_t PDL; // 23 Parameter Data length 1-231 + + uint8_t PD[RDM_PD_MAXLEN]; // Parameter Data ... variable length + }; +}; + +union RDM_Checksum +{ + uint16_t checksum; + struct + { + uint8_t csl; + uint8_t csh; + }; +}; + +struct RDM_DiscUniqueBranchPD +{ + RDM_Uid lbound; + RDM_Uid hbound; +}; + +struct RDM_DiscMuteUnMutePD +{ + uint16_t ctrlField; + +// Only for multiple ports +// RDM_Uid bindingUid; +}; + +struct RDM__DeviceInfoPD +{ + uint8_t protocolVersionMajor; + uint8_t protocolVersionMinor; + uint16_t deviceModelId; + uint16_t ProductCategory; // enum RdmProductCategory + uint8_t SoftwareVersionId[4]; + uint16_t DMX512FootPrint; + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; + uint16_t DMX512StartAddress; + uint16_t SubDeviceCount; + uint8_t SensorCount; +}; + +struct RDM_DeviceGetPersonality_PD +{ + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; +}; + +struct RDM_DeviceSetPersonality_PD +{ + uint8_t DMX512Personality; +}; + + +#endif /* RDM_DEFINES_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Uid.h b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Uid.h new file mode 100644 index 000000000..35c802c63 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Master_Manual_Break/Rdm_Uid.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Uid.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_UID_H_ +#define RDM_UID_H_ + +#include + +// +//48 bit UID Representation to identify RDM transponders +// +struct RDM_Uid { + + void Initialize ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4 ) + { + m_id[0] = ((uint8_t) (((uint16_t) (m)) >> 8)); + m_id[1] = (uint8_t)m; + m_id[2] = d1; + m_id[3] = d2; + m_id[4] = d3; + m_id[5] = d4; + } + + void copy ( const RDM_Uid &orig ) + { + for ( uint8_t i = 0; i < 6; i++ ) + m_id[i] = orig.m_id[i]; + } + + bool operator == ( const RDM_Uid & orig ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != orig.m_id[i] ) + return false; + + return true; + } + + bool operator != ( const RDM_Uid & orig ) const + { + return !(*this == orig); + } + + bool operator < ( const RDM_Uid & v ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] < v.m_id[i] ); + } + + bool operator > ( const RDM_Uid & v ) + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] > v.m_id[i] ); + } + + // + // match_mid = manufacturer id to match + // + bool isBroadcast ( uint8_t match_mid[2] ) + { + // Check for genuine broadcast on device part + for ( uint8_t i = 2; i < 6; i++ ) + if ( m_id[i] != 0xff ) + return false; + + // Broadcast or manufacturer designated broadcast + if ( (m_id[0] == 0xff && m_id[1] == 0xff) || + (m_id[0] == match_mid[0] && m_id[1] == match_mid[1]) ) + return true; + + // No broadcast + return false; + } + + uint8_t m_id[6]; //16bit manufacturer id + 32 bits device id +}; + + +#endif /* RDM_UID_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Slave/DMX_Slave.ino b/DMX_NeoPixels/examples/DMX_Slave/DMX_Slave.ino index 1e4fc2f21..655e04b72 100644 --- a/DMX_NeoPixels/examples/DMX_Slave/DMX_Slave.ino +++ b/DMX_NeoPixels/examples/DMX_Slave/DMX_Slave.ino @@ -21,7 +21,7 @@ */ -#include +#include "Conceptinetics.h" // // CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD JUMPER INSTRUCTIONS diff --git a/DMX_NeoPixels/examples/DMX_Slave/Rdm_Defines.h b/DMX_NeoPixels/examples/DMX_Slave/Rdm_Defines.h new file mode 100644 index 000000000..0accca63d --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave/Rdm_Defines.h @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Defines.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_DEFINES_H_ +#define RDM_DEFINES_H_ + +#include "Rdm_Uid.h" + +#define RDM_MAX_DEVICELABEL_LENGTH 32 + +namespace rdm +{ + enum RdmCommandClass + { + DiscoveryCommand = 0x10, + DiscoveryCommandResponse, + GetCommand = 0x20, + GetCommandResponse, + SetCommand = 0x30, + SetCommandResponse, + }; + + enum RdmResponseTypes + { + ResponseTypeAck = 0x00, + ResponseTypeAckTimer, + ResponseTypeNackReason, + ResponseTypeAckOverflow, // Additional response data available (see spec) + }; + + enum RdmParameters + { + // Category - Network Management + DiscUniqueBranch = 0x0001, // Required + DiscMute = 0x0002, // Required + DiscUnMute = 0x0003, // Required + + CommsStatus = 0x0015, // Get,Set + + // Category - Status Collection + QueuedMessage = 0x0020, // Get [enum RdmStatusTypes] + StatusMessages = 0x0030, // Get [enum RdmStatusTypes] + StatusIdDescription = 0x0031, // Get + ClearStatusId = 0x0032, // Set + SubDeviceStatusReportThreshold = 0x0033, // Get, Set [enum RdmStatusTypes] + + // Category - RDM Information + // ** Only required if supporting parameters + // beyond the minimum required set + SupportedParameters = 0x0005, // Get, **Required + ParameterDescription = 0x0051, // Get, **Required + + // Category = Product Information + DeviceInfo = 0x0060, // Get, Required + ProductDetailIdList = 0x0070, // Get + DeviceModelDescription = 0x0080, // Get + ManufacturerLabel = 0x0081, // Get + DeviceLabel = 0x0082, // Get, Set + FactoryDefaults = 0x0009, // Get, Set ** + SoftwareVersionLabel = 0x000c, // Get + + // Category - DMX512 Setup + DmxPersonality = 0x00e0, // Get, Set + DmxPersonalityDescription = 0x00e1, // Get + DmxStartAddress = 0x00f0, // Get, Set ** Required if DMX device + SlotInfo = 0x0120, // Get + SlotDescription = 0x0121, // Get + DefaultSlotValue = 0x0122, // Get + + // Category - Sensors + // Category - Dimmer Settings + // Category - Power/Lamp Settings + // Category - Display Settings + // Category - Configuration + + // Category - Control + IdentifyDevice = 0x1000, // Get, Set, Required + ResetDevice = 0x1001, // Set + PowerState = 0x1010, // Get, Set + PerformSelftest = 0x1020, // Get, Set + SelfTestDescription = 0x1021, // Get + }; + + + enum RdmStatusTypes + { + StatusNone = 0x00, + StatusGetLastMessage, + StatusAdvisory, + StatusWarning, + StatusError, + StatusAdvisoryCleared = 0x12, + StatusWarningCleared, + StatusErrorCleared, + }; + + enum RdmProductCategory + { + CategoryNotDeclared = 0x0000, + + // Fixtures - intended as source for illumination + CategoryFixture = 0x0100, + CategoryFixtureFixed = 0x0101, + CategoryFixtureMovingYoke = 0x0102, + CategoryFixtureMovingMirror = 0x0103, + CategoryFixtureOther = 0x01ff, + + // Fixture Accessories - add-ons to fixtures or projectors + CategoryFixtureAccessory = 0x0200, + CategoryFixtureAccessoryColor = 0x0201, + CategoryFixtureAccessoryYoke = 0x0202, + CategoryFixtureAccessoryMirror = 0x0203, + CategoryFixtureAccessoryEffect = 0x0204, + CategoryFixtureAccessoryBeam = 0x0205, + CategoryFixtureAccessoryOther = 0x02ff, + + // Projectors - Light source capable of producing + // realistic images from another media + CategoryProjector = 0x0300, + CategoryProjectorFixed = 0x0301, + CategoryProjectorMovingYoke = 0x0302, + CategoryProjectorMovingMirror = 0x0303, + CategoryProjectorOther = 0x03ff, + + // Atmospheric Effect - earth/wind/fire + CategoryAtmospheric = 0x0400, + CategoryAtmosphericEffect = 0x0401, // Fogger, Hazer, Flame + CategoryAtmosphericPyro = 0x0402, + CategoryAtmosphericOther = 0x04ff, + + // Insensity Control (Specifically dimming equipment) + CategoryDimmer = 0x0500, + CategoryDimmer_AC_Incandescent = 0x0501, + CategoryDimmer_AC_Fluorescent = 0x0502, + CategoryDimmer_AC_Coldcathode = 0x0503, + CategoryDimmer_AC_Nondim = 0x0504, + CategoryDimmer_AC_Elv = 0x0505, + CategoryDimmer_AC_Other = 0x0506, + CategoryDimmer_DC_Level = 0x0507, + CategoryDimmer_DC_PWM = 0x0508, + CategoryDimmer_CS_LED = 0x0509, + CategoryDimmer_Other = 0x05ff, + + // Power control (Other than dimming equipment) + CategoryPower = 0x0600, + CategoryPowerControl = 0x0601, + CategoryPowerSource = 0x0602, + CategoryPowerOther = 0x06ff, + + // Scenic Drive - Including motorized effects + // unrelated to light source + CategoryScenic = 0x0700, + CategoryScenicDrive = 0x0701, + CategoryScenicOther = 0x07ff, + + // DMX Infrastructure, conversion and interfaces + CategoryData = 0x0800, + CategoryDataDistribution = 0x0801, + CategoryDataConversion = 0x0802, + CategoryDataOther = 0x08ff, + + // Audio visual equipment + Category_AV = 0x0900, + Category_AV_Audio = 0x0901, + Category_AV_Video = 0x0902, + Category_AV_Other = 0x09ff, + + // Parameter monitoring equipment + CategoryMonitor = 0x0a00, + CategoryMonitorACLinePower = 0x0a01, + CategoryMonitorDCPower = 0x0a02, + CategoryMonitorEnvironmental = 0x0a03, + CategoryMonitorOther = 0x0aff, + + // Controllers, backup devices + CategoryControl = 0x7000, + CategoryControlController = 0x7001, + CategoryControlBackupdevice = 0x7002, + CategoryControlOther = 0x70ff, + + // Test equipment + CategoryTest = 0x7100, + CategoryTestEquipment = 0x7101, + CategoryTestEquipmentOther = 0x71ff, + + // Miscellaneous + CategoryOther = 0x7fff, + }; + + // + // Product details not yet supported in + // this library + // + enum RdmProductDetail + { + ProductDetailNotDeclared = 0x0000, + }; + + // Only LSB + enum RdmNackReasons + { + UnknownPid = 0x00, + FormatError, + HardwareFault, + ProxyReject, + WriteProtect, + UnsupportedCmdClass, + DataOutOfRange, + BufferFull, + PacketSizeUnsupported, + SubDeviceOutOfRange, + ProxyBufferFull + }; + +}; + + +#define RDM_HDR_LEN 24 // RDM Message header length ** fixed +#define RDM_PD_MAXLEN 32 // RDM Maximum parameter data length 1 - 231 + + +union RDM_Message +{ + uint8_t d[ RDM_HDR_LEN + RDM_PD_MAXLEN ]; + struct + { + uint8_t startCode; // 0 SC_RDM + uint8_t subStartCode; // 1 SC_SUB_MESSAGE + uint8_t msgLength; // 2 Range 24 - 255 + RDM_Uid dstUid; // 3-8 Destination UID + RDM_Uid srcUid; // 9-14 Source UID (sender) + uint8_t TN; // 15 Transaction number + uint8_t portId; // 16 Port ID / Response type + uint8_t msgCount; // 17 + uint16_t subDevice; // 18,19 0=root, 0xffff=all + uint8_t CC; // 20 GET_COMMAND + uint16_t PID; // 21,22 Parameter ID + uint8_t PDL; // 23 Parameter Data length 1-231 + + uint8_t PD[RDM_PD_MAXLEN]; // Parameter Data ... variable length + }; +}; + +union RDM_Checksum +{ + uint16_t checksum; + struct + { + uint8_t csl; + uint8_t csh; + }; +}; + +struct RDM_DiscUniqueBranchPD +{ + RDM_Uid lbound; + RDM_Uid hbound; +}; + +struct RDM_DiscMuteUnMutePD +{ + uint16_t ctrlField; + +// Only for multiple ports +// RDM_Uid bindingUid; +}; + +struct RDM__DeviceInfoPD +{ + uint8_t protocolVersionMajor; + uint8_t protocolVersionMinor; + uint16_t deviceModelId; + uint16_t ProductCategory; // enum RdmProductCategory + uint8_t SoftwareVersionId[4]; + uint16_t DMX512FootPrint; + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; + uint16_t DMX512StartAddress; + uint16_t SubDeviceCount; + uint8_t SensorCount; +}; + +struct RDM_DeviceGetPersonality_PD +{ + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; +}; + +struct RDM_DeviceSetPersonality_PD +{ + uint8_t DMX512Personality; +}; + + +#endif /* RDM_DEFINES_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Slave/Rdm_Uid.h b/DMX_NeoPixels/examples/DMX_Slave/Rdm_Uid.h new file mode 100644 index 000000000..35c802c63 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave/Rdm_Uid.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Uid.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_UID_H_ +#define RDM_UID_H_ + +#include + +// +//48 bit UID Representation to identify RDM transponders +// +struct RDM_Uid { + + void Initialize ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4 ) + { + m_id[0] = ((uint8_t) (((uint16_t) (m)) >> 8)); + m_id[1] = (uint8_t)m; + m_id[2] = d1; + m_id[3] = d2; + m_id[4] = d3; + m_id[5] = d4; + } + + void copy ( const RDM_Uid &orig ) + { + for ( uint8_t i = 0; i < 6; i++ ) + m_id[i] = orig.m_id[i]; + } + + bool operator == ( const RDM_Uid & orig ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != orig.m_id[i] ) + return false; + + return true; + } + + bool operator != ( const RDM_Uid & orig ) const + { + return !(*this == orig); + } + + bool operator < ( const RDM_Uid & v ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] < v.m_id[i] ); + } + + bool operator > ( const RDM_Uid & v ) + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] > v.m_id[i] ); + } + + // + // match_mid = manufacturer id to match + // + bool isBroadcast ( uint8_t match_mid[2] ) + { + // Check for genuine broadcast on device part + for ( uint8_t i = 2; i < 6; i++ ) + if ( m_id[i] != 0xff ) + return false; + + // Broadcast or manufacturer designated broadcast + if ( (m_id[0] == 0xff && m_id[1] == 0xff) || + (m_id[0] == match_mid[0] && m_id[1] == match_mid[1]) ) + return true; + + // No broadcast + return false; + } + + uint8_t m_id[6]; //16bit manufacturer id + 32 bits device id +}; + + +#endif /* RDM_UID_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/DMX_Slave_Signal_Timeout.ino b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/DMX_Slave_Signal_Timeout.ino index d36848135..617c323fa 100644 --- a/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/DMX_Slave_Signal_Timeout.ino +++ b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/DMX_Slave_Signal_Timeout.ino @@ -21,7 +21,7 @@ */ -#include +#include "Conceptinetics.h" // // CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD JUMPER INSTRUCTIONS diff --git a/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Defines.h b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Defines.h new file mode 100644 index 000000000..0accca63d --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Defines.h @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Defines.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_DEFINES_H_ +#define RDM_DEFINES_H_ + +#include "Rdm_Uid.h" + +#define RDM_MAX_DEVICELABEL_LENGTH 32 + +namespace rdm +{ + enum RdmCommandClass + { + DiscoveryCommand = 0x10, + DiscoveryCommandResponse, + GetCommand = 0x20, + GetCommandResponse, + SetCommand = 0x30, + SetCommandResponse, + }; + + enum RdmResponseTypes + { + ResponseTypeAck = 0x00, + ResponseTypeAckTimer, + ResponseTypeNackReason, + ResponseTypeAckOverflow, // Additional response data available (see spec) + }; + + enum RdmParameters + { + // Category - Network Management + DiscUniqueBranch = 0x0001, // Required + DiscMute = 0x0002, // Required + DiscUnMute = 0x0003, // Required + + CommsStatus = 0x0015, // Get,Set + + // Category - Status Collection + QueuedMessage = 0x0020, // Get [enum RdmStatusTypes] + StatusMessages = 0x0030, // Get [enum RdmStatusTypes] + StatusIdDescription = 0x0031, // Get + ClearStatusId = 0x0032, // Set + SubDeviceStatusReportThreshold = 0x0033, // Get, Set [enum RdmStatusTypes] + + // Category - RDM Information + // ** Only required if supporting parameters + // beyond the minimum required set + SupportedParameters = 0x0005, // Get, **Required + ParameterDescription = 0x0051, // Get, **Required + + // Category = Product Information + DeviceInfo = 0x0060, // Get, Required + ProductDetailIdList = 0x0070, // Get + DeviceModelDescription = 0x0080, // Get + ManufacturerLabel = 0x0081, // Get + DeviceLabel = 0x0082, // Get, Set + FactoryDefaults = 0x0009, // Get, Set ** + SoftwareVersionLabel = 0x000c, // Get + + // Category - DMX512 Setup + DmxPersonality = 0x00e0, // Get, Set + DmxPersonalityDescription = 0x00e1, // Get + DmxStartAddress = 0x00f0, // Get, Set ** Required if DMX device + SlotInfo = 0x0120, // Get + SlotDescription = 0x0121, // Get + DefaultSlotValue = 0x0122, // Get + + // Category - Sensors + // Category - Dimmer Settings + // Category - Power/Lamp Settings + // Category - Display Settings + // Category - Configuration + + // Category - Control + IdentifyDevice = 0x1000, // Get, Set, Required + ResetDevice = 0x1001, // Set + PowerState = 0x1010, // Get, Set + PerformSelftest = 0x1020, // Get, Set + SelfTestDescription = 0x1021, // Get + }; + + + enum RdmStatusTypes + { + StatusNone = 0x00, + StatusGetLastMessage, + StatusAdvisory, + StatusWarning, + StatusError, + StatusAdvisoryCleared = 0x12, + StatusWarningCleared, + StatusErrorCleared, + }; + + enum RdmProductCategory + { + CategoryNotDeclared = 0x0000, + + // Fixtures - intended as source for illumination + CategoryFixture = 0x0100, + CategoryFixtureFixed = 0x0101, + CategoryFixtureMovingYoke = 0x0102, + CategoryFixtureMovingMirror = 0x0103, + CategoryFixtureOther = 0x01ff, + + // Fixture Accessories - add-ons to fixtures or projectors + CategoryFixtureAccessory = 0x0200, + CategoryFixtureAccessoryColor = 0x0201, + CategoryFixtureAccessoryYoke = 0x0202, + CategoryFixtureAccessoryMirror = 0x0203, + CategoryFixtureAccessoryEffect = 0x0204, + CategoryFixtureAccessoryBeam = 0x0205, + CategoryFixtureAccessoryOther = 0x02ff, + + // Projectors - Light source capable of producing + // realistic images from another media + CategoryProjector = 0x0300, + CategoryProjectorFixed = 0x0301, + CategoryProjectorMovingYoke = 0x0302, + CategoryProjectorMovingMirror = 0x0303, + CategoryProjectorOther = 0x03ff, + + // Atmospheric Effect - earth/wind/fire + CategoryAtmospheric = 0x0400, + CategoryAtmosphericEffect = 0x0401, // Fogger, Hazer, Flame + CategoryAtmosphericPyro = 0x0402, + CategoryAtmosphericOther = 0x04ff, + + // Insensity Control (Specifically dimming equipment) + CategoryDimmer = 0x0500, + CategoryDimmer_AC_Incandescent = 0x0501, + CategoryDimmer_AC_Fluorescent = 0x0502, + CategoryDimmer_AC_Coldcathode = 0x0503, + CategoryDimmer_AC_Nondim = 0x0504, + CategoryDimmer_AC_Elv = 0x0505, + CategoryDimmer_AC_Other = 0x0506, + CategoryDimmer_DC_Level = 0x0507, + CategoryDimmer_DC_PWM = 0x0508, + CategoryDimmer_CS_LED = 0x0509, + CategoryDimmer_Other = 0x05ff, + + // Power control (Other than dimming equipment) + CategoryPower = 0x0600, + CategoryPowerControl = 0x0601, + CategoryPowerSource = 0x0602, + CategoryPowerOther = 0x06ff, + + // Scenic Drive - Including motorized effects + // unrelated to light source + CategoryScenic = 0x0700, + CategoryScenicDrive = 0x0701, + CategoryScenicOther = 0x07ff, + + // DMX Infrastructure, conversion and interfaces + CategoryData = 0x0800, + CategoryDataDistribution = 0x0801, + CategoryDataConversion = 0x0802, + CategoryDataOther = 0x08ff, + + // Audio visual equipment + Category_AV = 0x0900, + Category_AV_Audio = 0x0901, + Category_AV_Video = 0x0902, + Category_AV_Other = 0x09ff, + + // Parameter monitoring equipment + CategoryMonitor = 0x0a00, + CategoryMonitorACLinePower = 0x0a01, + CategoryMonitorDCPower = 0x0a02, + CategoryMonitorEnvironmental = 0x0a03, + CategoryMonitorOther = 0x0aff, + + // Controllers, backup devices + CategoryControl = 0x7000, + CategoryControlController = 0x7001, + CategoryControlBackupdevice = 0x7002, + CategoryControlOther = 0x70ff, + + // Test equipment + CategoryTest = 0x7100, + CategoryTestEquipment = 0x7101, + CategoryTestEquipmentOther = 0x71ff, + + // Miscellaneous + CategoryOther = 0x7fff, + }; + + // + // Product details not yet supported in + // this library + // + enum RdmProductDetail + { + ProductDetailNotDeclared = 0x0000, + }; + + // Only LSB + enum RdmNackReasons + { + UnknownPid = 0x00, + FormatError, + HardwareFault, + ProxyReject, + WriteProtect, + UnsupportedCmdClass, + DataOutOfRange, + BufferFull, + PacketSizeUnsupported, + SubDeviceOutOfRange, + ProxyBufferFull + }; + +}; + + +#define RDM_HDR_LEN 24 // RDM Message header length ** fixed +#define RDM_PD_MAXLEN 32 // RDM Maximum parameter data length 1 - 231 + + +union RDM_Message +{ + uint8_t d[ RDM_HDR_LEN + RDM_PD_MAXLEN ]; + struct + { + uint8_t startCode; // 0 SC_RDM + uint8_t subStartCode; // 1 SC_SUB_MESSAGE + uint8_t msgLength; // 2 Range 24 - 255 + RDM_Uid dstUid; // 3-8 Destination UID + RDM_Uid srcUid; // 9-14 Source UID (sender) + uint8_t TN; // 15 Transaction number + uint8_t portId; // 16 Port ID / Response type + uint8_t msgCount; // 17 + uint16_t subDevice; // 18,19 0=root, 0xffff=all + uint8_t CC; // 20 GET_COMMAND + uint16_t PID; // 21,22 Parameter ID + uint8_t PDL; // 23 Parameter Data length 1-231 + + uint8_t PD[RDM_PD_MAXLEN]; // Parameter Data ... variable length + }; +}; + +union RDM_Checksum +{ + uint16_t checksum; + struct + { + uint8_t csl; + uint8_t csh; + }; +}; + +struct RDM_DiscUniqueBranchPD +{ + RDM_Uid lbound; + RDM_Uid hbound; +}; + +struct RDM_DiscMuteUnMutePD +{ + uint16_t ctrlField; + +// Only for multiple ports +// RDM_Uid bindingUid; +}; + +struct RDM__DeviceInfoPD +{ + uint8_t protocolVersionMajor; + uint8_t protocolVersionMinor; + uint16_t deviceModelId; + uint16_t ProductCategory; // enum RdmProductCategory + uint8_t SoftwareVersionId[4]; + uint16_t DMX512FootPrint; + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; + uint16_t DMX512StartAddress; + uint16_t SubDeviceCount; + uint8_t SensorCount; +}; + +struct RDM_DeviceGetPersonality_PD +{ + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; +}; + +struct RDM_DeviceSetPersonality_PD +{ + uint8_t DMX512Personality; +}; + + +#endif /* RDM_DEFINES_H_ */ diff --git a/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Uid.h b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Uid.h new file mode 100644 index 000000000..35c802c63 --- /dev/null +++ b/DMX_NeoPixels/examples/DMX_Slave_Signal_Timeout/Rdm_Uid.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Uid.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_UID_H_ +#define RDM_UID_H_ + +#include + +// +//48 bit UID Representation to identify RDM transponders +// +struct RDM_Uid { + + void Initialize ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4 ) + { + m_id[0] = ((uint8_t) (((uint16_t) (m)) >> 8)); + m_id[1] = (uint8_t)m; + m_id[2] = d1; + m_id[3] = d2; + m_id[4] = d3; + m_id[5] = d4; + } + + void copy ( const RDM_Uid &orig ) + { + for ( uint8_t i = 0; i < 6; i++ ) + m_id[i] = orig.m_id[i]; + } + + bool operator == ( const RDM_Uid & orig ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != orig.m_id[i] ) + return false; + + return true; + } + + bool operator != ( const RDM_Uid & orig ) const + { + return !(*this == orig); + } + + bool operator < ( const RDM_Uid & v ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] < v.m_id[i] ); + } + + bool operator > ( const RDM_Uid & v ) + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] > v.m_id[i] ); + } + + // + // match_mid = manufacturer id to match + // + bool isBroadcast ( uint8_t match_mid[2] ) + { + // Check for genuine broadcast on device part + for ( uint8_t i = 2; i < 6; i++ ) + if ( m_id[i] != 0xff ) + return false; + + // Broadcast or manufacturer designated broadcast + if ( (m_id[0] == 0xff && m_id[1] == 0xff) || + (m_id[0] == match_mid[0] && m_id[1] == match_mid[1]) ) + return true; + + // No broadcast + return false; + } + + uint8_t m_id[6]; //16bit manufacturer id + 32 bits device id +}; + + +#endif /* RDM_UID_H_ */ diff --git a/DMX_NeoPixels/examples/RDM_Slave/RDM_Slave.ino b/DMX_NeoPixels/examples/RDM_Slave/RDM_Slave.ino index a793d2f32..23f77aad7 100644 --- a/DMX_NeoPixels/examples/RDM_Slave/RDM_Slave.ino +++ b/DMX_NeoPixels/examples/RDM_Slave/RDM_Slave.ino @@ -21,7 +21,7 @@ */ -#include +#include "Conceptinetics.h" // // CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD JUMPER INSTRUCTIONS diff --git a/DMX_NeoPixels/examples/RDM_Slave/Rdm_Defines.h b/DMX_NeoPixels/examples/RDM_Slave/Rdm_Defines.h new file mode 100644 index 000000000..0accca63d --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave/Rdm_Defines.h @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Defines.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_DEFINES_H_ +#define RDM_DEFINES_H_ + +#include "Rdm_Uid.h" + +#define RDM_MAX_DEVICELABEL_LENGTH 32 + +namespace rdm +{ + enum RdmCommandClass + { + DiscoveryCommand = 0x10, + DiscoveryCommandResponse, + GetCommand = 0x20, + GetCommandResponse, + SetCommand = 0x30, + SetCommandResponse, + }; + + enum RdmResponseTypes + { + ResponseTypeAck = 0x00, + ResponseTypeAckTimer, + ResponseTypeNackReason, + ResponseTypeAckOverflow, // Additional response data available (see spec) + }; + + enum RdmParameters + { + // Category - Network Management + DiscUniqueBranch = 0x0001, // Required + DiscMute = 0x0002, // Required + DiscUnMute = 0x0003, // Required + + CommsStatus = 0x0015, // Get,Set + + // Category - Status Collection + QueuedMessage = 0x0020, // Get [enum RdmStatusTypes] + StatusMessages = 0x0030, // Get [enum RdmStatusTypes] + StatusIdDescription = 0x0031, // Get + ClearStatusId = 0x0032, // Set + SubDeviceStatusReportThreshold = 0x0033, // Get, Set [enum RdmStatusTypes] + + // Category - RDM Information + // ** Only required if supporting parameters + // beyond the minimum required set + SupportedParameters = 0x0005, // Get, **Required + ParameterDescription = 0x0051, // Get, **Required + + // Category = Product Information + DeviceInfo = 0x0060, // Get, Required + ProductDetailIdList = 0x0070, // Get + DeviceModelDescription = 0x0080, // Get + ManufacturerLabel = 0x0081, // Get + DeviceLabel = 0x0082, // Get, Set + FactoryDefaults = 0x0009, // Get, Set ** + SoftwareVersionLabel = 0x000c, // Get + + // Category - DMX512 Setup + DmxPersonality = 0x00e0, // Get, Set + DmxPersonalityDescription = 0x00e1, // Get + DmxStartAddress = 0x00f0, // Get, Set ** Required if DMX device + SlotInfo = 0x0120, // Get + SlotDescription = 0x0121, // Get + DefaultSlotValue = 0x0122, // Get + + // Category - Sensors + // Category - Dimmer Settings + // Category - Power/Lamp Settings + // Category - Display Settings + // Category - Configuration + + // Category - Control + IdentifyDevice = 0x1000, // Get, Set, Required + ResetDevice = 0x1001, // Set + PowerState = 0x1010, // Get, Set + PerformSelftest = 0x1020, // Get, Set + SelfTestDescription = 0x1021, // Get + }; + + + enum RdmStatusTypes + { + StatusNone = 0x00, + StatusGetLastMessage, + StatusAdvisory, + StatusWarning, + StatusError, + StatusAdvisoryCleared = 0x12, + StatusWarningCleared, + StatusErrorCleared, + }; + + enum RdmProductCategory + { + CategoryNotDeclared = 0x0000, + + // Fixtures - intended as source for illumination + CategoryFixture = 0x0100, + CategoryFixtureFixed = 0x0101, + CategoryFixtureMovingYoke = 0x0102, + CategoryFixtureMovingMirror = 0x0103, + CategoryFixtureOther = 0x01ff, + + // Fixture Accessories - add-ons to fixtures or projectors + CategoryFixtureAccessory = 0x0200, + CategoryFixtureAccessoryColor = 0x0201, + CategoryFixtureAccessoryYoke = 0x0202, + CategoryFixtureAccessoryMirror = 0x0203, + CategoryFixtureAccessoryEffect = 0x0204, + CategoryFixtureAccessoryBeam = 0x0205, + CategoryFixtureAccessoryOther = 0x02ff, + + // Projectors - Light source capable of producing + // realistic images from another media + CategoryProjector = 0x0300, + CategoryProjectorFixed = 0x0301, + CategoryProjectorMovingYoke = 0x0302, + CategoryProjectorMovingMirror = 0x0303, + CategoryProjectorOther = 0x03ff, + + // Atmospheric Effect - earth/wind/fire + CategoryAtmospheric = 0x0400, + CategoryAtmosphericEffect = 0x0401, // Fogger, Hazer, Flame + CategoryAtmosphericPyro = 0x0402, + CategoryAtmosphericOther = 0x04ff, + + // Insensity Control (Specifically dimming equipment) + CategoryDimmer = 0x0500, + CategoryDimmer_AC_Incandescent = 0x0501, + CategoryDimmer_AC_Fluorescent = 0x0502, + CategoryDimmer_AC_Coldcathode = 0x0503, + CategoryDimmer_AC_Nondim = 0x0504, + CategoryDimmer_AC_Elv = 0x0505, + CategoryDimmer_AC_Other = 0x0506, + CategoryDimmer_DC_Level = 0x0507, + CategoryDimmer_DC_PWM = 0x0508, + CategoryDimmer_CS_LED = 0x0509, + CategoryDimmer_Other = 0x05ff, + + // Power control (Other than dimming equipment) + CategoryPower = 0x0600, + CategoryPowerControl = 0x0601, + CategoryPowerSource = 0x0602, + CategoryPowerOther = 0x06ff, + + // Scenic Drive - Including motorized effects + // unrelated to light source + CategoryScenic = 0x0700, + CategoryScenicDrive = 0x0701, + CategoryScenicOther = 0x07ff, + + // DMX Infrastructure, conversion and interfaces + CategoryData = 0x0800, + CategoryDataDistribution = 0x0801, + CategoryDataConversion = 0x0802, + CategoryDataOther = 0x08ff, + + // Audio visual equipment + Category_AV = 0x0900, + Category_AV_Audio = 0x0901, + Category_AV_Video = 0x0902, + Category_AV_Other = 0x09ff, + + // Parameter monitoring equipment + CategoryMonitor = 0x0a00, + CategoryMonitorACLinePower = 0x0a01, + CategoryMonitorDCPower = 0x0a02, + CategoryMonitorEnvironmental = 0x0a03, + CategoryMonitorOther = 0x0aff, + + // Controllers, backup devices + CategoryControl = 0x7000, + CategoryControlController = 0x7001, + CategoryControlBackupdevice = 0x7002, + CategoryControlOther = 0x70ff, + + // Test equipment + CategoryTest = 0x7100, + CategoryTestEquipment = 0x7101, + CategoryTestEquipmentOther = 0x71ff, + + // Miscellaneous + CategoryOther = 0x7fff, + }; + + // + // Product details not yet supported in + // this library + // + enum RdmProductDetail + { + ProductDetailNotDeclared = 0x0000, + }; + + // Only LSB + enum RdmNackReasons + { + UnknownPid = 0x00, + FormatError, + HardwareFault, + ProxyReject, + WriteProtect, + UnsupportedCmdClass, + DataOutOfRange, + BufferFull, + PacketSizeUnsupported, + SubDeviceOutOfRange, + ProxyBufferFull + }; + +}; + + +#define RDM_HDR_LEN 24 // RDM Message header length ** fixed +#define RDM_PD_MAXLEN 32 // RDM Maximum parameter data length 1 - 231 + + +union RDM_Message +{ + uint8_t d[ RDM_HDR_LEN + RDM_PD_MAXLEN ]; + struct + { + uint8_t startCode; // 0 SC_RDM + uint8_t subStartCode; // 1 SC_SUB_MESSAGE + uint8_t msgLength; // 2 Range 24 - 255 + RDM_Uid dstUid; // 3-8 Destination UID + RDM_Uid srcUid; // 9-14 Source UID (sender) + uint8_t TN; // 15 Transaction number + uint8_t portId; // 16 Port ID / Response type + uint8_t msgCount; // 17 + uint16_t subDevice; // 18,19 0=root, 0xffff=all + uint8_t CC; // 20 GET_COMMAND + uint16_t PID; // 21,22 Parameter ID + uint8_t PDL; // 23 Parameter Data length 1-231 + + uint8_t PD[RDM_PD_MAXLEN]; // Parameter Data ... variable length + }; +}; + +union RDM_Checksum +{ + uint16_t checksum; + struct + { + uint8_t csl; + uint8_t csh; + }; +}; + +struct RDM_DiscUniqueBranchPD +{ + RDM_Uid lbound; + RDM_Uid hbound; +}; + +struct RDM_DiscMuteUnMutePD +{ + uint16_t ctrlField; + +// Only for multiple ports +// RDM_Uid bindingUid; +}; + +struct RDM__DeviceInfoPD +{ + uint8_t protocolVersionMajor; + uint8_t protocolVersionMinor; + uint16_t deviceModelId; + uint16_t ProductCategory; // enum RdmProductCategory + uint8_t SoftwareVersionId[4]; + uint16_t DMX512FootPrint; + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; + uint16_t DMX512StartAddress; + uint16_t SubDeviceCount; + uint8_t SensorCount; +}; + +struct RDM_DeviceGetPersonality_PD +{ + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; +}; + +struct RDM_DeviceSetPersonality_PD +{ + uint8_t DMX512Personality; +}; + + +#endif /* RDM_DEFINES_H_ */ diff --git a/DMX_NeoPixels/examples/RDM_Slave/Rdm_Uid.h b/DMX_NeoPixels/examples/RDM_Slave/Rdm_Uid.h new file mode 100644 index 000000000..35c802c63 --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave/Rdm_Uid.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Uid.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_UID_H_ +#define RDM_UID_H_ + +#include + +// +//48 bit UID Representation to identify RDM transponders +// +struct RDM_Uid { + + void Initialize ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4 ) + { + m_id[0] = ((uint8_t) (((uint16_t) (m)) >> 8)); + m_id[1] = (uint8_t)m; + m_id[2] = d1; + m_id[3] = d2; + m_id[4] = d3; + m_id[5] = d4; + } + + void copy ( const RDM_Uid &orig ) + { + for ( uint8_t i = 0; i < 6; i++ ) + m_id[i] = orig.m_id[i]; + } + + bool operator == ( const RDM_Uid & orig ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != orig.m_id[i] ) + return false; + + return true; + } + + bool operator != ( const RDM_Uid & orig ) const + { + return !(*this == orig); + } + + bool operator < ( const RDM_Uid & v ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] < v.m_id[i] ); + } + + bool operator > ( const RDM_Uid & v ) + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] > v.m_id[i] ); + } + + // + // match_mid = manufacturer id to match + // + bool isBroadcast ( uint8_t match_mid[2] ) + { + // Check for genuine broadcast on device part + for ( uint8_t i = 2; i < 6; i++ ) + if ( m_id[i] != 0xff ) + return false; + + // Broadcast or manufacturer designated broadcast + if ( (m_id[0] == 0xff && m_id[1] == 0xff) || + (m_id[0] == match_mid[0] && m_id[1] == match_mid[1]) ) + return true; + + // No broadcast + return false; + } + + uint8_t m_id[6]; //16bit manufacturer id + 32 bits device id +}; + + +#endif /* RDM_UID_H_ */ diff --git a/DMX_NeoPixels/examples/RDM_Slave_Eeprom/RDM_Slave_Eeprom.ino b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/RDM_Slave_Eeprom.ino index f6aafbf7c..63e3e14b1 100644 --- a/DMX_NeoPixels/examples/RDM_Slave_Eeprom/RDM_Slave_Eeprom.ino +++ b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/RDM_Slave_Eeprom.ino @@ -23,7 +23,7 @@ #include -#include +#include "Conceptinetics.h" // // CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD JUMPER INSTRUCTIONS diff --git a/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Defines.h b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Defines.h new file mode 100644 index 000000000..0accca63d --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Defines.h @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Defines.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_DEFINES_H_ +#define RDM_DEFINES_H_ + +#include "Rdm_Uid.h" + +#define RDM_MAX_DEVICELABEL_LENGTH 32 + +namespace rdm +{ + enum RdmCommandClass + { + DiscoveryCommand = 0x10, + DiscoveryCommandResponse, + GetCommand = 0x20, + GetCommandResponse, + SetCommand = 0x30, + SetCommandResponse, + }; + + enum RdmResponseTypes + { + ResponseTypeAck = 0x00, + ResponseTypeAckTimer, + ResponseTypeNackReason, + ResponseTypeAckOverflow, // Additional response data available (see spec) + }; + + enum RdmParameters + { + // Category - Network Management + DiscUniqueBranch = 0x0001, // Required + DiscMute = 0x0002, // Required + DiscUnMute = 0x0003, // Required + + CommsStatus = 0x0015, // Get,Set + + // Category - Status Collection + QueuedMessage = 0x0020, // Get [enum RdmStatusTypes] + StatusMessages = 0x0030, // Get [enum RdmStatusTypes] + StatusIdDescription = 0x0031, // Get + ClearStatusId = 0x0032, // Set + SubDeviceStatusReportThreshold = 0x0033, // Get, Set [enum RdmStatusTypes] + + // Category - RDM Information + // ** Only required if supporting parameters + // beyond the minimum required set + SupportedParameters = 0x0005, // Get, **Required + ParameterDescription = 0x0051, // Get, **Required + + // Category = Product Information + DeviceInfo = 0x0060, // Get, Required + ProductDetailIdList = 0x0070, // Get + DeviceModelDescription = 0x0080, // Get + ManufacturerLabel = 0x0081, // Get + DeviceLabel = 0x0082, // Get, Set + FactoryDefaults = 0x0009, // Get, Set ** + SoftwareVersionLabel = 0x000c, // Get + + // Category - DMX512 Setup + DmxPersonality = 0x00e0, // Get, Set + DmxPersonalityDescription = 0x00e1, // Get + DmxStartAddress = 0x00f0, // Get, Set ** Required if DMX device + SlotInfo = 0x0120, // Get + SlotDescription = 0x0121, // Get + DefaultSlotValue = 0x0122, // Get + + // Category - Sensors + // Category - Dimmer Settings + // Category - Power/Lamp Settings + // Category - Display Settings + // Category - Configuration + + // Category - Control + IdentifyDevice = 0x1000, // Get, Set, Required + ResetDevice = 0x1001, // Set + PowerState = 0x1010, // Get, Set + PerformSelftest = 0x1020, // Get, Set + SelfTestDescription = 0x1021, // Get + }; + + + enum RdmStatusTypes + { + StatusNone = 0x00, + StatusGetLastMessage, + StatusAdvisory, + StatusWarning, + StatusError, + StatusAdvisoryCleared = 0x12, + StatusWarningCleared, + StatusErrorCleared, + }; + + enum RdmProductCategory + { + CategoryNotDeclared = 0x0000, + + // Fixtures - intended as source for illumination + CategoryFixture = 0x0100, + CategoryFixtureFixed = 0x0101, + CategoryFixtureMovingYoke = 0x0102, + CategoryFixtureMovingMirror = 0x0103, + CategoryFixtureOther = 0x01ff, + + // Fixture Accessories - add-ons to fixtures or projectors + CategoryFixtureAccessory = 0x0200, + CategoryFixtureAccessoryColor = 0x0201, + CategoryFixtureAccessoryYoke = 0x0202, + CategoryFixtureAccessoryMirror = 0x0203, + CategoryFixtureAccessoryEffect = 0x0204, + CategoryFixtureAccessoryBeam = 0x0205, + CategoryFixtureAccessoryOther = 0x02ff, + + // Projectors - Light source capable of producing + // realistic images from another media + CategoryProjector = 0x0300, + CategoryProjectorFixed = 0x0301, + CategoryProjectorMovingYoke = 0x0302, + CategoryProjectorMovingMirror = 0x0303, + CategoryProjectorOther = 0x03ff, + + // Atmospheric Effect - earth/wind/fire + CategoryAtmospheric = 0x0400, + CategoryAtmosphericEffect = 0x0401, // Fogger, Hazer, Flame + CategoryAtmosphericPyro = 0x0402, + CategoryAtmosphericOther = 0x04ff, + + // Insensity Control (Specifically dimming equipment) + CategoryDimmer = 0x0500, + CategoryDimmer_AC_Incandescent = 0x0501, + CategoryDimmer_AC_Fluorescent = 0x0502, + CategoryDimmer_AC_Coldcathode = 0x0503, + CategoryDimmer_AC_Nondim = 0x0504, + CategoryDimmer_AC_Elv = 0x0505, + CategoryDimmer_AC_Other = 0x0506, + CategoryDimmer_DC_Level = 0x0507, + CategoryDimmer_DC_PWM = 0x0508, + CategoryDimmer_CS_LED = 0x0509, + CategoryDimmer_Other = 0x05ff, + + // Power control (Other than dimming equipment) + CategoryPower = 0x0600, + CategoryPowerControl = 0x0601, + CategoryPowerSource = 0x0602, + CategoryPowerOther = 0x06ff, + + // Scenic Drive - Including motorized effects + // unrelated to light source + CategoryScenic = 0x0700, + CategoryScenicDrive = 0x0701, + CategoryScenicOther = 0x07ff, + + // DMX Infrastructure, conversion and interfaces + CategoryData = 0x0800, + CategoryDataDistribution = 0x0801, + CategoryDataConversion = 0x0802, + CategoryDataOther = 0x08ff, + + // Audio visual equipment + Category_AV = 0x0900, + Category_AV_Audio = 0x0901, + Category_AV_Video = 0x0902, + Category_AV_Other = 0x09ff, + + // Parameter monitoring equipment + CategoryMonitor = 0x0a00, + CategoryMonitorACLinePower = 0x0a01, + CategoryMonitorDCPower = 0x0a02, + CategoryMonitorEnvironmental = 0x0a03, + CategoryMonitorOther = 0x0aff, + + // Controllers, backup devices + CategoryControl = 0x7000, + CategoryControlController = 0x7001, + CategoryControlBackupdevice = 0x7002, + CategoryControlOther = 0x70ff, + + // Test equipment + CategoryTest = 0x7100, + CategoryTestEquipment = 0x7101, + CategoryTestEquipmentOther = 0x71ff, + + // Miscellaneous + CategoryOther = 0x7fff, + }; + + // + // Product details not yet supported in + // this library + // + enum RdmProductDetail + { + ProductDetailNotDeclared = 0x0000, + }; + + // Only LSB + enum RdmNackReasons + { + UnknownPid = 0x00, + FormatError, + HardwareFault, + ProxyReject, + WriteProtect, + UnsupportedCmdClass, + DataOutOfRange, + BufferFull, + PacketSizeUnsupported, + SubDeviceOutOfRange, + ProxyBufferFull + }; + +}; + + +#define RDM_HDR_LEN 24 // RDM Message header length ** fixed +#define RDM_PD_MAXLEN 32 // RDM Maximum parameter data length 1 - 231 + + +union RDM_Message +{ + uint8_t d[ RDM_HDR_LEN + RDM_PD_MAXLEN ]; + struct + { + uint8_t startCode; // 0 SC_RDM + uint8_t subStartCode; // 1 SC_SUB_MESSAGE + uint8_t msgLength; // 2 Range 24 - 255 + RDM_Uid dstUid; // 3-8 Destination UID + RDM_Uid srcUid; // 9-14 Source UID (sender) + uint8_t TN; // 15 Transaction number + uint8_t portId; // 16 Port ID / Response type + uint8_t msgCount; // 17 + uint16_t subDevice; // 18,19 0=root, 0xffff=all + uint8_t CC; // 20 GET_COMMAND + uint16_t PID; // 21,22 Parameter ID + uint8_t PDL; // 23 Parameter Data length 1-231 + + uint8_t PD[RDM_PD_MAXLEN]; // Parameter Data ... variable length + }; +}; + +union RDM_Checksum +{ + uint16_t checksum; + struct + { + uint8_t csl; + uint8_t csh; + }; +}; + +struct RDM_DiscUniqueBranchPD +{ + RDM_Uid lbound; + RDM_Uid hbound; +}; + +struct RDM_DiscMuteUnMutePD +{ + uint16_t ctrlField; + +// Only for multiple ports +// RDM_Uid bindingUid; +}; + +struct RDM__DeviceInfoPD +{ + uint8_t protocolVersionMajor; + uint8_t protocolVersionMinor; + uint16_t deviceModelId; + uint16_t ProductCategory; // enum RdmProductCategory + uint8_t SoftwareVersionId[4]; + uint16_t DMX512FootPrint; + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; + uint16_t DMX512StartAddress; + uint16_t SubDeviceCount; + uint8_t SensorCount; +}; + +struct RDM_DeviceGetPersonality_PD +{ + uint8_t DMX512CurrentPersonality; + uint8_t DMX512NumberPersonalities; +}; + +struct RDM_DeviceSetPersonality_PD +{ + uint8_t DMX512Personality; +}; + + +#endif /* RDM_DEFINES_H_ */ diff --git a/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Uid.h b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Uid.h new file mode 100644 index 000000000..35c802c63 --- /dev/null +++ b/DMX_NeoPixels/examples/RDM_Slave_Eeprom/Rdm_Uid.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2013 W.A. van der Meeren +// +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + Rdm_Uid.h - DMX library for Arduino with RDM (Remote Device Management) support + Copyright (c) 2013 W.A. van der Meeren . All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef RDM_UID_H_ +#define RDM_UID_H_ + +#include + +// +//48 bit UID Representation to identify RDM transponders +// +struct RDM_Uid { + + void Initialize ( uint16_t m, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4 ) + { + m_id[0] = ((uint8_t) (((uint16_t) (m)) >> 8)); + m_id[1] = (uint8_t)m; + m_id[2] = d1; + m_id[3] = d2; + m_id[4] = d3; + m_id[5] = d4; + } + + void copy ( const RDM_Uid &orig ) + { + for ( uint8_t i = 0; i < 6; i++ ) + m_id[i] = orig.m_id[i]; + } + + bool operator == ( const RDM_Uid & orig ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != orig.m_id[i] ) + return false; + + return true; + } + + bool operator != ( const RDM_Uid & orig ) const + { + return !(*this == orig); + } + + bool operator < ( const RDM_Uid & v ) const + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] < v.m_id[i] ); + } + + bool operator > ( const RDM_Uid & v ) + { + for ( uint8_t i = 0; i < 6; i++ ) + if ( m_id[i] != v.m_id[i] ) + return ( m_id[i] > v.m_id[i] ); + } + + // + // match_mid = manufacturer id to match + // + bool isBroadcast ( uint8_t match_mid[2] ) + { + // Check for genuine broadcast on device part + for ( uint8_t i = 2; i < 6; i++ ) + if ( m_id[i] != 0xff ) + return false; + + // Broadcast or manufacturer designated broadcast + if ( (m_id[0] == 0xff && m_id[1] == 0xff) || + (m_id[0] == match_mid[0] && m_id[1] == match_mid[1]) ) + return true; + + // No broadcast + return false; + } + + uint8_t m_id[6]; //16bit manufacturer id + 32 bits device id +}; + + +#endif /* RDM_UID_H_ */