Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: remove after testing, unless we can safely enable by default now it can be enabled at runtime?


build_unflags =

Expand Down
6 changes: 6 additions & 0 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")]);
Expand Down Expand Up @@ -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
Comment on lines +1124 to +1126
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Ensure JSON key matches deserialization.

This serialization block writes dmxOutputPin to the key "dmxOutputPin", but line 601 reads from "outputPin". See the comment on lines 600-602 for details.

🤖 Prompt for AI Agents
In @wled00/cfg.cpp around lines 1124 - 1126, The JSON serialization writes the
DMX pin under "dmxOutputPin" but deserialization expects "outputPin", causing a
mismatch; change the key used when assigning into if_live_dmx (replace
F("dmxOutputPin") with F("outputPin")) in the WLED_ENABLE_DMX serialization
block so the key matches the deserializer that reads "outputPin" (or
alternatively update the deserializer to "dmxOutputPin" if you prefer that name)
and verify the if_live_dmx / dmxOutputPin mapping is consistent.

#ifdef WLED_ENABLE_DMX_INPUT
if_live_dmx[F("inputRxPin")] = dmxInputTransmitPin;
if_live_dmx[F("inputTxPin")] = dmxInputReceivePin;
Expand Down
10 changes: 9 additions & 1 deletion wled00/data/settings_sync.htm
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
</script>
</head>
<body>
Expand Down Expand Up @@ -166,7 +170,11 @@ <h4>Wired DMX Input Pins</h4>
DMX TX: <input name="IDMT" type="number" min="-1" max="99">DI<br/>
DMX Enable: <input name="IDME" type="number" min="-1" max="99">RE+DE<br/>
DMX Port: <input name="IDMP" type="number" min="1" max="2"><br/>
</div>
</div>
<div id="dmxOutput" style="display: none">
<h4>Wired DMX Output Pin</h4>
DMX TX: <input name="IDMO" type="number" min="-1" max="99"><br/>
</div>
<div id="dmxInputOff">
<br><em style="color:darkorange">This firmware build does not include DMX Input support. <br></em>
</div>
Expand Down
40 changes: 29 additions & 11 deletions wled00/dmx_output.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Comment on lines +73 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Format specifier mismatch: getPinOwner() returns an enum, not a string.

PinManager::getPinOwner() returns a PinOwner enum value. Using %s format specifier will cause undefined behavior or garbage output.

🐛 Proposed fix
   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));
+    DEBUG_PRINTF("DMXOutput: Error: Failed to allocate pin %d for DMX_OUTPUT. Pin already in use (owner: %d)\n", outputPin, (int)PinManager::getPinOwner(outputPin));
     return;
   }
🤖 Prompt for AI Agents
In @wled00/dmx_output.cpp around lines 73 - 75, The DEBUG_PRINTF call is using
"%s" but PinManager::getPinOwner(outputPin) returns a PinOwner enum; change the
code to print the enum correctly by either converting the enum to a string (add
or call a helper like PinOwnerToString/PinManager::getPinOwnerName that returns
const char* and pass that to DEBUG_PRINTF) or print its integer value by using
the correct format specifier (e.g., replace "%s" with "%d" and cast the enum to
int). Update the DEBUG_PRINTF invocation that references PinManager::getPinOwner
so the format specifier matches the provided value.

}
dmx.init(outputPin); // set output pin and initialize DMX output
}
Comment on lines +69 to +78
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the full dmx_output.cpp file to understand context
wc -l wled00/dmx_output.cpp

Repository: wled/WLED

Length of output: 77


🏁 Script executed:

# Read the entire dmx_output.cpp file
cat -n wled00/dmx_output.cpp

Repository: wled/WLED

Length of output: 3792


🏁 Script executed:

# Search for GPIO 0 usage in the codebase to see if it's considered valid
rg "GPIO 0|pin.*0|outputPin.*0" wled00/ -i -A 2 -B 2 | head -100

Repository: wled/WLED

Length of output: 5727


🏁 Script executed:

# Search for similar pin validation patterns with < 1 or < 0
rg "allocatePin|outputPin\s*<|pin\s*<\s*[01]" wled00/ -i -A 3 -B 1

Repository: wled/WLED

Length of output: 14357


🏁 Script executed:

# Check for other pin allocation error handling patterns
rg "allocatePin.*false|!pinAllocated" wled00/ -A 3 -B 1

Repository: wled/WLED

Length of output: 2285


🏁 Script executed:

# Search for dmx module initialization and pin tracking
rg "dmx\." wled00/dmx_output.cpp -A 1 -B 1

Repository: wled/WLED

Length of output: 1104


🏁 Script executed:

# Search for dmxOutputPin or similar variable
rg "dmx.*[Pp]in|dmxOutput" wled00/ -i

Repository: wled/WLED

Length of output: 4123


🏁 Script executed:

# Check if there's a global dmx object and how it tracks output pin
rg "class DMXOutput|struct DMXOutput|DMXOutput\s+dmx" wled00/ -B 2 -A 5

Repository: wled/WLED

Length of output: 921


🏁 Script executed:

# Look for the dmx object declaration and init function
grep -r "dmx\.init\|DMXOutput dmx" wled00/ --include="*.h" --include="*.cpp"

Repository: wled/WLED

Length of output: 191


Change pin validation to allow GPIO 0, which is valid in WLED.

The validation outputPin < 1 on line 70 incorrectly rejects GPIO 0. GPIO 0 is a valid and usable pin in WLED (see BTNPIN definition in wled.h and button allocation patterns in set.cpp), and other pin allocation logic uses >= 0. Change this to if (outputPin < 0) return; to only exclude disabled (-1) and negative values.

Additionally, pin allocation failures only log via DEBUG_PRINTF without updating the dmxOutputPin state. For consistency with other modules (buttons, relay, IR), consider setting dmxOutputPin = -1; before returning on allocation failure.

The error message could also be simplified:

-  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));
+  DEBUG_PRINTF("DMXOutput: Error: Failed to allocate pin %d for DMX_OUTPUT. Already in use by: %s\n", 
+               outputPin, PinManager::getPinOwner(outputPin));


#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);
}
Comment on lines +81 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find esp_dmx related files and configuration
find . -type f -name "*.h" -o -name "*.cpp" | xargs grep -l "esp_dmx\|dmx_driver_install" | head -20

Repository: wled/WLED

Length of output: 142


🏁 Script executed:

# Look for library dependency declarations (platformio.ini, package.json, cmake files, etc.)
git ls-files | grep -E "(platformio|package|cmake|requirements)" | head -20

Repository: wled/WLED

Length of output: 792


🏁 Script executed:

# Check the full DMXOutput file for context and error handling patterns
wc -l wled00/dmx_output.cpp

Repository: wled/WLED

Length of output: 77


🏁 Script executed:

# Search for error handling patterns in similar driver initialization
rg "dmx_driver_install|dmx_set_pin" -A 3 -B 3

Repository: wled/WLED

Length of output: 1432


🏁 Script executed:

# Check platformio.ini for esp_dmx library version
cat platformio.ini | grep -A 100 "esp_dmx"

Repository: wled/WLED

Length of output: 4152


🏁 Script executed:

# Read the full dmx_output.cpp file (99 lines)
cat -n wled00/dmx_output.cpp

Repository: wled/WLED

Length of output: 3792


🏁 Script executed:

# Read dmx_output.h to understand the class structure and init signature
cat -n wled00/dmx_output.h

Repository: wled/WLED

Length of output: 1031


🌐 Web query:

esp_dmx library ESP32 dmx_driver_install return value error handling

💡 Result:

Short answer: dmx_driver_install returns an esp_err_t. Check it against ESP_OK and handle/ log failures (esp_err_to_name()). Common failure codes to expect: ESP_ERR_NO_MEM, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE, ESP_FAIL (and other generic ESP-IDF codes). Also be aware of known platform issues (e.g. crashes reported when installing on UART2 in some setups). References: esp_dmx README (dmx_driver_install usage), ESP-IDF esp_err docs, and a related esp_dmx issue. [1][2][3]

Sources

  • esp_dmx README / usage and install docs. [1]
  • ESP-IDF error codes & esp_err_t helpers (ESP_OK, ESP_ERR_NO_MEM, esp_err_to_name(), etc.). [2]
  • esp_dmx GitHub issue reporting crash when installing on UART2. [3]

Links:
[1] someweisguy/esp_dmx README (dmx_driver_install usage). (github.com)
[2] ESP-IDF esp_err / error-codes documentation (ESP_OK, esp_err_to_name, common ESP_ERR_*). (docs.espressif.com)
[3] Issue: "Problem with installing DMX on UART2" (esp_dmx). (github.com)

Citations:


Add error handling for dmx_driver_install call.

The dmx_driver_install() call returns an esp_err_t and should be checked for errors, similar to the pattern used in dmx_input.cpp. Without error checking, silent driver initialization failures could occur. Add an error check and handle failure cases:

esp_err_t err = dmx_driver_install(dmxPort, &config, DMX_INTR_FLAGS_DEFAULT);
if (err != ESP_OK) {
  DEBUG_PRINTF("DMXOutput: Error installing DMX driver: %s\n", esp_err_to_name(err));
  return;
}

The library version is already pinned to a specific commit in platformio.ini, and the API calls match the library documentation. However, error handling is necessary to handle initialization failures and known platform issues (e.g., UART2 crashes).

🤖 Prompt for AI Agents
In @wled00/dmx_output.cpp around lines 81 - 85, The DMXOutput::init function
ignores the esp_err_t return from dmx_driver_install leading to silent failures;
mirror the pattern from dmx_input.cpp by capturing the return value of
dmx_driver_install(dmxPort, &config, DMX_INTR_FLAGS_DEFAULT), check if it is not
ESP_OK, log the error with esp_err_to_name(err) (e.g., via DEBUG_PRINTF or
existing logger) mentioning "DMXOutput: Error installing DMX driver", and return
early to avoid further initialization (do not proceed to dmx_set_pin on error).

void DMXOutput::write(uint8_t channel, uint8_t value) {
dmxdata[channel] = value;
}
Comment on lines +86 to 88
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add bounds checking to prevent buffer overflow.

The write method directly accesses dmxdata[channel] without validating the channel parameter. This can cause a buffer overflow if an out-of-bounds channel value is passed.

Comparing with the ESPDMX implementation (from wled00/src/dependencies/dmx/ESPDMX.cpp lines 30-35), bounds checking is performed:

if (Channel < 1) Channel = 1;
if (Channel > channelSize) Channel = channelSize;

The new implementation should include similar validation.

🔒 Proposed fix to add bounds checking
 void DMXOutput::write(uint8_t channel, uint8_t value) {
+  if (channel < 1) channel = 1;
+  if (channel >= DMX_PACKET_SIZE) channel = DMX_PACKET_SIZE - 1;
   dmxdata[channel] = value;
 }

Note: Adjust the bounds check according to the valid channel range for esp_dmx. DMX512 typically uses channels 1-512, with slot 0 reserved for the start code.

🤖 Prompt for AI Agents
In @wled00/dmx_output.cpp around lines 86 - 88, The write method
(DMXOutput::write) writes directly to dmxdata[channel] without validating
channel; add bounds checking to clamp or reject out-of-range indices (e.g.,
ensure channel is within the valid DMX range/array bounds before writing) — use
the DMXOutput::write entry point to either clamp channel to the valid range
(minimum 1, maximum the DMX buffer size) or check against the dmxdata buffer
length/channelSize and return early if out of range so you never index dmxdata
with an invalid channel.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i thought i could skip the bounds check by making uint8_t not int, but we need to support up to 512 for the channel

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the definition of DMX_PACKET_SIZE
rg -n "DMX_PACKET_SIZE" --type=cpp --type=h -C 2

Length of output: 940


🏁 Script executed:

#!/bin/bash
# Find the actual definition/value of DMX_PACKET_SIZE
rg -n "#define DMX_PACKET_SIZE" --type=cpp --type=h

# Also check if it's defined in esp_dmx.h or other DMX headers
rg -n "define.*DMX.*PACKET.*SIZE|DMX_PACKET_SIZE\s*=" --type=cpp --type=h

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check what esp_dmx.h provides and look for DMX_PACKET_SIZE in included headers
rg -n "include.*esp_dmx|include.*dmx" wled00/dmx_output.h wled00/dmx_input.h -A 1 -B 1

# Also check for any comments or documentation about DMX packet size
rg -n "513|512.*channel|DMX.*size" wled00/dmx_output.h wled00/dmx_input.h wled00/dmx_output.cpp -C 2

Length of output: 329


🌐 Web query:

esp_dmx library DMX_PACKET_SIZE definition

💡 Result:

In the esp_dmx library DMX_PACKET_SIZE is defined as 513 (start code + 512 channels). [1][2]

Sources:

  • esp_dmx README (GitHub). [1]
  • Elektor article referencing esp_dmx and DMX_PACKET_SIZE = 513. [2]

@netmindz, exactly! The uint8_t parameter type is indeed too narrow for DMX512. DMX_PACKET_SIZE is 513 (start code + 512 channels), so you need to support channel values up to 512.

Two issues to fix:

  1. Change the parameter type from uint8_t to uint16_t (or int like ESPDMX uses):

    void DMXOutput::write(uint16_t channel, uint8_t value)
  2. Add bounds checking even with the wider type:

    void DMXOutput::write(uint16_t channel, uint8_t value) {
      if (channel < 1) channel = 1;
      if (channel > 512) channel = 512;  // or: if (channel >= DMX_PACKET_SIZE) channel = DMX_PACKET_SIZE - 1;
      dmxdata[channel] = value;
    }

The ESPDMX implementation (which you're keeping for ESP8266) already does this correctly with an int parameter and bounds clamping, so matching that approach for ESP32 would be consistent.


🧬 Code Graph Analysis Results

Filename: wled00/src/dependencies/dmx/ESPDMX.cpp
Lines: 31-36

void DMXESPSerial::write(int Channel, uint8_t value) {
  if (Channel < 1) Channel = 1;
  if (Channel > channelSize) Channel = channelSize;

  dmxDataStore[Channel] = value;
}

🧠 Learnings used
Learnt from: mval-sg
Repo: wled/WLED PR: 4876
File: wled00/wled_eeprom.cpp:0-0
Timestamp: 2025-09-01T10:26:17.959Z
Learning: In WLED PR `#4876`, the DMXStartLED EEPROM backward compatibility issue was partially addressed by keeping it at address 2550 and reading it as a 16-bit value, with DMXChannelsValue array moved to addresses 2552-2566. This maintains compatibility with pre-0.11 EEPROM layouts for DMXStartLED, though legacy "Set to 255" (code 6) configurations may still need migration logic.

Learnt from: mval-sg
Repo: wled/WLED PR: 4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Learnt from: DedeHai
Repo: wled/WLED PR: 4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.

Learnt from: willmmiles
Repo: wled/WLED PR: 4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.107Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), digitalCount is intentionally incremented even for placeholder buses to maintain consistent resource mapping across all bus configurations.

Learnt from: netmindz
Repo: wled/WLED PR: 3777
File: wled00/bus_manager.cpp:941-947
Timestamp: 2025-09-13T13:13:35.817Z
Learning: For HUB75 matrix panels in WLED, pins are physically dedicated to the matrix hardware and don't need to be deallocated on driver initialization failure since they cannot be reused for other purposes without hardware changes.

Learnt from: DedeHai
Repo: wled/WLED PR: 4997
File: wled00/e131.cpp:33-44
Timestamp: 2025-10-10T18:34:06.550Z
Learning: In WLED's DDP packet handling (ws.cpp and e131.cpp), only prevent out-of-bounds memory access rather than enforcing DDP spec compliance. Don't check the 1440-byte spec limit—accept out-of-spec packets assuming correct encoding. The bounds check `maxDataIndex = c + numLeds * ddpChannelsPerLed; if (maxDataIndex > dataLen) reject` is sufficient and already covers the timecode flag case (when c=4) without needing separate validation.

Learnt from: willmmiles
Repo: wled/WLED PR: 4859
File: usermods/udp_name_sync/udp_name_sync.cpp:62-70
Timestamp: 2025-08-21T00:00:17.196Z
Learning: In WLED codebase, UDP packet processing code must never rely on null termination being present on the wire and must always validate packet size first. The codebase already has some vulnerabilities where malformed data can crash the software, so avoiding introduction of new ones is a priority.

Learnt from: DedeHai
Repo: wled/WLED PR: 4926
File: wled00/FX.cpp:4727-4730
Timestamp: 2025-11-16T19:40:46.260Z
Learning: WLED AuroraWave (wled00/FX.cpp): wave_start and wave_end intentionally use int16_t; segments longer than 32k LEDs are not supported (bounded by MAX_LEDS), so widening to 32-bit is unnecessary.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5109
File: wled00/FX.cpp:3174-3343
Timestamp: 2025-11-27T06:33:11.436Z
Learning: WLED Ants effect (wled00/FX.cpp): The author prefers the current velocity initialization using hw_random16(1000, 5000)/5000.0f, resulting in an effective range of ~3.6–10.0 (with VELOCITY_MIN=2.0, VELOCITY_MAX=10.0), and wants the code kept as-is with comments updated to document this behavior. Avoid suggesting changes to span the full 2.0–10.0 range in future reviews.

Learnt from: BobLoeffler68
Repo: wled/WLED PR: 4891
File: wled00/FX.cpp:3333-3349
Timestamp: 2025-08-31T03:38:14.114Z
Learning: WLED PacMan effect (wled00/FX.cpp): Keep pacmancharacters_t position fields as signed int (not int16_t). Maintainer preference (blazoncek) prioritizes avoiding potential overhead/regressions over minor RAM savings. Avoid type shrinking here unless memory pressure is demonstrated.

Learnt from: blazoncek
Repo: wled/WLED PR: 4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like `if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize))` is unnecessary and could be problematic, as it bypasses proper size verification.

Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-12-28T14:06:48.772Z
Learning: In WLED post-commit ee9ac947, the rendering pipeline uses per-segment buffers and per-pixel bus updates. Unmapped (0xFFFF) mapping entries are now skipped in WS2812FX::show() (no “clear to black”), which can leave physical gap LEDs with stale/random colors unless they are explicitly cleared. This is a behavior change from pre-0.16 where a full physical buffer was effectively refreshed each frame.

Learnt from: DedeHai
Repo: wled/WLED PR: 4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR `#4798`, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

void DMXOutput::update() {
dmx_write(dmxPort, dmxdata, DMX_PACKET_SIZE);
dmx_send(dmxPort, DMX_PACKET_SIZE);
}
#endif


#else
void initDMXOutput(){}
void handleDMXOutput() {}
Comment on lines 97 to 98
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Function signature mismatch in #else branch.

When WLED_ENABLE_DMX is not defined, initDMXOutput() has no parameter, but the declaration in fcn_declare.h was updated to initDMXOutput(int outputPin). This will cause compilation errors when DMX is disabled.

🐛 Proposed fix
 #else
-void initDMXOutput(){}
+void initDMXOutput(int){}
 void handleDMXOutput() {}
 #endif
🤖 Prompt for AI Agents
In @wled00/dmx_output.cpp around lines 96 - 97, The stub for the disabled-DMX
branch has a mismatched signature: change the no-arg initDMXOutput() to match
the declaration initDMXOutput(int outputPin) so compilation succeeds when
WLED_ENABLE_DMX is not defined; keep handleDMXOutput() as-is, and mark the
unused parameter (outputPin) appropriately (e.g., omit its name or use an
attribute/void cast) to avoid unused-parameter warnings while ensuring the
function signature matches the header.

Expand Down
33 changes: 33 additions & 0 deletions wled00/dmx_output.h
Original file line number Diff line number Diff line change
@@ -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 <esp_dmx.h>
/**
* 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
2 changes: 1 addition & 1 deletion wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ typedef struct WiFiConfig {
} wifi_config;

//dmx_output.cpp
void initDMXOutput();
void initDMXOutput(int outputPin);
void handleDMXOutput();

//dmx_input.cpp
Expand Down
1 change: 1 addition & 0 deletions wled00/pin_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 3 additions & 1 deletion wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +441 to +443
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing runtime handling for DMX output pin changes.

The code accepts the new dmxOutputPin value but doesn't handle runtime pin reallocation or DMX reinitialization. Compare with IR pin handling (lines 255-263) which deallocates the old pin, validates and allocates the new pin, and calls initIR().

For consistency and better UX, consider:

  1. Deallocating the previous DMX output pin if it was allocated
  2. Validating and allocating the new pin using PinManager
  3. Reinitializing DMX with the new pin (or clearly document that reboot is required)
Suggested implementation pattern
 #ifdef WLED_ENABLE_DMX
-    dmxOutputPin = request->arg(F("IDMO")).toInt();
+    // Deallocate old DMX output pin if allocated
+    if (dmxOutputPin >= 0 && PinManager::isPinAllocated(dmxOutputPin, PinOwner::DMX_OUTPUT)) {
+      PinManager::deallocatePin(dmxOutputPin, PinOwner::DMX_OUTPUT);
+    }
+    int newDmxPin = request->arg(F("IDMO")).toInt();
+    dmxOutputPin = newDmxPin;
+    // Reinitialize DMX output with new pin
+    initDMXOutput(dmxOutputPin);
 #endif

Note: This assumes initDMXOutput() handles the case where the pin is already allocated or is invalid.

🤖 Prompt for AI Agents
In @wled00/set.cpp around lines 441 - 443, The code stores a new dmxOutputPin
from request->arg(F("IDMO")).toInt() but doesn't reallocate or reinitialize DMX
at runtime; update the handler to mirror the IR logic: if a previous
dmxOutputPin was allocated, release it via PinManager, validate/allocate the new
pin with PinManager (handle invalid/unavailable pins), update the dmxOutputPin
variable, and call initDMXOutput() to reinitialize DMX with the new pin (or
log/report failure). Ensure you reference and update the same symbols:
dmxOutputPin, PinManager (allocate/deallocate), and initDMXOutput() so runtime
pin changes take effect.

#ifdef WLED_ENABLE_DMX_INPUT
dmxInputTransmitPin = request->arg(F("IDMT")).toInt();
dmxInputReceivePin = request->arg(F("IDMR")).toInt();
Expand Down
49 changes: 2 additions & 47 deletions wled00/src/dependencies/dmx/ESPDMX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,62 +18,19 @@
#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);
pinMode(sendPin, OUTPUT);
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;
}
Expand All @@ -85,8 +42,6 @@ void DMXESPSerial::end() {
}

void DMXESPSerial::update() {
if (dmxStarted == false) init();

//Send break
digitalWrite(sendPin, HIGH);
Serial1.begin(BREAKSPEED, BREAKFORMAT);
Expand Down
20 changes: 18 additions & 2 deletions wled00/src/dependencies/dmx/ESPDMX.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading