Skip to content

Conversation

@softhack007
Copy link
Collaborator

@softhack007 softhack007 commented Jan 25, 2026

  • Align ESPDMX and SparkFunDMX with upstream code
  • Prevent driver compilation when WLED_ENABLE_DMX is not set (saves ~1KB RAM)
  • Protect against writing outside of array bounds

NB: there is a PR in upstream that addresses possible logic errors in DMX output (not covered by this MM-specific PR)

Summary by CodeRabbit

  • New Features

    • Broadened DMX driver support to additional ESP-based targets for wider device compatibility.
    • Increased DMX channel buffer to accommodate a protocol start byte.
  • Bug Fixes

    • Improved input validation to prevent out-of-range reads.
    • Clamped writes and tightened packet handling to avoid buffer overruns and ensure safer DMX transfers.

✏️ Tip: You can customize this high-level summary in your review settings.

imports a few DMX serial output improvement from upstream
both ESPDMX and SparkFunDMX have their own global buffers. Conditional compile prevents that these buffers are consuming RAM when not needed.
Copilot AI review requested due to automatic review settings January 25, 2026 14:55
@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

Warning

Rate limit exceeded

@softhack007 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 53 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Broadened DMX driver selection to additional ESP32 variants, added compile-time diagnostic pragmas, expanded DMX buffer from 512 to 513 to accommodate a start byte, and added bounds checks/clamping for DMX reads, writes, send, and receive paths.

Changes

Cohort / File(s) Summary
Pragma Message Updates
wled00/dmx_input.cpp, wled00/dmx_output.cpp
Updated compile-time diagnostic strings: input now reports "DMX physical input driver (esp-dmx) enabled"; output message changed to "DMX output enabled".
ESPDMX Driver
wled00/src/dependencies/dmx/ESPDMX.cpp
Broadened preprocessor guards to enable ESPDMX when WLED_ENABLE_DMX and target is ESP8266, ESP32C3, or ESP32S2. Added #pragma message "using ESPDMX", increased DMX data array length to 513, and introduced defaultMax = 32 for initial channel sizing.
SparkFunDMX Driver Hardening
wled00/src/dependencies/dmx/SparkFunDMX.cpp
Enabled under WLED_ENABLE_DMX with diagnostic pragma. Enlarged DMX buffer to 513. Added bounds checks and clamping: reads return 0 for Channel < 1 or > dmxMaxChannel; writes adjust chanSize and clamp Channel to dmxMaxChannel; send uses bounded length (min(dmxMaxChannel+1, chanSize)); receive path only writes when currentChannel <= dmxMaxChannel and end-of-packet considers both chanSize and dmxMaxChannel.
Conditional Compilation Update
wled00/wled.h
Adjusted DMX header selection and dmx type: include ESPDMX.h and declare DMXESPSerial dmx for ESP8266, CONFIG_IDF_TARGET_ESP32C3, or CONFIG_IDF_TARGET_ESP32S2; otherwise include SparkFunDMX.h and declare SparkFunDMX dmx.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I hopped through headers, quick and spry,
Gave one more slot where bytes may lie,
I checked each channel, capped the stream,
Pragmas winked in build-time gleam,
A rabbit's tweak to keep lights beam.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main purpose of the changeset: fixing crashes in DMX-serial output by preventing array bounds violations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses critical array bounds violations in DMX serial output that could cause random crashes. It includes platform alignment updates and conditional compilation improvements to prevent unnecessary code inclusion.

Changes:

  • Increased DMX data array size from 512 to 513 elements to account for start byte at index 0
  • Added bounds checking in read/write operations to prevent array overflows
  • Extended platform support to include ESP32-C3 and ESP32-S2 variants
  • Added conditional compilation guards to prevent DMX driver compilation when not needed

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
wled00/wled.h Extended platform check to include ESP32-C3 and ESP32-S2 for ESPDMX driver selection
wled00/src/dependencies/dmx/SparkFunDMX.cpp Fixed array bounds issues by increasing buffer size, adding bounds checks, and limiting write operations
wled00/src/dependencies/dmx/ESPDMX.cpp Increased buffer size and added conditional compilation guard with platform checks
wled00/dmx_output.cpp Updated pragma message text for clarity
wled00/dmx_input.cpp Updated pragma message text for clarity

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
wled00/wled.h (1)

166-172: Align DMX driver selection for ESP32‑C3/S2.

The include guard now routes ESP32C3/S2 to ESPDMX, but the global dmx instance still selects SparkFunDMX for all non‑ESP8266 targets. This will not compile (or will select the wrong driver) on C3/S2. Please mirror the same target guard used for the header selection.

🛠️ Proposed fix
-#ifdef ESP8266
+#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2)
  WLED_GLOBAL DMXESPSerial dmx;
 `#else` //ESP32
  WLED_GLOBAL SparkFunDMX dmx;
 `#endif`

Also applies to: 470-475

wled00/dmx_output.cpp (1)

13-106: Update init path for ESP32‑C3/S2 when using ESPDMX.

The driver selection now routes C3/S2 to ESPDMX, but initDMXOutput() still uses the SparkFun path for all non‑ESP8266 targets. That will call initWrite() on ESPDMX and fail to compile. Mirror the same target guard used for the include/instance.

🛠️ Proposed fix
 void initDMXOutput() {
- `#ifdef` ESP8266
+ `#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`
 }
🤖 Fix all issues with AI agents
In `@wled00/src/dependencies/dmx/ESPDMX.cpp`:
- Around line 34-35: The DMX buffer dmxDataStore is sized as dmxMaxChannel+1
(index 0 is start byte) but Serial1.write currently sends channelSize bytes
which omits the last channel; update all Serial1.write usages (and any similar
sends around the dmx handling functions) to send min(channelSize + 1,
dmxMaxChannel + 1) bytes so the start byte plus all channels are transmitted,
and ensure channelSize is clamped/validated against dmxMaxChannel before use
(references: dmxDataStore, dmxMaxChannel, channelSize, and Serial1.write).

In `@wled00/src/dependencies/dmx/SparkFunDMX.cpp`:
- Around line 39-40: The current send call uses min(dmxMaxChannel, chanSize)
which limits sent bytes to 512 and omits channel 512; update the write length to
use dmxMaxChannel + 1 as the upper bound (e.g. use min(dmxMaxChannel + 1,
chanSize)) so the 513-byte buffer (dmxData) including the start byte at index 0
is fully transmitted; locate uses in SparkFunDMX.cpp around dmxData and the
DMXSerial.write(...) call (also at the second occurrence noted) and change the
length calculation accordingly.

Repository owner deleted a comment from coderabbitai bot Jan 25, 2026
bounds check before changing global variables
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@wled00/src/dependencies/dmx/SparkFunDMX.cpp`:
- Around line 133-138: The write logic in SparkFunDMX::write incorrectly sets
chanSize to the channel index, but update() expects chanSize to be the total
buffer length including the start byte; this can drop the last channel. After
clamping Channel against bounds (and dmxMaxChannel), set chanSize = Channel + 1
(rather than chanSize = Channel) so chanSize reflects length including the start
byte; keep existing clamping behavior and assignment to dmxData[Channel].
- Around line 125-128: SparkFunDMX::read has an off-by-one: it returns
dmxData[Channel - 1] whereas SparkFunDMX::write stores at dmxData[Channel] and
the DMX buffer uses index 0 as the start code and index N for channel N; change
SparkFunDMX::read to return dmxData[Channel] (remove the "- 1" offset) and
delete the outdated comment about subtracting one, keeping the existing
chanSize/dmxMaxChannel bounds checks unchanged so reads remain safe.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

softhack007 and others added 2 commits January 25, 2026 17:29
If Channel is less than 0, it's clamped to 0 on line 134. Then on line 138, dmxData[0] is written with the value, overwriting the start byte that was just set on line 137. DMX Channel numbers should be 1-based (1-512). The bounds check should clamp Channel to a minimum of 1, not 0, to prevent corrupting the start byte

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@softhack007
Copy link
Collaborator Author

@coderabbitai please perform a comprehensive review

@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read all the changed files to perform a comprehensive review

echo "=== Reading dmx_input.cpp ==="
cat wled00/dmx_input.cpp

echo -e "\n\n=== Reading dmx_output.cpp ==="
cat wled00/dmx_output.cpp

echo -e "\n\n=== Reading ESPDMX.cpp ==="
cat wled00/src/dependencies/dmx/ESPDMX.cpp

echo -e "\n\n=== Reading SparkFunDMX.cpp ==="
cat wled00/src/dependencies/dmx/SparkFunDMX.cpp

echo -e "\n\n=== Reading wled.h (DMX-related sections) ==="
# Get context around DMX includes
grep -n -B5 -A5 "DMX" wled00/wled.h | head -80

echo -e "\n\n=== Getting the actual diff ==="
git diff origin/mdev...HEAD

Length of output: 32043


@softhack007, I'll provide a comprehensive review of this DMX hotfix PR.

Overall Assessment

This PR makes important progress toward preventing array bounds violations in DMX output. The SparkFunDMX.cpp changes are solid and comprehensive. However, I've identified a critical bug in ESPDMX.cpp that needs to be addressed.

Critical Issue: ESPDMX.cpp

🔴 Bug in update() function - Missing Last DMX Channel

File: wled00/src/dependencies/dmx/ESPDMX.cpp

The array was correctly expanded from 512 to 513 elements to accommodate:

  • Index 0: DMX start byte
  • Index 1-512: DMX channels 1-512

However, the update() function still sends only channelSize bytes:

Serial1.write(dmxDataStore, channelSize);

Problem: If channelSize = 512, this sends bytes at indices 0-511, which transmits:

  • Start byte (index 0) ✓
  • Channels 1-511 (indices 1-511) ✓
  • Channel 512 (index 512) is NOT sent

Fix Required: Send channelSize + 1 bytes to include all channels:

Serial1.write(dmxDataStore, channelSize + 1);

Or alternatively, modify init() to match SparkFunDMX's approach by setting channelSize = chanQuant + 1.

⚠️ Missing Bounds Check in write() function

File: wled00/src/dependencies/dmx/ESPDMX.cpp

The write() function clamps Channel to channelSize but not to dmxMaxChannel:

void DMXESPSerial::write(int Channel, uint8_t value) {
  if (Channel < 1) Channel = 1;
  if (Channel > channelSize) Channel = channelSize;  // Could be > dmxMaxChannel
  ...
  dmxDataStore[Channel] = value;
}

Recommendation: Add defense-in-depth bounds checking:

if (Channel > dmxMaxChannel) Channel = dmxMaxChannel;

This prevents potential out-of-bounds writes if channelSize is somehow set incorrectly, or if the function is called before initialization completes.

Minor: Redundant Type Checks

Lines with if (value < 0) and if (value > 255) are redundant since value is uint8_t (0-255 by definition).

Good Changes

✅ SparkFunDMX.cpp - Excellent Bounds Checking

The bounds checking in SparkFunDMX is comprehensive and correct:

  1. read() function - Returns 0 for out-of-bounds channels ✓
  2. write() function - Properly clamps to dmxMaxChannel and expands chanSize safely ✓
  3. update() send path - Limits bytes sent to min(dmxMaxChannel+1, chanSize)
  4. update() receive path - Checks currentChannel <= dmxMaxChannel before writing ✓

✅ Platform-Specific Driver Selection

The conditional compilation changes correctly route ESP32-C3 and ESP32-S2 to use ESPDMX instead of SparkFunDMX, which is appropriate since those platforms lack HardwareSerial(2).

✅ Conditional Compilation Guards

Adding WLED_ENABLE_DMX checks prevents driver compilation when DMX is disabled, saving RAM.

Recommendations

  1. Fix the critical bug in ESPDMX.cpp update() to send channelSize + 1 bytes
  2. Add bounds checking in ESPDMX.cpp write() against dmxMaxChannel
  3. Consider adding documentation explaining the indexing scheme (start byte at 0, channels at 1-512) in both driver files for future maintainers
  4. Note: I couldn't locate the upstream WLED PR #5316 you referenced. You may want to verify that link or consider if additional fixes from upstream should be included.

Would you like me to help generate the fixes for the critical issue in ESPDMX.cpp?


🧠 Learnings used
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR `#270`, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue `#272`; do not address it within PR `#270`.

Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR `#248` merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR `#270`, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 293
File: wled00/udp.cpp:668-669
Timestamp: 2025-12-21T22:26:03.261Z
Learning: In MoonModules/WLED-MM on 32-bit ESP32 platforms, changing pixel index variables to `int` type to align with the `setPixelColor(int, ...)` API signature is intentional and safe. Maximum pixel addresses are bounded by UINT16_MAX, and `setPixelColor()` performs internal overflow checks.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-24T00:15:38.069Z
Learning: In MoonModules/WLED-MM, when reading ESP32/ESP32-S3 eFuse blocks using esp_efuse_read_field_blob(): EFUSE_BLK0 has only 7 × 32-bit registers (224 bits total), not 8 registers (256 bits). Reading more than 7 words from EFUSE_BLK0 triggers assertion failure in esp_efuse_utility_read_reg(). Use maximum 7 words when reading EFUSE_BLK0 to avoid crashes. This applies to all ESP32 variants (ESP32, ESP32-S2, ESP32-S3, ESP32-C3).

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-14T13:55:44.442Z
Learning: In MoonModules/WLED-MM, always flag Variable Length Arrays (VLA) during code reviews, especially when they could overflow available stack space (typically 2K-4K on ESP32/ESP8266). VLAs based on dynamic parameters like segment dimensions, particle counts, or pixel buffers should be highlighted and require justification from the author. Prefer fixed-size arrays, heap allocation, or bounded VLAs with explicit maximum size constraints.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-12-22T20:31:17.991Z
Learning: In MoonModules/WLED-MM on ESP32/ESP8266 platforms with limited heap memory, when reallocating buffers (e.g., LED buffers), always free the old buffer before allocating the new one. Allocating the new buffer first would temporarily require double the memory and could cause malloc failures when available RAM is low. The free-then-allocate pattern is preferred for resource-constrained embedded devices, even if it creates a brief nullptr window that must be protected by proper synchronization.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-12-23T21:29:14.927Z
Learning: In MoonModules/WLED-MM, presets.json writes must be serialized and atomic: guard all write paths with a dedicated file-level mutex (presetFileMux) and use temp-file + rename to replace the file. The “immediate write” branch in presets.cpp can race with async doSaveState() and corrupt presets.json; prefer routing all saves through the async path or enforce the same file mutex + atomic write there.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-12-25T00:46:23.516Z
Learning: In MoonModules/WLED-MM file operations using the global file handle `f` in file.cpp, writeObjectToFile() must check `if (doCloseFile) closeFile();` before opening a new file, following the same pattern as readObjectFromFile(). Without this check, opening a new file while a previous file operation has set doCloseFile=true will overwrite the global file handle without flushing buffers, causing data corruption. This is critical for preventing presets.json corruption when async and immediate write paths race.

* clamp channel to dmxMaxChannel (write)
* ensure that all data get sent, including start byte
@softhack007
Copy link
Collaborator Author

@coderabbitai please perform an in-depth review of the data flow, to verify that all DMX data is always sent, no additional data gets sent and array bounds are always respected. Also consider that in case that the data array (channels) is full, it's acceptable to drop or ignore write requests that would require a bigger buffer array.

@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Reading ESPDMX.cpp for detailed analysis ==="
cat wled00/src/dependencies/dmx/ESPDMX.cpp

echo -e "\n\n=== Reading SparkFunDMX.cpp for detailed analysis ==="
cat wled00/src/dependencies/dmx/SparkFunDMX.cpp

echo -e "\n\n=== Checking for any other DMX-related code ==="
rg -n "channelSize|chanSize|dmxMaxChannel" --type=cpp -g '*dmx*' -C2

Length of output: 8475


@softhack007, I've completed the in-depth data flow analysis. Here are my findings:

✅ ESPDMX.cpp - Data Flow Analysis

Initialization & Buffer Setup

  • Array: dmxDataStore[513] (index 0 = start byte, indices 1-512 = channels)
  • init(32): sets channelSize = 32
  • init(512): sets channelSize = 512

Write Operations

write(Channel, value):
  • Clamps: Channel ∈ [1, min(channelSize, dmxMaxChannel)]
  • Writes to: dmxDataStore[Channel]
  • Full buffer behavior: Channels > dmxMaxChannel clamped to 512

Bounds Check: ✅ Safe - dual clamping to both channelSize and dmxMaxChannel

Send Operations

update():
  • Sends: min(dmxMaxChannel+1, channelSize+1) bytes
  • Example: channelSize=32 → sends 33 bytes (indices 0-32)
  • Example: channelSize=512 → sends 513 bytes (indices 0-512)

Data Transmission:CORRECT - All DMX data sent, no extra data, no missing data


✅ SparkFunDMX.cpp - Data Flow Analysis (Write Path)

Initialization & Buffer Setup

  • Array: dmxData[513] (index 0 = start byte, indices 1-512 = channels)
  • initWrite(32): sets chanSize = 33 (accounts for start byte)
  • initWrite(512): sets chanSize = 513

Write Operations

write(Channel, value):
  • Auto-expands: chanSize = min(dmxMaxChannel+1, Channel+1)
  • Clamps: Channel ∈ [1, dmxMaxChannel]
  • Writes to: dmxData[0]=0 (start), dmxData[Channel]=value
  • Full buffer behavior: Channel > 512 clamped to 512

Bounds Check: ✅ Safe - clamped to dmxMaxChannel, dynamic expansion capped at 513

Send Operations

update() WRITE path:
  • Sends: min(dmxMaxChannel+1, chanSize) bytes
  • Example: chanSize=33 → sends 33 bytes (indices 0-32)
  • Example: chanSize=513 → sends 513 bytes (indices 0-512)

Data Transmission:CORRECT - All DMX data sent, no extra data

Receive Operations

update() READ path:
  • Guard: if (currentChannel <= dmxMaxChannel)
  • Writes to: dmxData[currentChannel++]
  • Stops when: currentChannel > max(chanSize, dmxMaxChannel)

Bounds Check: ✅ Safe - writes only when currentChannel ≤ 512


📊 Verification Summary

Requirement ESPDMX.cpp SparkFunDMX.cpp
All DMX data sent ✅ Yes - sends channelSize+1 bytes including start byte ✅ Yes - sends chanSize bytes (already includes +1)
No extra data sent ✅ Capped at min(513, channelSize+1) ✅ Capped at min(513, chanSize)
Array bounds respected ✅ Write clamped to dmxMaxChannel=512 ✅ Write clamped to dmxMaxChannel=512
Full buffer handling ✅ Writes beyond capacity clamped to channel 512 ✅ Writes beyond capacity clamped to channel 512

Edge Case Testing

Scenario 1: Maximum Capacity (512 channels)

  • ESPDMX: channelSize=512 → sends 513 bytes [0-512] ✅
  • SparkFunDMX: chanSize=513 → sends 513 bytes [0-512] ✅

Scenario 2: Write to Channel 513 (Invalid)

  • ESPDMX: Clamped to 512, writes dmxDataStore[512]
  • SparkFunDMX: Clamped to 512, writes dmxData[512]

Scenario 3: Buffer Expansion (SparkFunDMX only)

  • initWrite(32) sets chanSize=33
  • write(100, val) expands to chanSize=101
  • update() sends min(513, 101)=101 bytes ✅

Scenario 4: Channel 512 Transmission

  • ESPDMX: write(512, val)dmxDataStore[512]=val → sent in byte 513 ✅
  • SparkFunDMX: write(512, val)dmxData[512]=val → sent in byte 513 ✅

🎯 Conclusion

Both drivers are now CORRECT. The PR successfully:

  1. Sends all DMX data - Including channel 512 (last byte in 513-byte packet)
  2. No extra data sent - Transmission capped at dmxMaxChannel+1 = 513 bytes
  3. Array bounds respected - All writes protected by dmxMaxChannel clamps
  4. Full buffer handling - Out-of-range writes gracefully clamped to maximum channel

The critical fix in ESPDMX.cpp using channelSize+1 in the update() function ensures the start byte and all channels are transmitted correctly. The protections added in both drivers prevent array bounds violations that could cause the crashes mentioned in the PR description.


📝 Minor Note (Out of Scope)

There's a pre-existing indexing inconsistency in SparkFunDMX.cpp read() function (uses dmxData[Channel-1] while write() uses dmxData[Channel]), but this is in the DMX_SEND_ONLY receive path and doesn't affect the DMX output functionality being fixed in this PR.


🧠 Learnings used
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-14T13:55:44.442Z
Learning: In MoonModules/WLED-MM, always flag Variable Length Arrays (VLA) during code reviews, especially when they could overflow available stack space (typically 2K-4K on ESP32/ESP8266). VLAs based on dynamic parameters like segment dimensions, particle counts, or pixel buffers should be highlighted and require justification from the author. Prefer fixed-size arrays, heap allocation, or bounded VLAs with explicit maximum size constraints.

Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR `#248` merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR `#270`, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue `#272`; do not address it within PR `#270`.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-12-25T00:46:23.516Z
Learning: In MoonModules/WLED-MM file operations using the global file handle `f` in file.cpp, writeObjectToFile() must check `if (doCloseFile) closeFile();` before opening a new file, following the same pattern as readObjectFromFile(). Without this check, opening a new file while a previous file operation has set doCloseFile=true will overwrite the global file handle without flushing buffers, causing data corruption. This is critical for preventing presets.json corruption when async and immediate write paths race.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR `#270`, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 293
File: wled00/udp.cpp:668-669
Timestamp: 2025-12-21T22:26:03.261Z
Learning: In MoonModules/WLED-MM on 32-bit ESP32 platforms, changing pixel index variables to `int` type to align with the `setPixelColor(int, ...)` API signature is intentional and safe. Maximum pixel addresses are bounded by UINT16_MAX, and `setPixelColor()` performs internal overflow checks.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-12-22T20:31:17.991Z
Learning: In MoonModules/WLED-MM on ESP32/ESP8266 platforms with limited heap memory, when reallocating buffers (e.g., LED buffers), always free the old buffer before allocating the new one. Allocating the new buffer first would temporarily require double the memory and could cause malloc failures when available RAM is low. The free-then-allocate pattern is preferred for resource-constrained embedded devices, even if it creates a brief nullptr window that must be protected by proper synchronization.

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-24T00:15:38.069Z
Learning: In MoonModules/WLED-MM, when reading ESP32/ESP32-S3 eFuse blocks using esp_efuse_read_field_blob(): EFUSE_BLK0 has only 7 × 32-bit registers (224 bits total), not 8 registers (256 bits). Reading more than 7 words from EFUSE_BLK0 triggers assertion failure in esp_efuse_utility_read_reg(). Use maximum 7 words when reading EFUSE_BLK0 to avoid crashes. This applies to all ESP32 variants (ESP32, ESP32-S2, ESP32-S3, ESP32-C3).

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-12-23T21:29:14.927Z
Learning: In MoonModules/WLED-MM, presets.json writes must be serialized and atomic: guard all write paths with a dedicated file-level mutex (presetFileMux) and use temp-file + rename to replace the file. The “immediate write” branch in presets.cpp can race with async doSaveState() and corrupt presets.json; prefer routing all saves through the async path or enforce the same file mutex + atomic write there.

@softhack007
Copy link
Collaborator Author

softhack007 commented Jan 25, 2026

@netmindz @troyhacks Could you quickly try the code with DMX hardware? I don't have DMX hardware, and we should be sure that I did not create any "regressions".A quick run with hardware connected should be sufficient,

@softhack007
Copy link
Collaborator Author

I'll release this into the wild now, hoping that users will report back in case of new problems.

@softhack007 softhack007 merged commit c2d755b into mdev Jan 25, 2026
65 checks passed
@softhack007 softhack007 added this to the 14.7.3 milestone Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants