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