-
-
Notifications
You must be signed in to change notification settings - Fork 4k
DMX Output - Runtime Config & replace SparkFunDMX with esp_dmx #5287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c0d662a
37732ca
96d4489
84ce66f
d40f0f6
7d3a673
d336b97
576b628
2f874cf
034e4f5
834c285
22aff94
585d174
7a910c8
5cdca58
48168ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -597,6 +597,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { | |
| tdd = if_live[F("timeout")] | -1; | ||
| if (tdd >= 0) realtimeTimeoutMs = tdd * 100; | ||
|
|
||
| #ifdef WLED_ENABLE_DMX | ||
| CJSON(dmxOutputPin, if_live_dmx[F("dmxOutputPin")]); | ||
| #endif | ||
| #ifdef WLED_ENABLE_DMX_INPUT | ||
| CJSON(dmxInputTransmitPin, if_live_dmx[F("inputRxPin")]); | ||
| CJSON(dmxInputReceivePin, if_live_dmx[F("inputTxPin")]); | ||
|
|
@@ -1118,6 +1121,9 @@ void serializeConfig(JsonObject root) { | |
| if_live_dmx[F("addr")] = DMXAddress; | ||
| if_live_dmx[F("dss")] = DMXSegmentSpacing; | ||
| if_live_dmx["mode"] = DMXMode; | ||
| #ifdef WLED_ENABLE_DMX | ||
| if_live_dmx[F("dmxOutputPin")] = dmxOutputPin; | ||
| #endif | ||
|
Comment on lines
+1124
to
+1126
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure JSON key matches deserialization. This serialization block writes 🤖 Prompt for AI Agents |
||
| #ifdef WLED_ENABLE_DMX_INPUT | ||
| if_live_dmx[F("inputRxPin")] = dmxInputTransmitPin; | ||
| if_live_dmx[F("inputTxPin")] = dmxInputReceivePin; | ||
|
|
||
| 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 | ||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Format specifier mismatch:
🐛 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 |
||
| } | ||
| dmx.init(outputPin); // set output pin and initialize DMX output | ||
| } | ||
|
Comment on lines
+69
to
+78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's look at the full dmx_output.cpp file to understand context
wc -l wled00/dmx_output.cppRepository: wled/WLED Length of output: 77 🏁 Script executed: # Read the entire dmx_output.cpp file
cat -n wled00/dmx_output.cppRepository: 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 -100Repository: 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 1Repository: wled/WLED Length of output: 14357 🏁 Script executed: # Check for other pin allocation error handling patterns
rg "allocatePin.*false|!pinAllocated" wled00/ -A 3 -B 1Repository: 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 1Repository: wled/WLED Length of output: 1104 🏁 Script executed: # Search for dmxOutputPin or similar variable
rg "dmx.*[Pp]in|dmxOutput" wled00/ -iRepository: 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 5Repository: 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 Additionally, pin allocation failures only log via 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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 -20Repository: 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 -20Repository: wled/WLED Length of output: 792 🏁 Script executed: # Check the full DMXOutput file for context and error handling patterns
wc -l wled00/dmx_output.cppRepository: 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 3Repository: 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.cppRepository: 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.hRepository: wled/WLED Length of output: 1031 🌐 Web query:
💡 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
Links: Citations:
Add error handling for The 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 |
||
| void DMXOutput::write(uint8_t channel, uint8_t value) { | ||
| dmxdata[channel] = value; | ||
| } | ||
|
Comment on lines
+86
to
88
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add bounds checking to prevent buffer overflow. The Comparing with the ESPDMX implementation (from 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
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 940 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 329 🌐 Web query: 💡 Result: In the esp_dmx library DMX_PACKET_SIZE is defined as 513 (start code + 512 channels). [1][2] Sources:
Two issues to fix:
The ESPDMX implementation (which you're keeping for ESP8266) already does this correctly with an 🧬 Code Graph Analysis ResultsFilename: wled00/src/dependencies/dmx/ESPDMX.cpp 🧠 Learnings used |
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function signature mismatch in When 🐛 Proposed fix #else
-void initDMXOutput(){}
+void initDMXOutput(int){}
void handleDMXOutput() {}
#endif🤖 Prompt for AI Agents |
||
|
|
||
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing runtime handling for DMX output pin changes. The code accepts the new For consistency and better UX, consider:
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);
#endifNote: This assumes 🤖 Prompt for AI Agents |
||
| #ifdef WLED_ENABLE_DMX_INPUT | ||
| dmxInputTransmitPin = request->arg(F("IDMT")).toInt(); | ||
| dmxInputReceivePin = request->arg(F("IDMR")).toInt(); | ||
|
|
||
There was a problem hiding this comment.
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?