From c0d662a9e32b0afad7acb9cf03b6eff4716105ae Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:27:51 +0000 Subject: [PATCH 01/16] Swap DMX output to also use esp_dmx --- wled00/dmx_output.cpp | 23 ++- wled00/dmx_output.h | 36 ++++ wled00/src/dependencies/dmx/SparkFunDMX.cpp | 182 -------------------- wled00/src/dependencies/dmx/SparkFunDMX.h | 42 ----- wled00/wled.h | 11 +- 5 files changed, 56 insertions(+), 238 deletions(-) create mode 100644 wled00/dmx_output.h delete mode 100644 wled00/src/dependencies/dmx/SparkFunDMX.cpp delete mode 100644 wled00/src/dependencies/dmx/SparkFunDMX.h diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index eace2145e6..6acd35046b 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -1,13 +1,12 @@ #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) * 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 @@ -72,9 +71,25 @@ 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 DMXOutput::init(uint8_t txPin) +{ + dmx_config_t config = DMX_CONFIG_DEFAULT; + dmx_driver_install(dmxPort, &config, DMX_INTR_FLAGS_DEFAULT); + dmx_set_pin(dmxPort, txPin, -1, -1); +} +void DMXOutput::write(uint8_t channel, uint8_t value) +{ + dmxdata[channel] = value; +} +void DMXOutput::update() +{ + dmx_send(dmxPort, DMX_PACKET_SIZE); +} + #else void initDMXOutput(){} void handleDMXOutput() {} diff --git a/wled00/dmx_output.h b/wled00/dmx_output.h new file mode 100644 index 0000000000..02db052269 --- /dev/null +++ b/wled00/dmx_output.h @@ -0,0 +1,36 @@ +// +// Created by will on 1/10/26. +// + +#ifndef DMX_OUTPUT_H +#define DMX_OUTPUT_H + +#if defined(ESP8266) +#include "src/dependencies/dmx/ESPDMX.h" +DMXESPSerial dmx; +#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 txPin); + 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; +}; + +DMXOutput dmx; +#endif + + +#endif //DMX_OUTPUT_H 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.h b/wled00/wled.h index 66b33740d6..e791dee6ab 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,6 @@ 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) - WLED_GLOBAL DMXESPSerial dmx; - #else //ESP32 - WLED_GLOBAL SparkFunDMX dmx; - #endif 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 From 37732ca21f46b25fe83b5e70afdb9f03acef4036 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:29:29 +0000 Subject: [PATCH 02/16] Temp enable DMX Output in all builds --- platformio.ini | 1 + 1 file changed, 1 insertion(+) 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 = From 96d4489a5e58d1460ae405a47d99baaa2aff6118 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:37:30 +0000 Subject: [PATCH 03/16] WLED_GLOBAL needed for dmx field to prevent linker errors --- wled00/dmx_output.h | 3 --- wled00/wled.h | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/wled00/dmx_output.h b/wled00/dmx_output.h index 02db052269..59c14084d7 100644 --- a/wled00/dmx_output.h +++ b/wled00/dmx_output.h @@ -7,7 +7,6 @@ #if defined(ESP8266) #include "src/dependencies/dmx/ESPDMX.h" -DMXESPSerial dmx; #else #include /** @@ -28,8 +27,6 @@ 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; }; - -DMXOutput dmx; #endif diff --git a/wled00/wled.h b/wled00/wled.h index e791dee6ab..6bfaae2f31 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -458,6 +458,11 @@ WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to f WLED_GLOBAL uint16_t DMXGap _INIT(10); // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. WLED_GLOBAL uint16_t DMXStart _INIT(10); // start address of the first fixture WLED_GLOBAL uint16_t DMXStartLED _INIT(0); // LED from which DMX fixtures start + #if defined(ESP8266) + WLED_GLOBAL DMXESPSerial dmx; + #else + WLED_GLOBAL DMXOutput dmx; + #endif #endif #ifdef WLED_ENABLE_DMX_INPUT WLED_GLOBAL int dmxInputTransmitPin _INIT(0); From 84ce66fbc0e4c656dbd88ad693cbb86f9a197ee6 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:39:56 +0000 Subject: [PATCH 04/16] ass ifdef for 8266 --- wled00/dmx_output.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index 6acd35046b..592ee25971 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -68,13 +68,10 @@ void handleDMXOutput() } void initDMXOutput() { - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) dmx.init(512); // initialize with bus length - #else - - #endif } +#if !defined(ESP8266) void DMXOutput::init(uint8_t txPin) { dmx_config_t config = DMX_CONFIG_DEFAULT; @@ -89,6 +86,8 @@ void DMXOutput::update() { dmx_send(dmxPort, DMX_PACKET_SIZE); } +#endif + #else void initDMXOutput(){} From d40f0f689e2abee48971f5b27d1efa40bdfc4ee6 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:58:20 +0000 Subject: [PATCH 05/16] Set output pin during init --- wled00/dmx_output.cpp | 5 ++-- wled00/src/dependencies/dmx/ESPDMX.cpp | 33 ++------------------------ wled00/src/dependencies/dmx/ESPDMX.h | 20 ++++++++++++++-- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index 592ee25971..f13bc80638 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -1,8 +1,7 @@ #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) + * Support for DMX output via serial (e.g. MAX485). * ESP8266 Library from: * https://github.com/Rickgg/ESP-Dmx * ESP32 Library from: @@ -68,7 +67,7 @@ void handleDMXOutput() } void initDMXOutput() { - dmx.init(512); // initialize with bus length + dmx.init(2); // set output pin and initialize DMX output } #if !defined(ESP8266) diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp index a80cad71c8..d1a31d832b 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,19 +27,6 @@ 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) { 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 From 7d3a673b619ba2b70f185e7b6fb405afe34f8ff1 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:59:00 +0000 Subject: [PATCH 06/16] Remove lazy init --- wled00/src/dependencies/dmx/ESPDMX.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp index d1a31d832b..ed78bd01e3 100644 --- a/wled00/src/dependencies/dmx/ESPDMX.cpp +++ b/wled00/src/dependencies/dmx/ESPDMX.cpp @@ -30,8 +30,6 @@ void DMXESPSerial::init(int sendPin) { // 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]); @@ -39,8 +37,6 @@ uint8_t DMXESPSerial::read(int 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; @@ -56,8 +52,6 @@ void DMXESPSerial::end() { } void DMXESPSerial::update() { - if (dmxStarted == false) init(); - //Send break digitalWrite(sendPin, HIGH); Serial1.begin(BREAKSPEED, BREAKFORMAT); From d336b97093b21b464aed086f65a163409b18137d Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 14:45:45 +0000 Subject: [PATCH 07/16] set output pin --- wled00/dmx_output.cpp | 5 +++-- wled00/fcn_declare.h | 2 +- wled00/wled.cpp | 2 +- wled00/wled.h | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index f13bc80638..a51c80d66e 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -66,8 +66,9 @@ void handleDMXOutput() dmx.update(); // update the DMX bus } -void initDMXOutput() { - dmx.init(2); // set output pin and initialize DMX output +void initDMXOutput(int outputPin) { + if (outputPin < 1) return; + dmx.init(outputPin); // set output pin and initialize DMX output } #if !defined(ESP8266) 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/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 6bfaae2f31..db6c198b8e 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -450,6 +450,7 @@ 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 + WLED_GLOBAL int dmxOutputPin _INIT(2); 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 From 576b62862c968605bae310043269042e5c1beb6c Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:07:42 +0000 Subject: [PATCH 08/16] Allow runtime config of DMX output pin --- wled00/cfg.cpp | 6 ++++++ wled00/data/settings_sync.htm | 10 +++++++++- wled00/set.cpp | 4 +++- wled00/xml.cpp | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 28b63ea65c..31e09348e9 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("outputPin")]); +#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/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/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 From 2f874cf9ac974dcd7c6f1501bb1c93a1b3fe77a7 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:08:17 +0000 Subject: [PATCH 09/16] remove redundant code --- wled00/src/dependencies/dmx/ESPDMX.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp index ed78bd01e3..e6c9193d30 100644 --- a/wled00/src/dependencies/dmx/ESPDMX.cpp +++ b/wled00/src/dependencies/dmx/ESPDMX.cpp @@ -27,20 +27,10 @@ void DMXESPSerial::init(int sendPin) { dmxStarted = true; } - -// Function to read DMX data -uint8_t DMXESPSerial::read(int Channel) { - 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 (Channel < 1) Channel = 1; if (Channel > channelSize) Channel = channelSize; - if (value < 0) value = 0; - if (value > 255) value = 255; dmxDataStore[Channel] = value; } From 034e4f542d7e9d531b167f60ef61d5fb966ce600 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:12:00 +0000 Subject: [PATCH 10/16] register pin with PinManager --- wled00/dmx_output.cpp | 6 ++++++ wled00/pin_manager.h | 1 + 2 files changed, 7 insertions(+) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index a51c80d66e..7b831b4963 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -68,6 +68,12 @@ void handleDMXOutput() 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 pins 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 } 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" From 834c285018c6635507c2c0a6b0f8fd94b31dece2 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:17:52 +0000 Subject: [PATCH 11/16] default output pin to -1 --- wled00/wled.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled.h b/wled00/wled.h index db6c198b8e..190b233a24 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -450,7 +450,7 @@ 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 - WLED_GLOBAL int dmxOutputPin _INIT(2); + 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 From 22aff94809aa05020a99ec0b5e67ab5438043a61 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:19:05 +0000 Subject: [PATCH 12/16] move dmx definition back to original location --- wled00/wled.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/wled.h b/wled00/wled.h index 190b233a24..d2da6c7c9f 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -450,6 +450,11 @@ 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) + WLED_GLOBAL DMXESPSerial dmx; + #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 @@ -459,11 +464,6 @@ WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to f WLED_GLOBAL uint16_t DMXGap _INIT(10); // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. WLED_GLOBAL uint16_t DMXStart _INIT(10); // start address of the first fixture WLED_GLOBAL uint16_t DMXStartLED _INIT(0); // LED from which DMX fixtures start - #if defined(ESP8266) - WLED_GLOBAL DMXESPSerial dmx; - #else - WLED_GLOBAL DMXOutput dmx; - #endif #endif #ifdef WLED_ENABLE_DMX_INPUT WLED_GLOBAL int dmxInputTransmitPin _INIT(0); From 585d1746c8bdaa5020a7493e42a29febfc44bfa2 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:21:05 +0000 Subject: [PATCH 13/16] minor cleanup --- wled00/dmx_output.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index 7b831b4963..bb4b07b945 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -70,7 +70,7 @@ 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 pins for DMX_OUTPUT. Pin already in use:\n"); + 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; } @@ -78,11 +78,11 @@ void initDMXOutput(int outputPin) { } #if !defined(ESP8266) -void DMXOutput::init(uint8_t txPin) +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, txPin, -1, -1); + dmx_set_pin(dmxPort, outputPin, -1, -1); } void DMXOutput::write(uint8_t channel, uint8_t value) { From 7a910c8899cc8fec45c8cb7824e18dcbf8bc4ccf Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:21:58 +0000 Subject: [PATCH 14/16] minor cleanup, code style --- wled00/dmx_output.cpp | 9 +++------ wled00/dmx_output.h | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index bb4b07b945..f1457dfb59 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -78,18 +78,15 @@ void initDMXOutput(int outputPin) { } #if !defined(ESP8266) -void DMXOutput::init(uint8_t outputPin) -{ +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) -{ +void DMXOutput::write(uint8_t channel, uint8_t value) { dmxdata[channel] = value; } -void DMXOutput::update() -{ +void DMXOutput::update() { dmx_send(dmxPort, DMX_PACKET_SIZE); } #endif diff --git a/wled00/dmx_output.h b/wled00/dmx_output.h index 59c14084d7..e292634cb3 100644 --- a/wled00/dmx_output.h +++ b/wled00/dmx_output.h @@ -17,7 +17,7 @@ class DMXOutput { public: - void init(uint8_t txPin); + void init(uint8_t outputPin); void write(uint8_t channel, uint8_t value); void update(); private: From 5cdca5829629b9ed35194d9645277af36c868e9d Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sun, 11 Jan 2026 00:15:36 +0000 Subject: [PATCH 15/16] Fix naming --- wled00/cfg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 31e09348e9..4f00b53b8f 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -598,7 +598,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (tdd >= 0) realtimeTimeoutMs = tdd * 100; #ifdef WLED_ENABLE_DMX - CJSON(dmxOutputPin, if_live_dmx[F("outputPin")]); + CJSON(dmxOutputPin, if_live_dmx[F("dmxOutputPin")]); #endif #ifdef WLED_ENABLE_DMX_INPUT CJSON(dmxInputTransmitPin, if_live_dmx[F("inputRxPin")]); From 48168edb4c05514bdec76d0f8fabdc01082cb7df Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sun, 11 Jan 2026 00:19:52 +0000 Subject: [PATCH 16/16] write data --- wled00/dmx_output.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index f1457dfb59..2be3371ee7 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -87,6 +87,7 @@ 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