diff --git a/platformio.ini b/platformio.ini index ec73bc5658..848dad8e3b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -121,6 +121,7 @@ build_flags = -D DECODE_LG=true -DWLED_USE_MY_CONFIG -D WLED_PS_DONT_REPLACE_FX ; PS replacement FX are purely a flash memory saving feature, do not replace classic FX until we run out of flash + -D WLED_ENABLE_DMX build_unflags = diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 28b63ea65c..4f00b53b8f 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -597,6 +597,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { tdd = if_live[F("timeout")] | -1; if (tdd >= 0) realtimeTimeoutMs = tdd * 100; +#ifdef WLED_ENABLE_DMX + CJSON(dmxOutputPin, if_live_dmx[F("dmxOutputPin")]); +#endif #ifdef WLED_ENABLE_DMX_INPUT CJSON(dmxInputTransmitPin, if_live_dmx[F("inputRxPin")]); CJSON(dmxInputReceivePin, if_live_dmx[F("inputTxPin")]); @@ -1118,6 +1121,9 @@ void serializeConfig(JsonObject root) { if_live_dmx[F("addr")] = DMXAddress; if_live_dmx[F("dss")] = DMXSegmentSpacing; if_live_dmx["mode"] = DMXMode; + #ifdef WLED_ENABLE_DMX + if_live_dmx[F("dmxOutputPin")] = dmxOutputPin; + #endif #ifdef WLED_ENABLE_DMX_INPUT if_live_dmx[F("inputRxPin")] = dmxInputTransmitPin; if_live_dmx[F("inputTxPin")] = dmxInputReceivePin; diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 73e4d9a268..28624030fe 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -50,6 +50,10 @@ } function hideDMXInput(){gId("dmxInput").style.display="none";} function hideNoDMXInput(){gId("dmxInputOff").style.display="none";} + function hideNoDMX(){ + gId("dmxOnOff2").style.display="none"; + gId("dmxOutput").style.display="inline"; + } @@ -166,7 +170,11 @@

Wired DMX Input Pins

DMX TX: DI
DMX Enable: RE+DE
DMX Port:
- + +

This firmware build does not include DMX Input support.
diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index eace2145e6..2be3371ee7 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -1,13 +1,11 @@ #include "wled.h" - +#include "dmx_output.h" /* - * Support for DMX output via serial (e.g. MAX485). - * Change the output pin in src/dependencies/ESPDMX.cpp, if needed (ESP8266) - * Change the output pin in src/dependencies/SparkFunDMX.cpp, if needed (ESP32) + * Support for DMX output via serial (e.g. MAX485). * ESP8266 Library from: * https://github.com/Rickgg/ESP-Dmx * ESP32 Library from: - * https://github.com/sparkfun/SparkFunDMX + * https://github.com/someweisguy/esp_dmx */ #ifdef WLED_ENABLE_DMX @@ -68,13 +66,33 @@ void handleDMXOutput() dmx.update(); // update the DMX bus } -void initDMXOutput() { - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) - dmx.init(512); // initialize with bus length - #else - dmx.initWrite(512); // initialize with bus length - #endif +void initDMXOutput(int outputPin) { + if (outputPin < 1) return; + const bool pinAllocated = PinManager::allocatePin(outputPin, true, PinOwner::DMX_OUTPUT); + if (!pinAllocated) { + DEBUG_PRINTF("DMXOutput: Error: Failed to allocate pin for DMX_OUTPUT. Pin already in use:\n"); + DEBUG_PRINTF("In use by: %s\n", PinManager::getPinOwner(outputPin)); + return; + } + dmx.init(outputPin); // set output pin and initialize DMX output +} + +#if !defined(ESP8266) +void DMXOutput::init(uint8_t outputPin) { + dmx_config_t config = DMX_CONFIG_DEFAULT; + dmx_driver_install(dmxPort, &config, DMX_INTR_FLAGS_DEFAULT); + dmx_set_pin(dmxPort, outputPin, -1, -1); +} +void DMXOutput::write(uint8_t channel, uint8_t value) { + dmxdata[channel] = value; } +void DMXOutput::update() { + dmx_write(dmxPort, dmxdata, DMX_PACKET_SIZE); + dmx_send(dmxPort, DMX_PACKET_SIZE); +} +#endif + + #else void initDMXOutput(){} void handleDMXOutput() {} diff --git a/wled00/dmx_output.h b/wled00/dmx_output.h new file mode 100644 index 0000000000..e292634cb3 --- /dev/null +++ b/wled00/dmx_output.h @@ -0,0 +1,33 @@ +// +// Created by will on 1/10/26. +// + +#ifndef DMX_OUTPUT_H +#define DMX_OUTPUT_H + +#if defined(ESP8266) +#include "src/dependencies/dmx/ESPDMX.h" +#else +#include +/** + * Support for DMX Output via serial (e.g. max485) on ESP32 + * ESP32 Library from: + * https://github.com/someweisguy/esp_dmx + */ +class DMXOutput +{ +public: + void init(uint8_t outputPin); + void write(uint8_t channel, uint8_t value); + void update(); +private: + byte dmxdata[DMX_PACKET_SIZE]; + /* Next, lets decide which DMX port to use. The ESP32 has either 2 or 3 ports. +Port 0 is typically used to transmit serial data back to your Serial Monitor, +so we shouldn't use that port. Lets use port 1! */ + dmx_port_t dmxPort = 1; +}; +#endif + + +#endif //DMX_OUTPUT_H diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 84b5595df7..973e176d88 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -75,7 +75,7 @@ typedef struct WiFiConfig { } wifi_config; //dmx_output.cpp -void initDMXOutput(); +void initDMXOutput(int outputPin); void handleDMXOutput(); //dmx_input.cpp diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index a488d24f70..98a9c1c435 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -41,6 +41,7 @@ enum struct PinOwner : uint8_t { HW_SPI = 0x8C, // 'SPI' == hardware (V)SPI pins (13,14&15 on ESP8266, 5,18&23 on ESP32) DMX_INPUT = 0x8D, // 'DMX_INPUT' == DMX input via serial HUB75 = 0x8E, // 'Hub75' == Hub75 driver + DMX_OUTPUT = 0x8F, // 'DMX_OUTPUT' == DMX output via serial // Use UserMod IDs from const.h here UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01 UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h" diff --git a/wled00/set.cpp b/wled00/set.cpp index db8b30bac8..7ebd593ab7 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -438,7 +438,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) arlsDisableGammaCorrection = request->hasArg(F("RG")); t = request->arg(F("WO")).toInt(); if (t >= -255 && t <= 255) arlsOffset = t; - +#ifdef WLED_ENABLE_DMX + dmxOutputPin = request->arg(F("IDMO")).toInt(); +#endif #ifdef WLED_ENABLE_DMX_INPUT dmxInputTransmitPin = request->arg(F("IDMT")).toInt(); dmxInputReceivePin = request->arg(F("IDMR")).toInt(); diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp index a80cad71c8..e6c9193d30 100644 --- a/wled00/src/dependencies/dmx/ESPDMX.cpp +++ b/wled00/src/dependencies/dmx/ESPDMX.cpp @@ -18,24 +18,8 @@ #include "ESPDMX.h" - -#define dmxMaxChannel 512 -#define defaultMax 32 - -#define DMXSPEED 250000 -#define DMXFORMAT SERIAL_8N2 -#define BREAKSPEED 83333 -#define BREAKFORMAT SERIAL_8N1 - -bool dmxStarted = false; -int sendPin = 2; //default on ESP8266 - -//DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements -uint8_t dmxDataStore[dmxMaxChannel+1] = {}; -int channelSize; - - -void DMXESPSerial::init() { +void DMXESPSerial::init(int sendPin) { + this->sendPin = sendPin; channelSize = defaultMax; Serial1.begin(DMXSPEED); @@ -43,37 +27,10 @@ void DMXESPSerial::init() { dmxStarted = true; } -// Set up the DMX-Protocol -void DMXESPSerial::init(int chanQuant) { - - if (chanQuant > dmxMaxChannel || chanQuant <= 0) { - chanQuant = defaultMax; - } - - channelSize = chanQuant; - - Serial1.begin(DMXSPEED); - pinMode(sendPin, OUTPUT); - dmxStarted = true; -} - -// Function to read DMX data -uint8_t DMXESPSerial::read(int Channel) { - if (dmxStarted == false) init(); - - if (Channel < 1) Channel = 1; - if (Channel > dmxMaxChannel) Channel = dmxMaxChannel; - return(dmxDataStore[Channel]); -} - // Function to send DMX data void DMXESPSerial::write(int Channel, uint8_t value) { - if (dmxStarted == false) init(); - if (Channel < 1) Channel = 1; if (Channel > channelSize) Channel = channelSize; - if (value < 0) value = 0; - if (value > 255) value = 255; dmxDataStore[Channel] = value; } @@ -85,8 +42,6 @@ void DMXESPSerial::end() { } void DMXESPSerial::update() { - if (dmxStarted == false) init(); - //Send break digitalWrite(sendPin, HIGH); Serial1.begin(BREAKSPEED, BREAKFORMAT); diff --git a/wled00/src/dependencies/dmx/ESPDMX.h b/wled00/src/dependencies/dmx/ESPDMX.h index 4585bdd26f..f3f5a42df5 100644 --- a/wled00/src/dependencies/dmx/ESPDMX.h +++ b/wled00/src/dependencies/dmx/ESPDMX.h @@ -16,16 +16,32 @@ #ifndef ESPDMX_h #define ESPDMX_h + +#define dmxMaxChannel 512 +#define defaultMax 32 + +#define DMXSPEED 250000 +#define DMXFORMAT SERIAL_8N2 +#define BREAKSPEED 83333 +#define BREAKFORMAT SERIAL_8N1 + // ---- Methods ---- class DMXESPSerial { public: - void init(); - void init(int MaxChan); + void init(int sendPin); uint8_t read(int Channel); void write(int channel, uint8_t value); void update(); void end(); +private: + int sendPin; + bool dmxStarted = false; + + //DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements + uint8_t dmxDataStore[dmxMaxChannel+1] = {}; + int channelSize; + }; #endif diff --git a/wled00/src/dependencies/dmx/SparkFunDMX.cpp b/wled00/src/dependencies/dmx/SparkFunDMX.cpp deleted file mode 100644 index 064b9ff620..0000000000 --- a/wled00/src/dependencies/dmx/SparkFunDMX.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/****************************************************************************** -SparkFunDMX.h -Arduino Library for the SparkFun ESP32 LED to DMX Shield -Andy England @ SparkFun Electronics -7/22/2019 - -Development environment specifics: -Arduino IDE 1.6.4 - -This code is released under the [MIT License](http://opensource.org/licenses/MIT). -Please review the LICENSE.md file included with this example. If you have any questions -or concerns with licensing, please contact techsupport@sparkfun.com. -Distributed as-is; no warranty is given. -******************************************************************************/ - -/* ----- LIBRARIES ----- */ -#if defined(ARDUINO_ARCH_ESP32) - -#include -#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) - -#include "SparkFunDMX.h" -#include - -#define dmxMaxChannel 512 -#define defaultMax 32 - -#define DMXSPEED 250000 -#define DMXFORMAT SERIAL_8N2 -#define BREAKSPEED 83333 -#define BREAKFORMAT SERIAL_8N1 - -static const int enablePin = -1; // disable the enable pin because it is not needed -static const int rxPin = -1; // disable the receiving pin because it is not needed - softhack007: Pin=-1 means "use default" not "disable" -static const int txPin = 2; // transmit DMX data over this pin (default is pin 2) - -//DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements -static uint8_t dmxData[dmxMaxChannel+1] = { 0 }; -static int chanSize = 0; -#if !defined(DMX_SEND_ONLY) -static int currentChannel = 0; -#endif - -// Some new MCUs (-S2, -C3) don't have HardwareSerial(2) -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) - #if SOC_UART_NUM < 3 - #error DMX output is not possible on your MCU, as it does not have HardwareSerial(2) - #endif -#endif - -static HardwareSerial DMXSerial(2); - -/* Interrupt Timer for DMX Receive */ -#if !defined(DMX_SEND_ONLY) -static hw_timer_t * timer = NULL; -static portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; -#endif - -static volatile int _interruptCounter = 0; -static volatile bool _startCodeDetected = false; - - -#if !defined(DMX_SEND_ONLY) -/* Start Code is detected by 21 low interrupts */ -void IRAM_ATTR onTimer() { - if ((rxPin >= 0) && (digitalRead(rxPin) == 1)) - { - _interruptCounter = 0; //If the RX Pin is high, we are not in an interrupt - } - else - { - _interruptCounter++; - } - if (_interruptCounter > 9) - { - portENTER_CRITICAL_ISR(&timerMux); - _startCodeDetected = true; - DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin); - portEXIT_CRITICAL_ISR(&timerMux); - _interruptCounter = 0; - } -} - -void SparkFunDMX::initRead(int chanQuant) { - - timer = timerBegin(0, 1, true); - timerAttachInterrupt(timer, &onTimer, true); - timerAlarmWrite(timer, 320, true); - timerAlarmEnable(timer); - _READWRITE = _READ; - if (chanQuant > dmxMaxChannel || chanQuant <= 0) - { - chanQuant = defaultMax; - } - chanSize = chanQuant; - if (enablePin >= 0) { - pinMode(enablePin, OUTPUT); - digitalWrite(enablePin, LOW); - } - if (rxPin >= 0) pinMode(rxPin, INPUT); -} -#endif - -// Set up the DMX-Protocol -void SparkFunDMX::initWrite (int chanQuant) { - - _READWRITE = _WRITE; - if (chanQuant > dmxMaxChannel || chanQuant <= 0) { - chanQuant = defaultMax; - } - - chanSize = chanQuant + 1; //Add 1 for start code - - DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin); - if (enablePin >= 0) { - pinMode(enablePin, OUTPUT); - digitalWrite(enablePin, HIGH); - } -} - -#if !defined(DMX_SEND_ONLY) -// Function to read DMX data -uint8_t SparkFunDMX::read(int Channel) { - if (Channel > chanSize) Channel = chanSize; - return(dmxData[Channel - 1]); //subtract one to account for start byte -} -#endif - -// Function to send DMX data -void SparkFunDMX::write(int Channel, uint8_t value) { - if (Channel < 0) Channel = 0; - if (Channel > chanSize) chanSize = Channel; - dmxData[0] = 0; - dmxData[Channel] = value; //add one to account for start byte -} - - - -void SparkFunDMX::update() { - if (_READWRITE == _WRITE) - { - //Send DMX break - digitalWrite(txPin, HIGH); - DMXSerial.begin(BREAKSPEED, BREAKFORMAT, rxPin, txPin);//Begin the Serial port - DMXSerial.write(0); - DMXSerial.flush(); - delay(1); - DMXSerial.end(); - - //Send DMX data - DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);//Begin the Serial port - DMXSerial.write(dmxData, chanSize); - DMXSerial.flush(); - DMXSerial.end();//clear our DMX array, end the Hardware Serial port - } -#if !defined(DMX_SEND_ONLY) - else if (_READWRITE == _READ)//In a perfect world, this function ends serial communication upon packet completion and attaches RX to a CHANGE interrupt so the start code can be read again - { - if (_startCodeDetected == true) - { - while (DMXSerial.available()) - { - dmxData[currentChannel++] = DMXSerial.read(); - } - if (currentChannel > chanSize) //Set the channel counter back to 0 if we reach the known end size of our packet - { - - portENTER_CRITICAL(&timerMux); - _startCodeDetected = false; - DMXSerial.flush(); - DMXSerial.end(); - portEXIT_CRITICAL(&timerMux); - currentChannel = 0; - } - } - } -#endif -} - -// Function to update the DMX bus -#endif -#endif diff --git a/wled00/src/dependencies/dmx/SparkFunDMX.h b/wled00/src/dependencies/dmx/SparkFunDMX.h deleted file mode 100644 index 73861153b2..0000000000 --- a/wled00/src/dependencies/dmx/SparkFunDMX.h +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** -SparkFunDMX.h -Arduino Library for the SparkFun ESP32 LED to DMX Shield -Andy England @ SparkFun Electronics -7/22/2019 - -Development environment specifics: -Arduino IDE 1.6.4 - -This code is released under the [MIT License](http://opensource.org/licenses/MIT). -Please review the LICENSE.md file included with this example. If you have any questions -or concerns with licensing, please contact techsupport@sparkfun.com. -Distributed as-is; no warranty is given. -******************************************************************************/ - -#include - - -#ifndef SparkFunDMX_h -#define SparkFunDMX_h - -#define DMX_SEND_ONLY // this disables DMX sending features, and saves us two GPIO pins - -// ---- Methods ---- - -class SparkFunDMX { -public: - void initWrite(int maxChan); -#if !defined(DMX_SEND_ONLY) - void initRead(int maxChan); - uint8_t read(int Channel); -#endif - void write(int channel, uint8_t value); - void update(); -private: - const uint8_t _startCodeValue = 0xFF; - const bool _READ = true; - const bool _WRITE = false; - bool _READWRITE; -}; - -#endif \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index c0ec92a916..28713d7254 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -517,7 +517,7 @@ void WLED::setup() } #endif #ifdef WLED_ENABLE_DMX - initDMXOutput(); + initDMXOutput(dmxOutputPin); #endif #ifdef WLED_ENABLE_DMX_INPUT dmxInput.init(dmxInputReceivePin, dmxInputTransmitPin, dmxInputEnablePin, dmxInputPort); diff --git a/wled00/wled.h b/wled00/wled.h index 66b33740d6..d2da6c7c9f 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -139,11 +139,7 @@ #endif #ifdef WLED_ENABLE_DMX - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) - #include "src/dependencies/dmx/ESPDMX.h" - #else //ESP32 - #include "src/dependencies/dmx/SparkFunDMX.h" - #endif +#include "dmx_output.h" #endif #ifdef WLED_ENABLE_DMX_INPUT @@ -454,11 +450,12 @@ WLED_GLOBAL bool arlsDisableGammaCorrection _INIT(true); // activate if WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to force max brightness if source has very dark colors that would be black #ifdef WLED_ENABLE_DMX - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) + #if defined(ESP8266) WLED_GLOBAL DMXESPSerial dmx; - #else //ESP32 - WLED_GLOBAL SparkFunDMX dmx; - #endif + #else + WLED_GLOBAL DMXOutput dmx; + #endif + WLED_GLOBAL int dmxOutputPin _INIT(-1); // DMX output pin (use -1 for disabled) WLED_GLOBAL uint16_t e131ProxyUniverse _INIT(0); // output this E1.31 (sACN) / ArtNet universe via MAX485 (0 = disabled) // dmx CONFIG WLED_GLOBAL byte DMXChannels _INIT(7); // number of channels per fixture diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 194256d82e..5351e5ea89 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -460,6 +460,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) printSetFormValue(settingsScript,PSTR("EU"),e131Universe); #ifdef WLED_ENABLE_DMX settingsScript.print(SET_F("hideNoDMX();")); // hide "not compiled in" message + printSetFormValue(settingsScript,SET_F("IDMO"), dmxOutputPin); #endif #ifndef WLED_ENABLE_DMX_INPUT settingsScript.print(SET_F("hideDMXInput();")); // hide "dmx input" settings