diff --git a/platformio.ini b/platformio.ini
index 6d959fe121..5e0c01db79 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -282,8 +282,10 @@ build_flags = -g
-DARDUINO_ARCH_ESP32 -DESP32
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
+ -D WLED_ENABLE_DMX_INPUT
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ https://github.com/someweisguy/esp_dmx.git#47db25d
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 38e804ed97..443f16c73b 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -522,6 +522,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
tdd = if_live[F("timeout")] | -1;
if (tdd >= 0) realtimeTimeoutMs = tdd * 100;
+
+ #ifdef WLED_ENABLE_DMX_INPUT
+ CJSON(dmxInputTransmitPin, if_live_dmx[F("inputRxPin")]);
+ CJSON(dmxInputReceivePin, if_live_dmx[F("inputTxPin")]);
+ CJSON(dmxInputEnablePin, if_live_dmx[F("inputEnablePin")]);
+ CJSON(dmxInputPort, if_live_dmx[F("dmxInputPort")]);
+ #endif
+
CJSON(arlsForceMaxBri, if_live[F("maxbri")]);
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
CJSON(arlsOffset, if_live[F("offset")]); // 0
@@ -1001,6 +1009,12 @@ void serializeConfig() {
if_live_dmx[F("addr")] = DMXAddress;
if_live_dmx[F("dss")] = DMXSegmentSpacing;
if_live_dmx["mode"] = DMXMode;
+ #ifdef WLED_ENABLE_DMX_INPUT
+ if_live_dmx[F("inputRxPin")] = dmxInputTransmitPin;
+ if_live_dmx[F("inputTxPin")] = dmxInputReceivePin;
+ if_live_dmx[F("inputEnablePin")] = dmxInputEnablePin;
+ if_live_dmx[F("dmxInputPort")] = dmxInputPort;
+ #endif
if_live[F("timeout")] = realtimeTimeoutMs / 100;
if_live[F("maxbri")] = arlsForceMaxBri;
diff --git a/wled00/const.h b/wled00/const.h
index 6a023fadc7..3f82219d31 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -250,6 +250,7 @@
#define REALTIME_MODE_ARTNET 6
#define REALTIME_MODE_TPM2NET 7
#define REALTIME_MODE_DDP 8
+#define REALTIME_MODE_DMX 9
//realtime override modes
#define REALTIME_OVERRIDE_NONE 0
diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm
index 34b9fc6cdb..775f87a964 100644
--- a/wled00/data/settings_sync.htm
+++ b/wled00/data/settings_sync.htm
@@ -151,6 +151,19 @@
Realtime
Force max brightness:
Disable realtime gamma correction:
Realtime LED offset:
+
+
Wired DMX Input Pins
+ DMX RX: RO
+ DMX TX: DI
+ DMX Enable: RE+DE
+ DMX Port:
+
+
+
This firmware build does not include DMX Input support.
+
+
+
This firmware build does not include DMX output support.
+
Alexa Voice Assistant
diff --git a/wled00/dmx_input.cpp b/wled00/dmx_input.cpp
new file mode 100644
index 0000000000..afbc9f0d0b
--- /dev/null
+++ b/wled00/dmx_input.cpp
@@ -0,0 +1,280 @@
+#include "wled.h"
+
+#ifdef WLED_ENABLE_DMX_INPUT
+
+#ifdef ESP8266
+#error DMX input is only supported on ESP32
+#endif
+
+#include "dmx_input.h"
+#include
+
+void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
+ void *context)
+{
+ DMXInput *dmx = static_cast(context);
+
+ if (!dmx) {
+ DEBUG_PRINTLN("DMX: Error: no context in rdmPersonalityChangedCb");
+ return;
+ }
+
+ if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
+ const uint8_t personality = dmx_get_current_personality(dmx->inputPortNum);
+ DMXMode = std::min(DMX_MODE_PRESET, std::max(DMX_MODE_SINGLE_RGB, int(personality)));
+ doSerializeConfig = true;
+ DEBUG_PRINTF("DMX personality changed to to: %d\n", DMXMode);
+ }
+}
+
+void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
+ void *context)
+{
+ DMXInput *dmx = static_cast(context);
+
+ if (!dmx) {
+ DEBUG_PRINTLN("DMX: Error: no context in rdmAddressChangedCb");
+ return;
+ }
+
+ if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
+ const uint16_t addr = dmx_get_start_address(dmx->inputPortNum);
+ DMXAddress = std::min(512, int(addr));
+ doSerializeConfig = true;
+ DEBUG_PRINTF("DMX start addr changed to: %d\n", DMXAddress);
+ }
+}
+
+static dmx_config_t createConfig()
+{
+ dmx_config_t config;
+ config.pd_size = 255;
+ config.dmx_start_address = DMXAddress;
+ config.model_id = 0;
+ config.product_category = RDM_PRODUCT_CATEGORY_FIXTURE;
+ config.software_version_id = VERSION;
+ strcpy(config.device_label, "WLED_MM");
+
+ const std::string versionString = "WLED_V" + std::to_string(VERSION);
+ strncpy(config.software_version_label, versionString.c_str(), 32);
+ config.software_version_label[32] = '\0'; // zero termination in case versionString string was longer than 32 chars
+
+ config.personalities[0].description = "SINGLE_RGB";
+ config.personalities[0].footprint = 3;
+ config.personalities[1].description = "SINGLE_DRGB";
+ config.personalities[1].footprint = 4;
+ config.personalities[2].description = "EFFECT";
+ config.personalities[2].footprint = 15;
+ config.personalities[3].description = "MULTIPLE_RGB";
+ config.personalities[3].footprint = std::min(512, int(strip.getLengthTotal()) * 3);
+ config.personalities[4].description = "MULTIPLE_DRGB";
+ config.personalities[4].footprint = std::min(512, int(strip.getLengthTotal()) * 3 + 1);
+ config.personalities[5].description = "MULTIPLE_RGBW";
+ config.personalities[5].footprint = std::min(512, int(strip.getLengthTotal()) * 4);
+ config.personalities[6].description = "EFFECT_W";
+ config.personalities[6].footprint = 18;
+ config.personalities[7].description = "EFFECT_SEGMENT";
+ config.personalities[7].footprint = std::min(512, strip.getSegmentsNum() * 15);
+ config.personalities[8].description = "EFFECT_SEGMENT_W";
+ config.personalities[8].footprint = std::min(512, strip.getSegmentsNum() * 18);
+ config.personalities[9].description = "PRESET";
+ config.personalities[9].footprint = 1;
+
+ config.personality_count = 10;
+ // rdm personalities are numbered from 1, thus we can just set the DMXMode directly.
+ config.current_personality = DMXMode;
+
+ return config;
+}
+
+void dmxReceiverTask(void *context)
+{
+ DMXInput *instance = static_cast(context);
+ if (instance == nullptr) {
+ return;
+ }
+
+ if (instance->installDriver()) {
+ while (true) {
+ instance->updateInternal();
+ }
+ }
+}
+
+bool DMXInput::installDriver()
+{
+
+ const auto config = createConfig();
+ DEBUG_PRINTF("DMX port: %u\n", inputPortNum);
+ if (!dmx_driver_install(inputPortNum, &config, DMX_INTR_FLAGS_DEFAULT)) {
+ DEBUG_PRINTF("Error: Failed to install dmx driver\n");
+ return false;
+ }
+
+ DEBUG_PRINTF("Listening for DMX on pin %u\n", rxPin);
+ DEBUG_PRINTF("Sending DMX on pin %u\n", txPin);
+ DEBUG_PRINTF("DMX enable pin is: %u\n", enPin);
+ dmx_set_pin(inputPortNum, txPin, rxPin, enPin);
+
+ rdm_register_dmx_start_address(inputPortNum, rdmAddressChangedCb, this);
+ rdm_register_dmx_personality(inputPortNum, rdmPersonalityChangedCb, this);
+ initialized = true;
+ return true;
+}
+
+void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPortNum)
+{
+
+#ifdef WLED_ENABLE_DMX_OUTPUT
+ //TODO add again once dmx output has been merged
+ // if(inputPortNum == dmxOutputPort)
+ // {
+ // DEBUG_PRINTF("DMXInput: Error: Input port == output port");
+ // return;
+ // }
+#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[] = {
+ {(int8_t)txPin, false}, // these are not used as gpio pins, thus isOutput is always false.
+ {(int8_t)rxPin, false},
+ {(int8_t)enPin, false}};
+ const bool pinsAllocated = PinManager::allocateMultiplePins(pins, 3, PinOwner::DMX_INPUT);
+ if (!pinsAllocated) {
+ DEBUG_PRINTF("DMXInput: Error: Failed to allocate pins for DMX_INPUT. Pins already in use:\n");
+ DEBUG_PRINTF("rx in use by: %s\n", pinManager.getPinOwnerText(rxPin).c_str());
+ DEBUG_PRINTF("tx in use by: %s\n", pinManager.getPinOwnerText(txPin).c_str());
+ DEBUG_PRINTF("en in use by: %s\n", pinManager.getPinOwnerText(enPin).c_str());
+ return;
+ }
+
+ this->rxPin = rxPin;
+ this->txPin = txPin;
+ this->enPin = enPin;
+
+ // put dmx receiver into seperate task because it should not be blocked
+ // pin to core 0 because wled is running on core 1
+ xTaskCreatePinnedToCore(dmxReceiverTask, "DMX_RCV_TASK", 10240, this, 2, &task, 0);
+ if (!task) {
+ DEBUG_PRINTF("Error: Failed to create dmx rcv task");
+ }
+ }
+ else {
+ DEBUG_PRINTLN("DMX input disabled due to rxPin, enPin or txPin not set");
+ return;
+ }
+}
+
+void DMXInput::updateInternal()
+{
+ if (!initialized) {
+ return;
+ }
+
+ checkAndUpdateConfig();
+
+ dmx_packet_t packet;
+ unsigned long now = millis();
+ if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK)) {
+ if (!packet.err) {
+ if(!connected) {
+ DEBUG_PRINTLN("DMX Input - connected");
+ }
+ connected = true;
+ identify = isIdentifyOn();
+ if (!packet.is_rdm) {
+ const std::lock_guard lock(dmxDataLock);
+ dmx_read(inputPortNum, dmxdata, packet.size);
+ }
+ }
+ else {
+ connected = false;
+ }
+ }
+ else {
+ if(connected) {
+ DEBUG_PRINTLN("DMX Input - disconnected");
+ }
+ connected = false;
+ }
+}
+
+
+void DMXInput::update()
+{
+ if (identify) {
+ turnOnAllLeds();
+ }
+ else if (connected) {
+ const std::lock_guard lock(dmxDataLock);
+ handleDMXData(1, 512, dmxdata, REALTIME_MODE_DMX, 0);
+ }
+}
+
+void DMXInput::turnOnAllLeds()
+{
+ // TODO not sure if this is the correct way?
+ const uint16_t numPixels = strip.getLengthTotal();
+ for (uint16_t i = 0; i < numPixels; ++i)
+ {
+ strip.setPixelColor(i, 255, 255, 255, 255);
+ }
+ strip.setBrightness(255, true);
+ strip.show();
+}
+
+void DMXInput::disable()
+{
+ if (initialized) {
+ dmx_driver_disable(inputPortNum);
+ }
+}
+void DMXInput::enable()
+{
+ if (initialized) {
+ dmx_driver_enable(inputPortNum);
+ }
+}
+
+bool DMXInput::isIdentifyOn() const
+{
+
+ uint8_t identify = 0;
+ const bool gotIdentify = rdm_get_identify_device(inputPortNum, &identify);
+ // gotIdentify should never be false because it is a default parameter in rdm
+ // but just in case we check for it anyway
+ return bool(identify) && gotIdentify;
+}
+
+void DMXInput::checkAndUpdateConfig()
+{
+
+ /**
+ * The global configuration variables are modified by the web interface.
+ * If they differ from the driver configuration, we have to update the driver
+ * configuration.
+ */
+
+ const uint8_t currentPersonality = dmx_get_current_personality(inputPortNum);
+ if (currentPersonality != DMXMode) {
+ DEBUG_PRINTF("DMX personality has changed from %d to %d\n", currentPersonality, DMXMode);
+ dmx_set_current_personality(inputPortNum, DMXMode);
+ }
+
+ const uint16_t currentAddr = dmx_get_start_address(inputPortNum);
+ if (currentAddr != DMXAddress) {
+ DEBUG_PRINTF("DMX address has changed from %d to %d\n", currentAddr, DMXAddress);
+ dmx_set_start_address(inputPortNum, DMXAddress);
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/wled00/dmx_input.h b/wled00/dmx_input.h
new file mode 100644
index 0000000000..7845778d78
--- /dev/null
+++ b/wled00/dmx_input.h
@@ -0,0 +1,73 @@
+#pragma once
+#include
+#include
+#include
+#include
+
+/*
+ * Support for DMX/RDM input via serial (e.g. max485) on ESP32
+ * ESP32 Library from:
+ * https://github.com/someweisguy/esp_dmx
+ */
+class DMXInput
+{
+public:
+ void init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPortNum);
+ void update();
+
+ /**disable dmx receiver (do this before disabling the cache)*/
+ void disable();
+ void enable();
+
+private:
+ /// @return true if rdm identify is active
+ bool isIdentifyOn() const;
+
+ /**
+ * Checks if the global dmx config has changed and updates the changes in rdm
+ */
+ void checkAndUpdateConfig();
+
+ /// overrides everything and turns on all leds
+ void turnOnAllLeds();
+
+ /// installs the dmx driver
+ /// @return false on fail
+ bool installDriver();
+
+ /// is called by the dmx receive task regularly to receive new dmx data
+ void updateInternal();
+
+ // is invoked whenver the dmx start address is changed via rdm
+ friend void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
+ void *context);
+
+ // is invoked whenever the personality is changed via rdm
+ friend void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
+ void *context);
+
+ /// The internal dmx task.
+ /// This is the main loop of the dmx receiver. It never returns.
+ friend void dmxReceiverTask(void * context);
+
+ uint8_t inputPortNum = 255;
+ uint8_t rxPin = 255;
+ uint8_t txPin = 255;
+ uint8_t enPin = 255;
+
+ /// is written to by the dmx receive task.
+ byte dmxdata[DMX_PACKET_SIZE];
+ /// True once the dmx input has been initialized successfully
+ bool initialized = false; // true once init finished successfully
+ /// True if dmx is currently connected
+ std::atomic connected{false};
+ std::atomic identify{false};
+ /// Timestamp of the last time a dmx frame was received
+ unsigned long lastUpdate = 0;
+
+ /// Taskhandle of the dmx task that is running in the background
+ TaskHandle_t task;
+ /// Guards access to dmxData
+ std::mutex dmxDataLock;
+
+};
diff --git a/wled00/dmx.cpp b/wled00/dmx_output.cpp
similarity index 93%
rename from wled00/dmx.cpp
rename to wled00/dmx_output.cpp
index dbe70f2aac..eace2145e6 100644
--- a/wled00/dmx.cpp
+++ b/wled00/dmx_output.cpp
@@ -1,7 +1,7 @@
#include "wled.h"
/*
- * Support for DMX Output via MAX485.
+ * 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:
@@ -12,7 +12,7 @@
#ifdef WLED_ENABLE_DMX
-void handleDMX()
+void handleDMXOutput()
{
// don't act, when in DMX Proxy mode
if (e131ProxyUniverse != 0) return;
@@ -68,11 +68,14 @@ void handleDMX()
dmx.update(); // update the DMX bus
}
-void initDMX() {
+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
}
+#else
+void initDMXOutput(){}
+void handleDMXOutput() {}
#endif
diff --git a/wled00/e131.cpp b/wled00/e131.cpp
index bc26a0639e..c16ed9332e 100644
--- a/wled00/e131.cpp
+++ b/wled00/e131.cpp
@@ -116,6 +116,11 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
// update status info
realtimeIP = clientIP;
+
+ handleDMXData(uni, dmxChannels, e131_data, mde, previousUniverses);
+}
+
+void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8_t mde, uint8_t previousUniverses) {
byte wChannel = 0;
unsigned totalLen = strip.getLengthTotal();
unsigned availDMXLen = 0;
@@ -130,7 +135,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
}
// DMX data in Art-Net packet starts at index 0, for E1.31 at index 1
- if (protocol == P_ARTNET && dataOffset > 0) {
+ if (mde == REALTIME_MODE_ARTNET && dataOffset > 0) {
dataOffset--;
}
@@ -211,7 +216,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
else
dataOffset = DMXAddress;
// Modify address for Art-Net data
- if (protocol == P_ARTNET && dataOffset > 0)
+ if (mde == REALTIME_MODE_ARTNET && dataOffset > 0)
dataOffset--;
// Skip out of universe addresses
if (dataOffset > dmxChannels - dmxEffectChannels + 1)
@@ -285,7 +290,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
}
} else {
// All subsequent universes start at the first channel.
- dmxOffset = (protocol == P_ARTNET) ? 0 : 1;
+ dmxOffset = (mde == REALTIME_MODE_ARTNET) ? 0 : 1;
const unsigned dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0;
unsigned ledsInFirstUniverse = (((MAX_CHANNELS_PER_UNIVERSE - DMXAddress) + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed;
previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * ledsPerUniverse;
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index 4a57740634..a1e3629141 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -183,12 +183,17 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
-//dmx.cpp
-void initDMX();
-void handleDMX();
+//dmx_output.cpp
+void initDMXOutput();
+void handleDMXOutput();
+
+//dmx_input.cpp
+void initDMXInput();
+void handleDMXInput();
//e131.cpp
void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol);
+void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8_t mde, uint8_t previousUniverses);
void handleArtnetPollReply(IPAddress ipAddress);
void prepareArtnetPollReply(ArtPollReply* reply);
void sendArtnetPollReply(ArtPollReply* reply, IPAddress ipAddress, uint16_t portAddress);
diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp
index 37ebd41ec0..6f16523010 100644
--- a/wled00/pin_manager.cpp
+++ b/wled00/pin_manager.cpp
@@ -141,7 +141,9 @@ bool PinManager::allocateMultiplePins(const managed_pin_type * mptArray, byte ar
bool PinManager::allocatePin(byte gpio, bool output, PinOwner tag)
{
// HW I2C & SPI pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
- if (!isPinOk(gpio, output) || (gpio >= WLED_NUM_PINS) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI) {
+ // DMX_INPUT pins have to be allocated using allocateMultiplePins variant since there is always RX/TX/EN triple
+ if (!isPinOk(gpio, output) || (gpio >= WLED_NUM_PINS) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI
+ || tag==PinOwner::DMX_INPUT) {
#ifdef WLED_DEBUG
if (gpio < 255) { // 255 (-1) is the "not defined GPIO"
if (!isPinOk(gpio, output)) {
diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h
index c8fb165ced..b285b6ee5d 100644
--- a/wled00/pin_manager.h
+++ b/wled00/pin_manager.h
@@ -35,15 +35,16 @@ enum struct PinOwner : uint8_t {
Ethernet = 0x81,
BusDigital = 0x82,
BusOnOff = 0x83,
- BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
- Button = 0x85, // 'Butn' == button from configuration
- IR = 0x86, // 'IR' == IR receiver pin from configuration
- 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
- 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)
+ BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
+ Button = 0x85, // 'Butn' == button from configuration
+ IR = 0x86, // 'IR' == IR receiver pin from configuration
+ 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
+ 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
// 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 160eb48f07..08a0180ad5 100644
--- a/wled00/set.cpp
+++ b/wled00/set.cpp
@@ -420,6 +420,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
t = request->arg(F("WO")).toInt();
if (t >= -255 && t <= 255) arlsOffset = t;
+#ifdef WLED_ENABLE_DMX_INPUT
+ dmxInputTransmitPin = request->arg(F("IDMT")).toInt();
+ dmxInputReceivePin = request->arg(F("IDMR")).toInt();
+ dmxInputEnablePin = request->arg(F("IDME")).toInt();
+ dmxInputPort = request->arg(F("IDMP")).toInt();
+ if(dmxInputPort <= 0 || dmxInputPort > 2) dmxInputPort = 2;
+#endif
+
#ifndef WLED_DISABLE_ALEXA
alexaEnabled = request->hasArg(F("AL"));
strlcpy(alexaInvocationName, request->arg(F("AI")).c_str(), 33);
diff --git a/wled00/wled.cpp b/wled00/wled.cpp
index 3430d06a33..da1c33044c 100644
--- a/wled00/wled.cpp
+++ b/wled00/wled.cpp
@@ -65,7 +65,10 @@ void WLED::loop()
handleNotifications();
handleTransitions();
#ifdef WLED_ENABLE_DMX
- handleDMX();
+ handleDMXOutput();
+ #endif
+ #ifdef WLED_ENABLE_DMX_INPUT
+ dmxInput.update();
#endif
#ifdef WLED_DEBUG
@@ -527,7 +530,10 @@ void WLED::setup()
}
#endif
#ifdef WLED_ENABLE_DMX
- initDMX();
+ initDMXOutput();
+#endif
+#ifdef WLED_ENABLE_DMX_INPUT
+ dmxInput.init(dmxInputReceivePin, dmxInputTransmitPin, dmxInputEnablePin, dmxInputPort);
#endif
#ifdef WLED_ENABLE_ADALIGHT
@@ -778,7 +784,6 @@ int8_t WLED::findWiFi(bool doScan) {
void WLED::initConnection()
{
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
-
#ifdef WLED_ENABLE_WEBSOCKETS
ws.onEvent(wsEvent);
#endif
@@ -807,6 +812,7 @@ void WLED::initConnection()
if (!WLED_WIFI_CONFIGURED) {
DEBUG_PRINTLN(F("No connection configured."));
if (!apActive) initAP(); // instantly go to ap mode
+ return;
} else if (!apActive) {
if (apBehavior == AP_BEHAVIOR_ALWAYS) {
DEBUG_PRINTLN(F("Access point ALWAYS enabled."));
diff --git a/wled00/wled.h b/wled00/wled.h
index 533b120632..b7f1ae710b 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -144,6 +144,10 @@
#endif
#endif
+#ifdef WLED_ENABLE_DMX_INPUT
+ #include "dmx_input.h"
+#endif
+
#include "src/dependencies/e131/ESPAsyncE131.h"
#ifndef WLED_DISABLE_MQTT
#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h"
@@ -459,7 +463,15 @@ WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to f
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
#endif
-WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consecutive universes)
+#ifdef WLED_ENABLE_DMX_INPUT
+ WLED_GLOBAL int dmxInputTransmitPin _INIT(0);
+ WLED_GLOBAL int dmxInputReceivePin _INIT(0);
+ WLED_GLOBAL int dmxInputEnablePin _INIT(0);
+ WLED_GLOBAL int dmxInputPort _INIT(2);
+ WLED_GLOBAL DMXInput dmxInput;
+#endif
+
+WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes)
WLED_GLOBAL uint16_t e131Port _INIT(5568); // DMX in port. E1.31 default is 5568, Art-Net is 6454
WLED_GLOBAL byte e131Priority _INIT(0); // E1.31 port priority (if != 0 priority handling is active)
WLED_GLOBAL E131Priority highPriority _INIT(3); // E1.31 highest priority tracking, init = timeout in seconds
diff --git a/wled00/xml.cpp b/wled00/xml.cpp
index 2a19cdfabc..0ed32c04b9 100644
--- a/wled00/xml.cpp
+++ b/wled00/xml.cpp
@@ -436,6 +436,18 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormCheckbox(settingsScript,PSTR("ES"),e131SkipOutOfSequence);
printSetFormCheckbox(settingsScript,PSTR("EM"),e131Multicast);
printSetFormValue(settingsScript,PSTR("EU"),e131Universe);
+#ifdef WLED_ENABLE_DMX
+ settingsScript.print(SET_F("hideNoDMX();")); // hide "not compiled in" message
+#endif
+#ifndef WLED_ENABLE_DMX_INPUT
+ settingsScript.print(SET_F("hideDMXInput();")); // hide "dmx input" settings
+#else
+ settingsScript.print(SET_F("hideNoDMXInput();")); //hide "not compiled in" message
+ printSetFormValue(settingsScript,SET_F("IDMT"),dmxInputTransmitPin);
+ printSetFormValue(settingsScript,SET_F("IDMR"),dmxInputReceivePin);
+ printSetFormValue(settingsScript,SET_F("IDME"),dmxInputEnablePin);
+ printSetFormValue(settingsScript,SET_F("IDMP"),dmxInputPort);
+#endif
printSetFormValue(settingsScript,PSTR("DA"),DMXAddress);
printSetFormValue(settingsScript,PSTR("XX"),DMXSegmentSpacing);
printSetFormValue(settingsScript,PSTR("PY"),e131Priority);