DMX Output - Runtime Config & replace SparkFunDMX with esp_dmx#5433
DMX Output - Runtime Config & replace SparkFunDMX with esp_dmx#5433
Conversation
…f requested port is 2 on device with only 1
…utput-esp_dmx # Conflicts: # wled00/data/settings_sync.htm # wled00/dmx_output.cpp # wled00/xml.cpp
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip Migrating from UI to YAML configuration.Use the |
| 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 |
| -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 |
There was a problem hiding this comment.
Todo: remove before merge
|
Replaces corrupt #5287 |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
wled00/src/dependencies/dmx/ESPDMX.cpp (1)
54-56:⚠️ Potential issue | 🟠 MajorSend the start code plus all configured slots.
dmxDataStore[0]is the DMX start code, so writing onlychannelSizebytes drops the highest channel every frame. With the current default of 32, slot 32 never gets transmitted.💡 Suggested change
- Serial1.write(dmxDataStore, channelSize); + Serial1.write(dmxDataStore, channelSize + 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wled00/src/dependencies/dmx/ESPDMX.cpp` around lines 54 - 56, The code is sending only channelSize bytes from dmxDataStore which omits dmxDataStore[0] (the DMX start code) plus the last slot; update the send call so Serial1.write transmits the start code plus all configured slots (send dmxDataStore with length channelSize + 1) — locate the Serial1.write(dmxDataStore, channelSize) call in ESPDMX.cpp and change it to send channelSize + 1 bytes so all slots (including the highest) are transmitted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@wled00/dmx_input.cpp`:
- Around line 151-158: The code sets a fallback this->inputPortNum = 1 then
returns, which leaves previously allocated pins reserved and prevents DMX input
from starting; fix by either (A) removing the early return so execution
continues after setting inputPortNum to 1 (allowing DMX input to start on port
1) or (B) if you must abort, release the allocated pins/resources before
returning (call the cleanup routine, e.g., releasePins()/deinit()/freePins() or
the destructor-equivalent) so there is no resource leak; locate the logic around
inputPortNum in dmx_input.cpp (the init/constructor where inputPortNum is
validated) and apply one of these two fixes to ensure pins are not left
reserved.
In `@wled00/dmx_output.cpp`:
- Around line 1-2: dmx_output.cpp currently includes dmx_output.h before the
WLED_ENABLE_DMX guard, forcing esp_dmx.h dependencies even when DMX is disabled;
move the `#include` "dmx_output.h" (and any includes that pull in esp_dmx.h)
inside the existing `#ifdef` WLED_ENABLE_DMX / `#endif` block so that dmx_output.h
and esp_dmx.h are only included when WLED_ENABLE_DMX is defined, preserving the
current feature guard around the DMX-related functions (references:
dmx_output.h, WLED_ENABLE_DMX, esp_dmx.h).
- Around line 69-93: initDMXOutput currently allocates a pin via
PinManager::allocatePin but DMXOutput::init returns void and on failures
(dmx_driver_install, dmx_set_pin) the allocation is never undone; also
dmx_driver_install and dmx_set_pin return esp_err_t but are stored in bools
losing error info. Change DMXOutput::init to return a bool (or esp_err_t) to
signal success/failure, modify initDMXOutput to check that result and call
PinManager::freePin(outputPin) on failure, and in DMXOutput::init assign the
results of dmx_driver_install and dmx_set_pin to esp_err_t variables and compare
against ESP_OK (use dmx_driver_install, dmx_set_pin, dmx.init,
PinManager::allocatePin, PinManager::freePin, PinManager::getPinOwner to locate
and fix the spots).
In `@wled00/dmx_output.h`:
- Around line 20-24: Change the write() API to accept a 16-bit DMX channel index
(replace uint8_t with uint16_t in the write() declaration) so addresses >255 do
not wrap; in the implementation (write() in dmx_output.cpp) treat the incoming
channel as a full DMX address used by handleDMXOutput()/DMXStart, validate it
against DMX_PACKET_SIZE and DMXStart, and prevent writing to index 0 (the DMX
start code) by either rejecting channel==0 or offsetting checks so index 0 is
never overwritten.
In `@wled00/src/dependencies/dmx/ESPDMX.cpp`:
- Around line 21-23: The ESP8266 DMX backend currently hardcodes channelSize to
defaultMax inside DMXESPSerial::init (and clamps addresses later), which forces
a 32-channel ceiling and aliases higher fixture addresses; change
DMXESPSerial::init to accept and set the actual highest-used slot (or a
maxChannels parameter) instead of always assigning defaultMax to channelSize,
and update any address-clamping logic that references channelSize (the clamp at
lines that reduce higher addresses to the ceiling) to use this passed-in max so
frames are sized to the real highest-used slot rather than being truncated to
32.
- Around line 25-27: The code treats sendPin as if it were the DMX TX, but on
ESP8266 Serial1 TX is always GPIO2 and sendPin is only the RS485 direction pin;
update ESPDMX.cpp to stop treating sendPin as the UART TX and instead
reserve/register GPIO2 with the PinManager when using Serial1 on ESP8266 (while
keeping sendPin configured as the RS485 direction pin via pinMode/digitalWrite).
Specifically, adjust the initialization around Serial1.begin, pinMode(sendPin,
OUTPUT) and dmxStarted so that PinManager is used to mark GPIO2 as the serial TX
(not sendPin) and ensure sendPin remains only for direction control to avoid
GPIO2 allocation conflicts.
---
Outside diff comments:
In `@wled00/src/dependencies/dmx/ESPDMX.cpp`:
- Around line 54-56: The code is sending only channelSize bytes from
dmxDataStore which omits dmxDataStore[0] (the DMX start code) plus the last
slot; update the send call so Serial1.write transmits the start code plus all
configured slots (send dmxDataStore with length channelSize + 1) — locate the
Serial1.write(dmxDataStore, channelSize) call in ESPDMX.cpp and change it to
send channelSize + 1 bytes so all slots (including the highest) are transmitted.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6e5b0125-2ad6-4e6e-8d7c-4748bb3b6968
📒 Files selected for processing (17)
platformio.iniwled00/cfg.cppwled00/const.hwled00/data/settings_sync.htmwled00/dmx_input.cppwled00/dmx_output.cppwled00/dmx_output.hwled00/fcn_declare.hwled00/pin_manager.hwled00/set.cppwled00/src/dependencies/dmx/ESPDMX.cppwled00/src/dependencies/dmx/ESPDMX.hwled00/src/dependencies/dmx/SparkFunDMX.cppwled00/src/dependencies/dmx/SparkFunDMX.hwled00/wled.cppwled00/wled.hwled00/xml.cpp
💤 Files with no reviewable changes (2)
- wled00/src/dependencies/dmx/SparkFunDMX.cpp
- wled00/src/dependencies/dmx/SparkFunDMX.h
| 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; | ||
| } |
There was a problem hiding this comment.
Invalid-port fallback exits too early and leaves allocated pins reserved.
At Line 157, init returns after setting fallback port 1, so DMX input never starts on this path, and the previously allocated pins are not released.
Suggested fix
- 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;
- }
+ 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;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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; | |
| } | |
| 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; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wled00/dmx_input.cpp` around lines 151 - 158, The code sets a fallback
this->inputPortNum = 1 then returns, which leaves previously allocated pins
reserved and prevents DMX input from starting; fix by either (A) removing the
early return so execution continues after setting inputPortNum to 1 (allowing
DMX input to start on port 1) or (B) if you must abort, release the allocated
pins/resources before returning (call the cleanup routine, e.g.,
releasePins()/deinit()/freePins() or the destructor-equivalent) so there is no
resource leak; locate the logic around inputPortNum in dmx_input.cpp (the
init/constructor where inputPortNum is validated) and apply one of these two
fixes to ensure pins are not left reserved.
| #include "wled.h" | ||
|
|
||
| #include "dmx_output.h" |
There was a problem hiding this comment.
Don't pull in dmx_output.h before the feature guard.
The #ifdef WLED_ENABLE_DMX doesn't start until Line 11, so Line 2 makes every non-ESP8266 build require esp_dmx.h even when DMX output is disabled.
💡 Suggested change
`#include` "wled.h"
-#include "dmx_output.h"
+#ifdef WLED_ENABLE_DMX
+#include "dmx_output.h"
+#endif📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #include "wled.h" | |
| #include "dmx_output.h" | |
| `#include` "wled.h" | |
| `#ifdef` WLED_ENABLE_DMX | |
| `#include` "dmx_output.h" | |
| `#endif` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wled00/dmx_output.cpp` around lines 1 - 2, dmx_output.cpp currently includes
dmx_output.h before the WLED_ENABLE_DMX guard, forcing esp_dmx.h dependencies
even when DMX is disabled; move the `#include` "dmx_output.h" (and any includes
that pull in esp_dmx.h) inside the existing `#ifdef` WLED_ENABLE_DMX / `#endif`
block so that dmx_output.h and esp_dmx.h are only included when WLED_ENABLE_DMX
is defined, preserving the current feature guard around the DMX-related
functions (references: dmx_output.h, WLED_ENABLE_DMX, esp_dmx.h).
| void initDMXOutput(int outputPin) { | ||
| if (outputPin < 1) return; | ||
| 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; | ||
| 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; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the DMX output related files
find . -name "*dmx*" -type f | head -20Repository: wled/WLED
Length of output: 198
🏁 Script executed:
# Look at the full dmx_output.cpp file
wc -l wled00/dmx_output.cppRepository: wled/WLED
Length of output: 78
🏁 Script executed:
# Check if there's a dmx_output.h header file
find . -name "dmx_output.h" -type fRepository: wled/WLED
Length of output: 74
🏁 Script executed:
cat -n wled00/dmx_output.hRepository: wled/WLED
Length of output: 1031
🏁 Script executed:
cat -n wled00/dmx_output.cppRepository: wled/WLED
Length of output: 4164
🏁 Script executed:
# Search for esp_dmx library references or documentation in the repo
rg -i "esp_dmx" --type h --type cpp | head -20Repository: wled/WLED
Length of output: 320
🏁 Script executed:
# Check if esp_dmx is included as a dependency
find . -name "esp_dmx*" -type f | head -20Repository: wled/WLED
Length of output: 35
🏁 Script executed:
# Search for dmx_driver_install or dmx_set_pin usage/definition
rg "dmx_driver_install|dmx_set_pin" --type h --type cppRepository: wled/WLED
Length of output: 414
🌐 Web query:
someweisguy esp_dmx API dmx_driver_install dmx_set_pin return type
💡 Result:
esp_dmx is an open-source ESP32 library by someweisguy (Mitch Weisbrod) for ANSI-ESTA E1.11 DMX-512A and E1.20 RDM over UART ports. Include "esp_dmx.h". dmx_driver_install(dmx_port_t dmx_num, const dmx_config_t *config, const dmx_personality_t *personalities, int personality_count) installs/configures the DMX driver for a UART port. No explicit return type shown in examples or code snippets; called without checking return value, suggesting void. dmx_set_pin(dmx_port_t dmx_num, int tx_pin, int rx_pin, int rts_pin) configures GPIO pins for TX/RX/RTS. Similarly, no explicit return type; used without return check, suggesting void. Usage example from README: const dmx_port_t dmx_num = DMX_NUM_1; dmx_config_t config = DMX_CONFIG_DEFAULT; dmx_personality_t personalities[] = {{1, "Default"}}; int personality_count = 1; dmx_driver_install(dmx_num, &config, personalities, personality_count); dmx_set_pin(dmx_num, 17, 16, -1);
Citations:
- 1: https://github.com/someweisguy/esp_dmx
- 2: https://github.com/someweisguy/esp_dmx/blob/release/v4.1/README.md
- 3: https://github.com/someweisguy/esp_dmx/blob/release%2Fv4.1/src%2Fdmx%2Fhal%2Fuart.c
- 4: Could not find any RDM capable devices someweisguy/esp_dmx#151
🌐 Web query:
esp_dmx dmx_driver_install return value esp_err_t
💡 Result:
The function dmx_driver_install from the esp_dmx library (https://github.com/someweisguy/esp_dmx) returns esp_err_t, Espressif's standard error type used across ESP-IDF and Arduino-ESP32 APIs. This is evidenced by its usage in code examples where it is assigned to esp_err_t variables (e.g., esp_err_t dmxError = dmx_driver_install(...);) and consistent with ESP hardware driver patterns like uart_driver_install. Official documentation does not explicitly state the return type in READMEs, but source code patterns and error handling confirm it returns ESP_OK on success or an error code (e.g., ESP_ERR_INVALID_ARG) on failure. The library is compatible with ESP-IDF v4.4.1+ and Arduino-ESP32 v2.0.3+, supporting DMX-512A and RDM over ESP32 UARTs.
Citations:
- 1: https://github.com/someweisguy/esp_dmx/blob/release/v4.1/README.md
- 2: https://gist.github.com/cnf/36ff0779ba7639840cc90047da6f78ca
- 3: https://github.com/someweisguy/esp_dmx
- 4: Could not find any RDM capable devices someweisguy/esp_dmx#151
- 5: Problem with installing DMX on UART2 someweisguy/esp_dmx#150
- 6: https://github.com/someweisguy/esp_dmx/blob/release%2Fv4.1/src%2Fdmx%2Fhal%2Fuart.c
- 7: fixed periph.module bug someweisguy/esp_dmx#187
- 8: Random read input someweisguy/esp_dmx#10
- 9: https://github.com/mtongnz/espDMX
Fix pin allocation leak and correct error handling return types.
Line 71 allocates the pin but DMXOutput::init() returns void with no mechanism to signal failure to the caller. If dmx_driver_install() or dmx_set_pin() fail, the pin allocation is never unwound, preventing retries on the same pin without a reboot.
Additionally, lines 84 and 89 incorrectly assign esp_err_t return values to bool variables. The esp_dmx library functions return esp_err_t (success/error codes), not bool. Assigning to bool loses the error information and creates invalid checks—the code will proceed regardless of actual failure. Both functions should be assigned to esp_err_t variables and checked against ESP_OK.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wled00/dmx_output.cpp` around lines 69 - 93, initDMXOutput currently
allocates a pin via PinManager::allocatePin but DMXOutput::init returns void and
on failures (dmx_driver_install, dmx_set_pin) the allocation is never undone;
also dmx_driver_install and dmx_set_pin return esp_err_t but are stored in bools
losing error info. Change DMXOutput::init to return a bool (or esp_err_t) to
signal success/failure, modify initDMXOutput to check that result and call
PinManager::freePin(outputPin) on failure, and in DMXOutput::init assign the
results of dmx_driver_install and dmx_set_pin to esp_err_t variables and compare
against ESP_OK (use dmx_driver_install, dmx_set_pin, dmx.init,
PinManager::allocatePin, PinManager::freePin, PinManager::getPinOwner to locate
and fix the spots).
| void init(uint8_t outputPin); | ||
| void write(uint8_t channel, uint8_t value); | ||
| void update(); | ||
| private: | ||
| byte dmxdata[DMX_PACKET_SIZE]; |
There was a problem hiding this comment.
Use a 16-bit channel index for DMX writes.
DMXStart is uint16_t in wled00/wled.h, and handleDMXOutput() in wled00/dmx_output.cpp builds full DMX addresses from it. Exposing write() as uint8_t guarantees wraparound above channel 255, and channel 0 can currently overwrite the DMX start code.
💡 Suggested change
- void write(uint8_t channel, uint8_t value);
+ void write(uint16_t channel, uint8_t value);And in wled00/dmx_output.cpp:
-void DMXOutput::write(uint8_t channel, uint8_t value) {
- dmxdata[channel] = value;
+void DMXOutput::write(uint16_t channel, uint8_t value) {
+ if (channel == 0 || channel >= DMX_PACKET_SIZE) return;
+ dmxdata[channel] = value;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void init(uint8_t outputPin); | |
| void write(uint8_t channel, uint8_t value); | |
| void update(); | |
| private: | |
| byte dmxdata[DMX_PACKET_SIZE]; | |
| void init(uint8_t outputPin); | |
| void write(uint16_t channel, uint8_t value); | |
| void update(); | |
| private: | |
| byte dmxdata[DMX_PACKET_SIZE]; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wled00/dmx_output.h` around lines 20 - 24, Change the write() API to accept a
16-bit DMX channel index (replace uint8_t with uint16_t in the write()
declaration) so addresses >255 do not wrap; in the implementation (write() in
dmx_output.cpp) treat the incoming channel as a full DMX address used by
handleDMXOutput()/DMXStart, validate it against DMX_PACKET_SIZE and DMXStart,
and prevent writing to index 0 (the DMX start code) by either rejecting
channel==0 or offsetting checks so index 0 is never overwritten.
| #ifdef WLED_ENABLE_DMX | ||
| dmxOutputPin = request->arg(F("IDMO")).toInt(); | ||
| #endif |
There was a problem hiding this comment.
Add server-side bounds validation for IDMO.
Line 477 trusts client input directly. Please clamp/validate the pin range before persisting to avoid invalid DMX output pin state from crafted requests.
Suggested fix
`#ifdef` WLED_ENABLE_DMX
- dmxOutputPin = request->arg(F("IDMO")).toInt();
+ t = request->arg(F("IDMO")).toInt();
+ if (t >= -1 && t < WLED_NUM_PINS) {
+ dmxOutputPin = t;
+ } else {
+ dmxOutputPin = -1;
+ }
`#endif`| void DMXESPSerial::init(int sendPin) { | ||
| this->sendPin = sendPin; | ||
| channelSize = defaultMax; |
There was a problem hiding this comment.
This caps the ESP8266 backend at 32 DMX channels.
Line 23 hardcodes channelSize to defaultMax, and Lines 32-33 clamp every higher address down to that ceiling. Any fixture mapping beyond slot 32 will be aliased onto channel 32 instead of reaching its real DMX address.
💡 Suggested change
- channelSize = defaultMax;
+ channelSize = dmxMaxChannel;If you want shorter frames, plumb the actual highest-used slot into init() instead of falling back to 32.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wled00/src/dependencies/dmx/ESPDMX.cpp` around lines 21 - 23, The ESP8266 DMX
backend currently hardcodes channelSize to defaultMax inside DMXESPSerial::init
(and clamps addresses later), which forces a 32-channel ceiling and aliases
higher fixture addresses; change DMXESPSerial::init to accept and set the actual
highest-used slot (or a maxChannels parameter) instead of always assigning
defaultMax to channelSize, and update any address-clamping logic that references
channelSize (the clamp at lines that reduce higher addresses to the ceiling) to
use this passed-in max so frames are sized to the real highest-used slot rather
than being truncated to 32.
| Serial1.begin(DMXSPEED); | ||
| pinMode(sendPin, OUTPUT); | ||
| dmxStarted = true; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n wled00/src/dependencies/dmx/ESPDMX.cpp | head -60Repository: wled/WLED
Length of output: 1894
🏁 Script executed:
cat -n wled00/src/dependencies/dmx/ESPDMX.cppRepository: wled/WLED
Length of output: 1965
🏁 Script executed:
fd ESPDMX.hRepository: wled/WLED
Length of output: 89
🏁 Script executed:
cat -n wled00/src/dependencies/dmx/ESPDMX.hRepository: wled/WLED
Length of output: 1434
🏁 Script executed:
rg "DMXESPSerial" -t cpp -t h -A 3 -B 3Repository: wled/WLED
Length of output: 2234
🏁 Script executed:
rg "dmx|DMX" -t cpp wled00/pin_manager.cpp -i -A 2 -B 2Repository: wled/WLED
Length of output: 914
🏁 Script executed:
rg "dmx\.init|DMX.*init" -t cpp -A 2 -B 2Repository: wled/WLED
Length of output: 1111
🏁 Script executed:
rg "allocatePin|PinOwner::DMX" -t cpp -A 2 -B 2Repository: wled/WLED
Length of output: 32042
🏁 Script executed:
rg "Serial1|SERIAL.*TX|GPIO.*2" -t cpp wled00/ -i -B 2 -A 2 | head -50Repository: wled/WLED
Length of output: 3077
🏁 Script executed:
rg "GPIO.?2|TX.*pin|Serial1.*pin" -t h -t cpp -i -B 2 -A 2 | head -80Repository: wled/WLED
Length of output: 5090
🏁 Script executed:
fd "pins.h|config.h|defaults.h" | head -20Repository: wled/WLED
Length of output: 141
🏁 Script executed:
rg "GPIO.?2|Serial1" wled00/ -t cpp -t h | grep -i "esp8266\|serial\|pin"Repository: wled/WLED
Length of output: 1809
🏁 Script executed:
cat -n wled00/src/dependencies/dmx/ESPDMX.cpp | sed -n '44,60p'Repository: wled/WLED
Length of output: 534
🏁 Script executed:
cat -n wled00/dmx_output.cppRepository: wled/WLED
Length of output: 4164
🏁 Script executed:
rg "Serial1|UART.*TX|GPIO.*2" wled00/ -t cpp -i -B 1 -A 1 | head -100Repository: wled/WLED
Length of output: 7314
🏁 Script executed:
rg "ESP8266.*Serial|Serial1.*pin" -t cpp -iRepository: wled/WLED
Length of output: 152
🏁 Script executed:
web_search
ESP8266 Serial1 TX pin hardware fixed GPIORepository: wled/WLED
Length of output: 149
🌐 Web query:
ESP8266 Serial1 TX pin GPIO fixed hardware
💡 Result:
The ESP8266 Serial1 (UART1) TX pin is fixed to GPIO2 by hardware. This is a dedicated hardware connection where UART1 TXD is hard-wired to GPIO2 pin. UART1 RX is not practically usable as its RXD pin is internally connected to the flash chip. In Arduino ESP8266 core, Serial1.begin uses this fixed GPIO2 for TX. Unlike UART0 (Serial), which defaults to GPIO1 (TX) and GPIO3 (RX) but can be swapped to other pins via software (e.g., Serial.swap to GPIO15 TX / GPIO13 RX), UART1 TX remains on GPIO2 and cannot be remapped to other GPIOs due to the fixed hardware mapping. To use Serial1 TX: Serial1.begin(115200); // TX on GPIO2 automatically Sources confirm this across official and technical documentation: - Arduino ESP8266 core reference: "Serial1 uses UART1, TX pin is GPIO2." - ESP8266EX datasheet: GPIO2 listed with UART TX function during flash programming, consistent with UART1 TX. - ESP8266 community wiki: Pin functions map SPECIAL to TX1 on pin 2 (GPIO2). - Espressif FAQ: UART1 TXD for logs/printing, RX occupied by flash. GPIO2 can be used as general GPIO when not using Serial1, but activating Serial1 configures it as TX.
Citations:
- 1: https://arduino-esp8266.readthedocs.io/en/latest/reference.html?highlight=serial1
- 2: https://arduino.esp8266.com/Arduino/versions/2.1.0/doc/reference.html
- 3: https://espressif.com/sites/default/files/documentation/0a-esp8266ex_datasheet_en.pdf
- 4: https://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations
- 5: http://docs.gizwits.com/en-us/module_source/esp8266/esp8266_datasheet.html
- 6: https://github.com/esp8266/esp8266-wiki/wiki/Pin-definition
sendPin is misleading on the ESP8266 Serial1 path.
On ESP8266, Serial1 TX is hard-wired to GPIO2 and cannot be remapped. The sendPin parameter at line 26 is allocated in PinManager but is only used for RS485 direction control (digitalWrite), not actual DMX transmission. The actual TX pin (GPIO2) is never registered with PinManager, creating a mismatch where a runtime-configurable pin is reserved while the true TX pin remains untracked and could conflict with other GPIO2 allocations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@wled00/src/dependencies/dmx/ESPDMX.cpp` around lines 25 - 27, The code treats
sendPin as if it were the DMX TX, but on ESP8266 Serial1 TX is always GPIO2 and
sendPin is only the RS485 direction pin; update ESPDMX.cpp to stop treating
sendPin as the UART TX and instead reserve/register GPIO2 with the PinManager
when using Serial1 on ESP8266 (while keeping sendPin configured as the RS485
direction pin via pinMode/digitalWrite). Specifically, adjust the initialization
around Serial1.begin, pinMode(sendPin, OUTPUT) and dmxStarted so that PinManager
is used to mark GPIO2 as the serial TX (not sendPin) and ensure sendPin remains
only for direction control to avoid GPIO2 allocation conflicts.
Allow the DMX Output pin to be set at runtime, not need the user to hack around in the source code
Also swap the DMX Output to the same driver as DMX Input, leaving only 8266 to use ESPDMX
Summary by CodeRabbit