From c0d662a9e32b0afad7acb9cf03b6eff4716105ae Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:27:51 +0000 Subject: [PATCH 01/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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 From 93ef4549e1f3e7dca73d59085de66e9c47644026 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:27:51 +0000 Subject: [PATCH 17/35] 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 21b340d94c..2a754702d7 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -142,11 +142,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 cd08d6f04e138ad0f68978855e53390b8a507092 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:29:29 +0000 Subject: [PATCH 18/35] Temp enable DMX Output in all builds --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 29949d33c0..1645a52727 100644 --- a/platformio.ini +++ b/platformio.ini @@ -122,6 +122,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 c8ff29225f514e3c5f714412fdd68712812718f2 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:37:30 +0000 Subject: [PATCH 19/35] 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 2a754702d7..6bba51ea55 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 b11c7f7a62f17c923317d432225de50f96351fc0 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:39:56 +0000 Subject: [PATCH 20/35] 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 d5061ed602d2e703e922e07b41030cd02a11e25f Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:58:20 +0000 Subject: [PATCH 21/35] 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 1ccef16ba4de2b2122b23cceb5737db59cd91f01 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 13:59:00 +0000 Subject: [PATCH 22/35] 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 4a60207833ca3c8091da6b2b7c77e818b37c75be Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 14:45:45 +0000 Subject: [PATCH 23/35] 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 67958314b4..86722a7c05 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -90,7 +90,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 d67f784078..f66bd974ec 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -555,7 +555,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 6bba51ea55..517457cc5e 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 3b92b2d58f373bd540054fb1c4b0d60bd690e8e5 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:07:42 +0000 Subject: [PATCH 24/35] Allow runtime config of DMX output pin --- wled00/cfg.cpp | 6 ++++++ wled00/data/settings_sync.htm | 11 +++++++++-- wled00/set.cpp | 4 +++- wled00/xml.cpp | 1 + 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 97b48c4ab6..be5950643f 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -607,6 +607,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")]); @@ -1133,6 +1136,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 43baaf6718..97937f70b3 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -50,7 +50,10 @@ } function hideDMXInput(){gId("dmxInput").style.display="none";} function hideNoDMXInput(){gId("dmxInputOff").style.display="none";} - function hideNoDMXOutput(){gId("dmxOnOffOutput").style.display="none";} + function hideNoDMXOutput(){ + gId("dmxOnOffOutput").style.display="none"; + gId("dmxOutput").style.display="inline"; + } @@ -179,7 +182,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 3c6c72b7b3..a1e29840cc 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -473,7 +473,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 dceebbdf09..8091569797 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -494,6 +494,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) printSetFormValue(settingsScript,PSTR("EU"),e131Universe); #ifdef WLED_ENABLE_DMX settingsScript.print(SET_F("hideNoDMXOutput();")); // 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 ec6435593acf13d992382481e9a6d7be6ccde5eb Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:08:17 +0000 Subject: [PATCH 25/35] 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 02274e19da9146acb1f7a41095e0f50e8171084a Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:12:00 +0000 Subject: [PATCH 26/35] 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 5f774bb47d..b0c6ed8056 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -48,6 +48,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 b8fcb1c1d871695055a7de1d961c1b82e6768cef Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:17:52 +0000 Subject: [PATCH 27/35] 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 517457cc5e..3e73446dc8 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 306ba83e506465558f54383689a0ebcec7c3638f Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:19:05 +0000 Subject: [PATCH 28/35] 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 3e73446dc8..784e756ccb 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 f5522e6c6b472f23be96cb56e556fbf1e3e28d1d Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:21:05 +0000 Subject: [PATCH 29/35] 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 38330b54ae971d58c74fde64f2ee98b39fa42854 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 10 Jan 2026 15:21:58 +0000 Subject: [PATCH 30/35] 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 daa222307849c97dffd4c7c6bb35859463e1505e Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sun, 11 Jan 2026 00:15:36 +0000 Subject: [PATCH 31/35] Fix naming --- wled00/cfg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index be5950643f..4268c70a2f 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -608,7 +608,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 e28774df1f9b1864dc804d3f3d380990df8878f1 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sun, 11 Jan 2026 00:19:52 +0000 Subject: [PATCH 32/35] 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 From c20f59dc28e57e02a6222c3331f26107261df0e8 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 20 Mar 2026 08:45:20 +0000 Subject: [PATCH 33/35] DMXInput: try to assign pins before UART port and default back to 1 if requested port is 2 on device with only 1 --- wled00/dmx_input.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/wled00/dmx_input.cpp b/wled00/dmx_input.cpp index 83ab606688..6ab1d4af33 100644 --- a/wled00/dmx_input.cpp +++ b/wled00/dmx_input.cpp @@ -134,14 +134,6 @@ void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPo // } #endif - if (inputPortNum <= (SOC_UART_NUM - 1) && inputPortNum > 0) { - this->inputPortNum = inputPortNum; - } - else { - DEBUG_PRINTF("DMXInput: Error: invalid inputPortNum: %d\n", inputPortNum); - return; - } - if (rxPin > 0 && enPin > 0 && txPin > 0) { const managed_pin_type pins[] = { @@ -156,6 +148,15 @@ void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPo DEBUG_PRINTF("en in use by: %s\n", PinManager::getPinOwner(enPin)); return; } + if (inputPortNum <= (SOC_UART_NUM - 1) && inputPortNum > 0) { + this->inputPortNum = inputPortNum; + } + else { + DEBUG_PRINTF("DMXInput: Error: invalid inputPortNum: %d, default to 1\n", inputPortNum); + this->inputPortNum = 1; + return; + } + this->rxPin = rxPin; this->txPin = txPin; From ea3aba9d22de4673890cc230b92bf20edc4055a9 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 20 Mar 2026 08:46:11 +0000 Subject: [PATCH 34/35] DMXInput: No need to swap default pin for LED when DMX not hard-coded --- wled00/const.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/wled00/const.h b/wled00/const.h index 95e69d855b..aac2c232a6 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -650,13 +650,8 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); // Defaults pins, type and counts to configure LED output #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) - #ifdef WLED_ENABLE_DMX - #define DEFAULT_LED_PIN 1 - #warning "Compiling with DMX. The default LED pin has been changed to pin 1." - #else #define DEFAULT_LED_PIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board - #endif -#else + #else #if defined(WLED_USE_ETHERNET) #define DEFAULT_LED_PIN 4 // GPIO4 seems to be a "safe bet" for all known ethernet boards (issue #5155) //#warning "Compiling with Ethernet support. The default LED pin has been changed to pin 4." From ab632e9a00036d45b9d23d091bc8b60e85fb4505 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 20 Mar 2026 08:48:57 +0000 Subject: [PATCH 35/35] DMXInput: Move allocation of pin to initDMXOutput --- wled00/dmx_output.cpp | 15 ++++++++++++--- wled00/pin_manager.h | 3 +-- wled00/wled.cpp | 3 --- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index 2be3371ee7..8e9fc541ee 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -68,20 +68,29 @@ void handleDMXOutput() void initDMXOutput(int outputPin) { if (outputPin < 1) return; - const bool pinAllocated = PinManager::allocatePin(outputPin, true, PinOwner::DMX_OUTPUT); + const bool pinAllocated = PinManager::allocatePin(outputPin, true, PinOwner::DMX); 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; } + DEBUG_PRINTF("DMXOutput: init: pin %d\n", outputPin); 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); + const bool installOk = dmx_driver_install(dmxPort, &config, DMX_INTR_FLAGS_DEFAULT); + if (!installOk) { + DEBUG_PRINTF("DMXOutput: Error: Failed to install dmx driver\n"); + return; + } + const bool setPin = dmx_set_pin(dmxPort, outputPin, -1, -1); + if (!setPin) { + DEBUG_PRINTF("DMXOutput: Error: Failed to set DMX output pin\n"); + return; + } } void DMXOutput::write(uint8_t channel, uint8_t value) { dmxdata[channel] = value; diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index b0c6ed8056..cbc47ce2cc 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -43,12 +43,11 @@ enum struct PinOwner : uint8_t { Relay = 0x87, // 'Rly' == Relay pin from configuration SPI_RAM = 0x88, // 'SpiR' == SPI RAM DebugOut = 0x89, // 'Dbg' == debug output always IO1 - DMX = 0x8A, // 'DMX' == hard-coded to IO2 + DMX = 0x8A, // 'DMX' == DMX output via serial HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32) 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/wled.cpp b/wled00/wled.cpp index f66bd974ec..7d5fea8782 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -443,9 +443,6 @@ void WLED::setup() #if defined(WLED_DEBUG) && !defined(WLED_DEBUG_HOST) PinManager::allocatePin(hardwareTX, true, PinOwner::DebugOut); // TX (GPIO1 on ESP32) reserved for debug output #endif -#ifdef WLED_ENABLE_DMX //reserve GPIO2 as hardcoded DMX pin - PinManager::allocatePin(2, true, PinOwner::DMX); -#endif DEBUG_PRINTF_P(PSTR("heap %u\n"), getFreeHeapSize());