Skip to content
Closed
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
39 changes: 39 additions & 0 deletions docs/esp32-p4-eth-network-compat.md

@softhack007 softhack007 Jun 22, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sorry but explanations is this file do not make any sense for me, even when I read it in the context of the "V5-C6" branch. AI slop?

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ESP32-P4 Ethernet network helper compatibility

This branch keeps WLED's network helper on the unique symbol `WLEDNetwork` while preserving existing WLED source call sites that use `Network`.

## What changed

WLED previously had its own helper object named `Network`. The ESP32 Arduino 3.x / ESP-IDF 5 stack also provides a global `Network` object for its `NetworkManager`, which creates a name collision when building the ESP32-P4/V5 stack.

To avoid that collision while minimizing churn in the branch:

- WLED's helper class/object are named `WLEDNetworkClass` and `WLEDNetwork`.
- `Network.h` maps legacy WLED helper calls with `#define Network WLEDNetwork` after including the framework WiFi/Ethernet headers.
- `Network.cpp` defines the `WLEDNetwork` object rather than a WLED-owned object named `Network`.
- The shared `DNSlookup` state used by `BusNetwork::resolveHostname()` is declared/defined so the current `bus_manager.cpp` code has a valid object to use.

## Consequences and tradeoffs

This is intended to keep both relevant ESP32 build stacks viable:

- the older/current WLED ESP32 stack where WLED code historically used `Network`, and
- the ESP32-P4/V5 stack where Arduino-ESP32 provides its own global `Network` object.

The compatibility macro keeps the diff small because most WLED call sites can remain as `Network.isConnected()`, `Network.localIP()`, etc. Those calls are redirected to WLED's `WLEDNetwork` helper after `Network.h` is included.

The main downside is that `Network` becomes a preprocessor alias in translation units that include WLED's `Network.h`. Code in those translation units that intentionally needs Arduino-ESP32's own `NetworkManager Network` after including WLED's helper header may need to include/use it before this header or locally `#undef Network`. This branch does not currently add such a use case.

The helper object name exposed by WLED is now `WLEDNetwork`. Any external code or usermod that directly declared or linked against WLED's old `NetworkClass Network` symbol, instead of using normal WLED headers and call sites, may need adjustment.

No runtime behavior change is intended for Wi-Fi, Ethernet, Art-Net, DDP, Improv, JSON info, or Espalexa. The change is primarily a build/link compatibility fix for the network helper name collision.

## Test note

ETH and DDP tested only.

PlatformIO build validation should still be run for at least:

- `esp32dev_V4`
- `esp32dev`
- `esp32p4_32MB`
50 changes: 50 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ default_envs = nodemcuv2
esp32s3dev_8MB_qspi
esp32s3dev_8MB_none
esp32s3_4M_qspi
esp32p4_32MB
usermods

src_dir = ./wled00
Expand Down Expand Up @@ -383,6 +384,55 @@ board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8
upload_speed = 921600
monitor_filters = esp32_exception_decoder

[esp32p4]
;; generic definitions for ESP32-P4 boards. Uses pioarduino for IDF 5.5 based P4 support.
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip
platform_packages =
build_unflags = ${common.build_unflags}
-D WLED_ENABLE_DMX_INPUT ;; esp_dmx is excluded for ESP32-P4
-D WLED_ENABLE_DMX ;; DMX serial output needs ESP32-P4-specific adaptation
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32P4
-DCONFIG_IDF_TARGET_ESP32P4=1
Comment on lines +394 to +397

Copy link
Copy Markdown
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:

# First, locate and examine platformio.ini to find the esp32p4 section
rg -n "\[esp32p4\]" platformio.ini -A 10

Repository: wled/WLED

Length of output: 614


🏁 Script executed:

# Check wled00/wled.h around lines 80-88 to see the condition mentioned
sed -n '75,95p' wled00/wled.h

Repository: wled/WLED

Length of output: 771


🏁 Script executed:

# Search for ESP32 define usage across the codebase to understand dependencies
rg -n "ESP32.*ARDUINO_ARCH_ESP32|ARDUINO_ARCH_ESP32.*ESP32" wled00/wled.h -B 2 -A 2

Repository: wled/WLED

Length of output: 953


🏁 Script executed:

# Check other esp32 variants in platformio.ini to see if they have -DESP32
rg -n "\[esp32.*\]" platformio.ini | head -20

Repository: wled/WLED

Length of output: 165


🏁 Script executed:

# For each esp32 variant, check their build_flags
rg -n "^\[esp32" platformio.ini -A 5 | head -100

Repository: wled/WLED

Length of output: 2030


🏁 Script executed:

# Get complete build_flags for each esp32 variant
rg -n "^\[esp32" platformio.ini -A 20 | grep -E "^\[esp32|build_flags|^\s*-D" | head -80

Repository: wled/WLED

Length of output: 427


🏁 Script executed:

# More specifically, search for -DESP32 in platformio.ini across all variants
rg -n "\-DESP32" platformio.ini

Repository: wled/WLED

Length of output: 102


Add -DESP32 to the esp32p4 build flags.

The wled00/wled.h file (line 86) enforces that both ESP32 and ARDUINO_ARCH_ESP32 must be defined together. The current esp32p4 configuration defines ARDUINO_ARCH_ESP32 but not ESP32, which will cause a build error. Other ESP32 variants (e.g., esp32s2, esp32s3) include -DESP32 in their build flags and should be matched.

Suggested fix
 [esp32p4]
 build_flags = -g
+  -DESP32
   -DARDUINO_ARCH_ESP32
   -DARDUINO_ARCH_ESP32P4
   -DCONFIG_IDF_TARGET_ESP32P4=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.

Suggested change
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32P4
-DCONFIG_IDF_TARGET_ESP32P4=1
build_flags = -g
-DESP32
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32P4
-DCONFIG_IDF_TARGET_ESP32P4=1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platformio.ini` around lines 394 - 397, The esp32p4 build configuration in
platformio.ini is missing the `-DESP32` flag in its build_flags section. The
wled.h header file requires both ESP32 and ARDUINO_ARCH_ESP32 to be defined
together, but the current esp32p4 configuration only defines ARDUINO_ARCH_ESP32.
Add `-DESP32` to the build_flags list in the esp32p4 configuration block to
match the pattern used in other ESP32 variants like esp32s2 and esp32s3,
ensuring both required defines are present.

-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-D WLED_ENABLE_GIF
-DCO
-DARDUINO_USB_MODE=1
-D WLED_DISABLE_INFRARED
-D WLED_DISABLE_ESPNOW
-D WLED_DISABLE_MQTT
-D WLED_USE_SHARED_RMT
-D ESP32_ARDUINO_NO_RGB_BUILTIN
lib_deps =
esp32async/AsyncTCP @ 3.4.10
bitbank2/AnimatedGIF@^1.4.7
https://github.com/Aircoookie/GifDecoder.git#bc3af189b6b1e06946569f6b4287f0b79a860f8e
NeoPixelBus = git+https://github.com/Makuna/NeoPixelBus#76afe832f74b0738a3fa1bba0caf389ade9e7693
crankyoldgit/IRremoteESP8266 @ 2.9.0
ESPAsyncWebServerWLED = git+https://github.com/Aircoookie/ESPAsyncWebServer#ac44e32abf2a69ae650412fb6bc193c59ccac38a
marvinroger/AsyncMqttClient @ 0.9.0
https://github.com/blazoncek/QuickESPNow.git#optional-debug
lib_ignore =
NeoESP32RmtHI
IRremoteESP8266
QuickEspNow
esp_dmx

[env:esp32p4_32MB]
;; (experimental) ESP32-P4 with 32 MB flash and PSRAM
extends = esp32p4
board = esp32-p4-evboard
board_build.flash_mode = qio
board_build.partitions = tools/WLED_ESP32_32MB.csv
build_unflags = ${esp32p4.build_unflags}
build_flags = ${common.build_flags} ${esp32p4.build_flags}
-D WLED_RELEASE_NAME=\"ESP32-P4_32MB\"
-DBOARD_HAS_PSRAM
upload_speed = 460800
monitor_filters = esp32_exception_decoder


# ------------------------------------------------------------------------------
# WLED BUILDS
Expand Down
18 changes: 18 additions & 0 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,12 @@ BusPwm::BusPwm(const BusConfig &bc)
pinMode(_pins[i], OUTPUT);
#else
unsigned channel = _ledcStart + i;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
ledcAttachPin(_pins[i], channel);
#else
ledcAttachChannel(_pins[i], _frequency, _depth - (dithering*4), channel);
#endif
// LEDC timer reset credit @dedehai
uint8_t group = (channel / 8), timer = ((channel / 2) % 4); // same fromula as in ledcSetup()
ledc_timer_rst((ledc_mode_t)group, (ledc_timer_t)timer); // reset timer so all timers are almost in sync (for phase shift)
Expand Down Expand Up @@ -585,10 +589,15 @@ void BusPwm::show() {
unsigned ch = channel%8; // group channel
// directly write to LEDC struct as there is no HAL exposed function for dithering
// duty has 20 bit resolution with 4 fractional bits (24 bits in total)
#if defined(CONFIG_IDF_TARGET_ESP32P4)
// TODO: find out if / how dithering support can be implemented on P4
ledc_set_duty_and_update((ledc_mode_t)gr, (ledc_channel_t)ch, duty >> bitShift, hPoint >> bitShift);
#else
LEDC.channel_group[gr].channel[ch].duty.duty = duty << ((!dithering)*4); // lowest 4 bits are used for dithering, shift by 4 bits if not using dithering
LEDC.channel_group[gr].channel[ch].hpoint.hpoint = hPoint >> bitShift; // hPoint is at _depth resolution (needs shifting if dithering)
ledc_update_duty((ledc_mode_t)gr, (ledc_channel_t)ch);
#endif
#endif

if (!_reversed) hPoint += duty;
hPoint += deadTime; // offset to cascade the signals
Expand Down Expand Up @@ -623,7 +632,11 @@ void BusPwm::deallocatePins() {
#ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
if (_ledcStart < WLED_MAX_ANALOG_CHANNELS) ledcDetachPin(_pins[i]);
#else
if (_ledcStart < WLED_MAX_ANALOG_CHANNELS) ledcDetach(_pins[i]);
#endif
#endif
}
#ifdef ARDUINO_ARCH_ESP32
Expand Down Expand Up @@ -1285,6 +1298,10 @@ void BusManager::removeAll() {
// since I2S outputs are known only during config of buses, lets just assume RMT is used for digital buses
// unused RMT channels should have no effect
void BusManager::esp32RMTInvertIdle() {
#if defined(CONFIG_IDF_TARGET_ESP32P4)
// ESP32-P4 uses shared RMT method - idle level inversion not supported
return;
#else
bool idle_out;
unsigned rmt = 0;
unsigned u = 0;
Expand All @@ -1302,6 +1319,7 @@ void BusManager::esp32RMTInvertIdle() {
rmt_set_idle_level(ch, idle_out, lvl);
u++;
}
#endif
}
#endif

Expand Down
38 changes: 35 additions & 3 deletions wled00/bus_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

//#define NPB_CONF_4STEP_CADENCE
#include "NeoPixelBus.h"
#include "wled_boards.h"

//Hardware SPI Pins
#define P_8266_HS_MOSI 13
Expand Down Expand Up @@ -204,6 +205,8 @@

/*** ESP32 Neopixel methods ***/
#ifdef ARDUINO_ARCH_ESP32
#include <esp_arduino_version.h>

// C3: I2S0 and I2S1 methods not supported (has one I2S bus)
// S2: I2S0 methods supported (single & parallel), I2S1 methods not supported (has one I2S bus)
// S3: I2S0 methods not supported, I2S1 supports LCD parallel methods (has two I2S buses)
Expand Down Expand Up @@ -231,7 +234,29 @@
typedef NeoEsp32I2s0Apa106Method X1Apa106Method;
typedef NeoEsp32I2s0Ws2805Method X1Ws2805Method;
typedef NeoEsp32I2s0Tm1914Method X1Tm1914Method;
#elif !defined(CONFIG_IDF_TARGET_ESP32C3)
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
// P4 has no I2S LED driver in NeoPixelBus; keep dormant I2S switch arms compileable.
// AI: below section was generated by an AI
typedef XWs2812xMethod X1Ws2812xMethod;
typedef XSk6812Method X1Sk6812Method;
typedef X400KbpsMethod X1400KbpsMethod;
typedef X800KbpsMethod X1800KbpsMethod;
typedef XTm1814Method X1Tm1814Method;
typedef XTm1829Method X1Tm1829Method;
typedef XApa106Method X1Apa106Method;
typedef XWs2805Method X1Ws2805Method;
typedef XTm1914Method X1Tm1914Method;
typedef XWs2812xMethod X8Ws2812xMethod;
typedef XSk6812Method X8Sk6812Method;
typedef X400KbpsMethod X8400KbpsMethod;
typedef X800KbpsMethod X8800KbpsMethod;
typedef XTm1814Method X8Tm1814Method;
typedef XTm1829Method X8Tm1829Method;
typedef XApa106Method X8Apa106Method;
typedef XWs2805Method X8Ws2805Method;
typedef XTm1914Method X8Tm1914Method;
// AI: end
#elif defined(WLED_HAS_PARALLEL_I2S)
Comment on lines +237 to +259

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search NeoPixelBus library for XWs2812xMethod and similar base type definitions
# to confirm they exist and are available for ESP32-P4

echo "=== Checking NeoPixelBus for base method type definitions ==="
rg -n "typedef.*XWs2812xMethod" lib/ || echo "No typedef found"
rg -n "XWs2812xMethod" lib/ --type cpp --type h | head -20

echo ""
echo "=== Checking for ESP32P4 support in NeoPixelBus ==="
rg -n "ESP32P4|CONFIG_IDF_TARGET_ESP32P4" lib/ --type cpp --type h | head -20

Repository: wled/WLED

Length of output: 185


🏁 Script executed:

# Find where bus_wrapper.h includes NeoPixelBus from
head -50 wled00/bus_wrapper.h | grep -n "`#include`"

# Search more broadly for XWs2812xMethod across entire repo
rg -n "XWs2812xMethod" . --type cpp --type h

# Look for NeoPixelBus include paths
rg -n "NeoPixelBus" wled00/bus_wrapper.h -A 2 -B 2

# Check repository structure for external libraries
ls -la | grep -i lib
find . -maxdepth 3 -name "*NeoPixel*" -o -name "*neopixel*" 2>/dev/null | head -20

Repository: wled/WLED

Length of output: 11245


🏁 Script executed:

# Search for all X*Method definitions in bus_wrapper.h
rg -n "typedef.*X[0-9A-Z]*Method" wled00/bus_wrapper.h

# Look for RMT method definitions that might be the fallback
rg -n "NeoEsp32Rmt" wled00/bus_wrapper.h

# Check full context around the P4 block to see if there's something before it
sed -n '200,280p' wled00/bus_wrapper.h

Repository: wled/WLED

Length of output: 5196


Fix undefined base type names in ESP32-P4 typedef block — code will not compile.

The P4 block attempts to use undefined types (XWs2812xMethod, XSk6812Method, X400KbpsMethod, etc.). These base types do not exist in the WLED codebase or NeoPixelBus. Since P4 has no I2S driver, it must fall back to RMT methods. Replace the undefined X* types with the appropriate RMT method types (e.g., NeoEsp32RmtMethod(Ws2812x) which expands to the correct platform-specific RMT method name based on ESP Arduino version).

Current incorrect pattern (P4 block, lines 240–258)
  typedef XWs2812xMethod X1Ws2812xMethod;
  typedef XSk6812Method X1Sk6812Method;
  typedef X400KbpsMethod X1400KbpsMethod;
  // ... etc (undefined types)

Compare with S2 block above it (lines 225–233), which correctly maps to concrete I2S0 methods, or the S3 block (lines 215–224), which maps to X8* parallel methods. For P4, define these mappings using the RMT methods that NeoPixelBus provides for P4.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/bus_wrapper.h` around lines 237 - 259, The ESP32-P4 typedef block in
the CONFIG_IDF_TARGET_ESP32P4 section uses undefined base types (XWs2812xMethod,
XSk6812Method, X400KbpsMethod, X800KbpsMethod, XTm1814Method, XTm1829Method,
XApa106Method, XWs2805Method, XTm1914Method) that do not exist and will cause
compilation errors. Since P4 has no I2S driver, replace these undefined X* base
types with the appropriate RMT method types that NeoPixelBus provides for P4.
Reference the S2 and S3 typedef blocks above the P4 section to understand the
correct mapping pattern: S3 uses X8* parallel methods while S2 uses concrete
I2S0 methods, so P4 should use the equivalent RMT method types for each LED
protocol (Ws2812x, Sk6812, 400Kbps, 800Kbps, Tm1814, Tm1829, Apa106, Ws2805,
Tm1914).

// regular ESP32 will use I2S1
typedef NeoEsp32I2s1Ws2812xMethod X1Ws2812xMethod;
typedef NeoEsp32I2s1Sk6812Method X1Sk6812Method;
Expand All @@ -245,7 +270,9 @@
#endif

// RMT driver selection
#if !defined(WLED_USE_SHARED_RMT) && !defined(__riscv)
#if ESP_ARDUINO_VERSION_MAJOR >= 3
#define NeoEsp32RmtMethod(x) NeoEsp32RmtX ## x ## Method
#elif !defined(WLED_USE_SHARED_RMT) && !defined(__riscv)
Comment on lines +273 to +275

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for RMT method naming in NeoPixelBus to verify the X variant exists
echo "=== Searching for NeoEsp32RmtX* method types ==="
rg -n "NeoEsp32RmtX" lib/ --type cpp --type h | head -30

echo ""
echo "=== Searching for NeoEsp32Rmt.*Method definitions ==="
rg -n "typedef.*NeoEsp32Rmt.*Method" lib/ --type h | head -30

Repository: wled/WLED

Length of output: 4829


🏁 Script executed:

# First, let's see the complete macro definition context
cat -n wled00/bus_wrapper.h | sed -n '270,290p'

Repository: wled/WLED

Length of output: 1218


🏁 Script executed:

# Search for actual usage of NeoEsp32RmtMethod in the file
rg -n "NeoEsp32RmtMethod" wled00/ --type cpp --type h -A 2

Repository: wled/WLED

Length of output: 4711


🏁 Script executed:

# Check if NeoEsp32RmtX exists anywhere (case-insensitive and without the X)
rg -i "NeoEsp32Rmt[^H].*Method" lib/ --type cpp --type h | head -20

Repository: wled/WLED

Length of output: 3143


🏁 Script executed:

# Search for NeoEsp32RmtN variants (the fallback macro)
rg -n "NeoEsp32RmtN" lib/ --type cpp --type h | head -20

Repository: wled/WLED

Length of output: 35


🏁 Script executed:

# Search for any comments in bus_wrapper.h about Arduino v3 or RMT driver changes
rg -n "Arduino.*3|v3|RMT.*driver" wled00/bus_wrapper.h -B 2 -A 2

Repository: wled/WLED

Length of output: 197


🏁 Script executed:

# Search the entire repo for NeoEsp32RmtX to see if it's defined elsewhere
rg -r "NeoEsp32RmtX" . --type cpp --type h

Repository: wled/WLED

Length of output: 50368


Verify Arduino-ESP32 v3+ RMT method naming—NeoEsp32RmtX types are not found in NeoPixelBus library.*

The macro for Arduino-ESP32 v3+ generates NeoEsp32RmtXWs2812xMethod and similar names, but these types do not exist in the bundled NeoPixelBus library (only NeoEsp32RmtHI* variants are defined). The fallback macro also generates NeoEsp32RmtN* types which also do not exist. These types must be provided by Arduino-ESP32 v3 core headers, but this dependency is undocumented and unverified.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/bus_wrapper.h` around lines 273 - 275, The NeoEsp32RmtMethod macro for
Arduino-ESP32 v3+ generates type names like NeoEsp32RmtXWs2812xMethod that do
not exist in the bundled NeoPixelBus library. Either verify that these types are
provided by Arduino-ESP32 v3 core headers (and document this external
dependency), or correct the macro prefix from NeoEsp32RmtX to match the types
that actually exist in NeoPixelBus (such as NeoEsp32RmtHI variants). The
fallback clause for earlier versions should also be verified to ensure the
generated type names (NeoEsp32RmtN*) actually exist in the available libraries.

#include <NeoEsp32RmtHIMethod.h>
#define NeoEsp32RmtMethod(x) NeoEsp32RmtHIN ## x ## Method
#else
Expand Down Expand Up @@ -1345,10 +1372,15 @@ class PolyBus {
#else //ESP32
// dynamic channel allocation based on driver preference
// determine which driver to use based on preference and availability. First I2S bus locks the I2S type, all subsequent I2S buses are assigned the same type (hardware restriction)
#ifdef WLED_HAS_PARALLEL_I2S
constexpr bool canUseI2S = true;
#else
constexpr bool canUseI2S = false;
#endif
uint8_t offset = 0; // 0 = RMT, 1 = I2S/LCD
if (driverPreference == 0 && _rmtChannelsAssigned < WLED_MAX_RMT_CHANNELS) {
_rmtChannelsAssigned++;
} else if (_i2sChannelsAssigned < WLED_MAX_I2S_CHANNELS) {
} else if (canUseI2S && _i2sChannelsAssigned < WLED_MAX_I2S_CHANNELS) {
Comment on lines +1375 to +1383

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Potential silent failure when I2S driver is preferred but unavailable on P4.

When WLED_HAS_PARALLEL_I2S is not defined (ESP32-P4), canUseI2S is false. If driverPreference == 1 (I2S preferred), the logic:

  1. Skips RMT allocation at line 1381 (driverPreference == 0 fails)
  2. Skips I2S allocation at line 1383 (canUseI2S is false)
  3. Returns I_NONE even if RMT channels are available

This could silently fail bus allocation on P4 if user config specifies I2S preference (e.g., config migrated from ESP32-S3). Consider falling back to RMT when I2S is unavailable:

Proposed fix to allow RMT fallback when I2S unavailable
       `#ifdef` WLED_HAS_PARALLEL_I2S
       constexpr bool canUseI2S = true;
       `#else`
       constexpr bool canUseI2S = false;
       `#endif`
       uint8_t offset = 0; // 0 = RMT, 1 = I2S/LCD
-      if (driverPreference == 0 && _rmtChannelsAssigned < WLED_MAX_RMT_CHANNELS) {
+      if ((driverPreference == 0 || !canUseI2S) && _rmtChannelsAssigned < WLED_MAX_RMT_CHANNELS) {
         _rmtChannelsAssigned++;
       } else if (canUseI2S && _i2sChannelsAssigned < WLED_MAX_I2S_CHANNELS) {
📝 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.

Suggested change
#ifdef WLED_HAS_PARALLEL_I2S
constexpr bool canUseI2S = true;
#else
constexpr bool canUseI2S = false;
#endif
uint8_t offset = 0; // 0 = RMT, 1 = I2S/LCD
if (driverPreference == 0 && _rmtChannelsAssigned < WLED_MAX_RMT_CHANNELS) {
_rmtChannelsAssigned++;
} else if (_i2sChannelsAssigned < WLED_MAX_I2S_CHANNELS) {
} else if (canUseI2S && _i2sChannelsAssigned < WLED_MAX_I2S_CHANNELS) {
`#ifdef` WLED_HAS_PARALLEL_I2S
constexpr bool canUseI2S = true;
`#else`
constexpr bool canUseI2S = false;
`#endif`
uint8_t offset = 0; // 0 = RMT, 1 = I2S/LCD
if ((driverPreference == 0 || !canUseI2S) && _rmtChannelsAssigned < WLED_MAX_RMT_CHANNELS) {
_rmtChannelsAssigned++;
} else if (canUseI2S && _i2sChannelsAssigned < WLED_MAX_I2S_CHANNELS) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/bus_wrapper.h` around lines 1375 - 1383, The conditional logic for
driver selection has a silent failure case: when driverPreference is 1 (I2S
preferred) but canUseI2S is false (on ESP32-P4), neither the RMT nor I2S
allocation branches are taken, causing the function to return I_NONE even though
RMT channels are available. Modify the logic to fall back to RMT allocation when
I2S is preferred but unavailable due to canUseI2S being false. Restructure the
conditions so that if driverPreference equals 1 but canUseI2S is false, the code
still attempts to allocate RMT channels (checking _rmtChannelsAssigned against
WLED_MAX_RMT_CHANNELS), rather than silently failing. This ensures graceful
degradation when user configurations specify I2S preference on hardware where
I2S is not available.

offset = 1; // I2S requested or RMT full
_i2sChannelsAssigned++;
} else {
Expand Down
17 changes: 5 additions & 12 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc[F("eth")];
CJSON(ethernetType, ethernet["type"]);
#if defined(WLED_ETHERNET_ONLY_BUILD) && defined(WLED_ETH_DEFAULT)
if (ethernetType == WLED_ETH_NONE) ethernetType = WLED_ETH_DEFAULT;
#endif
// NOTE: Ethernet configuration takes priority over other use of pins
initEthernet();
#endif
Expand Down Expand Up @@ -922,18 +925,8 @@ void serializeConfig(JsonObject root) {
if (ethernetBoards[ethernetType].eth_power>=0) pins.add(ethernetBoards[ethernetType].eth_power);
if (ethernetBoards[ethernetType].eth_mdc>=0) pins.add(ethernetBoards[ethernetType].eth_mdc);
if (ethernetBoards[ethernetType].eth_mdio>=0) pins.add(ethernetBoards[ethernetType].eth_mdio);
switch (ethernetBoards[ethernetType].eth_clk_mode) {
case ETH_CLOCK_GPIO0_IN:
case ETH_CLOCK_GPIO0_OUT:
pins.add(0);
break;
case ETH_CLOCK_GPIO16_OUT:
pins.add(16);
break;
case ETH_CLOCK_GPIO17_OUT:
pins.add(17);
break;
}
managed_pin_type clockPin = ethernetClockPin(ethernetBoards[ethernetType]);
if (clockPin.pin >= 0) pins.add(clockPin.pin);
}
#endif

Expand Down
34 changes: 24 additions & 10 deletions wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ constexpr size_t FASTLED_PALETTE_COUNT = 7; // 6-12 = sizeof(fastledPalettes)
constexpr size_t GRADIENT_PALETTE_COUNT = 59; // 13-72 = sizeof(gGradientPalettes) / sizeof(gGradientPalettes[0]);
constexpr size_t DYNAMIC_PALETTE_COUNT = 6; // 0- 5 = dynamic palettes (0=default(virtual),1=random,2=primary,3=primary+secondary,4=primary+secondary+tertiary,5=primary+secondary(+tertiary if not black)
constexpr size_t FIXED_PALETTE_COUNT = DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT + GRADIENT_PALETTE_COUNT; // total number of fixed palettes

// Palette ID space layout (palette IDs are uint8_t, 0-255):
// 0 .. FIXED_PALETTE_COUNT-1 : fixed built-in palettes
// 72 .. WLED_CUSTOM_PALETTE_ID_BASE(200) : user custom palettes (index 0 = ID 200, growing downward)
Expand Down Expand Up @@ -73,8 +72,14 @@ constexpr size_t WLED_MAX_USERMOD_PALETTES = WLED_USERMOD_PALETTE_ID_BASE -
#if !defined(LEDC_CHANNEL_MAX) || !defined(LEDC_SPEED_MODE_MAX)
#include "driver/ledc.h" // needed for analog/LEDC channel counts
#endif
#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX)
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM

// define -> constexpr to avoid preprocessor errors and enum arithmetic warnings from newer compilers
#ifdef WLED_MAX_ANALOG_CHANNELS
#undef WLED_MAX_ANALOG_CHANNELS // avoid clash between macro name and constexpr constant
#endif
constexpr size_t WLED_MAX_ANALOG_CHANNELS = static_cast<size_t>(LEDC_CHANNEL_MAX) * static_cast<size_t>(LEDC_SPEED_MODE_MAX);

#if defined(CONFIG_IDF_TARGET_ESP32C3)
Comment on lines +75 to +82

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '^\s*#\s*(if|elif)\b.*\bWLED_MAX_(ANALOG_CHANNELS|BUSSES)\b' --type=cpp --type=h --type=hpp --type=ino

Repository: wled/WLED

Length of output: 81


🏁 Script executed:

#!/bin/bash
rg -nP '^\s*#\s*(if|elif)\b.*\bWLED_MAX_(ANALOG_CHANNELS|BUSSES)\b' --type=cpp --type=h --type=c --type=ino

Repository: wled/WLED

Length of output: 81


🏁 Script executed:

#!/bin/bash
rg -nP '^\s*#\s*(if|elif)\b.*\bWLED_MAX_(ANALOG_CHANNELS|BUSSES)\b' --type=cpp --type=h

Repository: wled/WLED

Length of output: 113


🏁 Script executed:

#!/bin/bash
echo "=== Lines 75-82 and 119-121 in wled00/const.h ==="
sed -n '75,82p; 119,121p' wled00/const.h

echo ""
echo "=== Context around FX_fcn.cpp line 32 ==="
sed -n '28,36p' wled00/FX_fcn.cpp

Repository: wled/WLED

Length of output: 1030


🏁 Script executed:

#!/bin/bash
echo "=== Searching for `#ifdef` checks on these constants ==="
rg -n '`#ifdef`\s+(WLED_MAX_ANALOG_CHANNELS|WLED_MAX_BUSSES)' --type=cpp --type=h

echo ""
echo "=== Searching for all uses of WLED_MAX_BUSSES in preprocessor context ==="
rg -n '#\s*(if|elif|ifdef|ifndef|defined).*\bWLED_MAX_BUSSES\b' --type=cpp --type=h

Repository: wled/WLED

Length of output: 379


Fix preprocessor use of constexpr WLED_MAX_BUSSES in FX_fcn.cpp:32.

The definition in wled00/const.h:120 converts WLED_MAX_BUSSES from a macro to constexpr, but it's still used in a #if preprocessor directive at wled00/FX_fcn.cpp:32. The preprocessor cannot evaluate constexpr values, so the condition will silently treat WLED_MAX_BUSSES as undefined (0), breaking the intended #error check.

Replace the #if directive with a static_assert at runtime or find an alternative approach that doesn't rely on preprocessor evaluation. The guard #ifdef checks in const.h lines 77 and 116 are intentional and correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/const.h` around lines 75 - 82, In FX_fcn.cpp at line 32, replace the
preprocessor `#if` directive that checks WLED_MAX_BUSSES with a static_assert
statement instead. Since WLED_MAX_BUSSES has been converted to a constexpr in
const.h line 120, the preprocessor cannot evaluate it in a `#if` condition. Use
static_assert to perform the same validation check at compile-time while working
correctly with the constexpr value, preserving the intended error check
behavior.

#define WLED_MAX_RMT_CHANNELS 2 // ESP32-C3 has 2 RMT output channels
#define WLED_MAX_I2S_CHANNELS 0 // I2S not supported by NPB
//#define WLED_MAX_ANALOG_CHANNELS 6
Expand All @@ -89,21 +94,30 @@ constexpr size_t WLED_MAX_USERMOD_PALETTES = WLED_USERMOD_PALETTE_ID_BASE -
#define WLED_MAX_I2S_CHANNELS 8 // uses LCD parallel output not I2S
//#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_PLATFORM_ID 3 // used in UI to distinguish ESP type in UI, needs a proper fix!
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
#define WLED_MAX_RMT_CHANNELS 2 // ESP32-P4 uses the shared RMT driver
#define WLED_MAX_I2S_CHANNELS 0 // I2S LED output is not supported by NeoPixelBus on P4
#define WLED_PLATFORM_ID 42 // used in UI to distinguish ESP type in UI
#else
#define WLED_MAX_RMT_CHANNELS 8 // ESP32 has 8 RMT output channels
#define WLED_MAX_I2S_CHANNELS 8 // I2S parallel output supported by NPB
//#define WLED_MAX_ANALOG_CHANNELS 16
#define WLED_PLATFORM_ID 4 // used in UI to distinguish ESP type in UI, needs a proper fix!
#endif
#define WLED_MAX_TIMERS 64 // maximum number of timers
#define WLED_MAX_DIGITAL_CHANNELS (WLED_MAX_RMT_CHANNELS + WLED_MAX_I2S_CHANNELS)
#ifndef WLED_MAX_DIGITAL_CHANNELS
#define WLED_MAX_DIGITAL_CHANNELS (WLED_MAX_RMT_CHANNELS + WLED_MAX_I2S_CHANNELS)
#else
#warning "buildenv overrides WLED_MAX_DIGITAL_CHANNELS - please check that the value is correct"
#endif
#endif
// WLED_MAX_BUSSES was used to define the size of busses[] array which is no longer needed
// instead it will help determine max number of buses that can be defined at compile time
#ifdef WLED_MAX_BUSSES
#undef WLED_MAX_BUSSES
#endif
#define WLED_MAX_BUSSES (WLED_MAX_DIGITAL_CHANNELS+WLED_MAX_ANALOG_CHANNELS)
// define -> constexpr to align with definition of WLED_MAX_ANALOG_CHANNELS
constexpr size_t WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHANNELS;
static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");

// Maximum number of pins per output. 5 for RGBCCT analog LEDs.
Expand Down Expand Up @@ -395,7 +409,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#define BTN_TYPE_TOUCH_SWITCH 9

//Ethernet board types
#define WLED_NUM_ETH_TYPES 16
#define WLED_NUM_ETH_TYPES 17


#define WLED_ETH_NONE 0
Expand All @@ -414,7 +428,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#define WLED_ETH_GLEDOPTO 13
#define WLED_ETH_QUINLED_V4_UNOQUAD 14
#define WLED_ETH_QUINLED_V4_OCTA 15

#define WLED_ETH_ESP32P4_ETH 16

//Hue error codes
#define HUE_ERROR_INACTIVE 0
Expand Down Expand Up @@ -537,7 +551,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_LEDS 2048 //due to memory constraints S2
#else
#define MAX_LEDS 16384
#define MAX_LEDS 16384 // classic esp32, S3 and P4 can take more
#endif
#endif

Expand All @@ -552,7 +566,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#else
#define MAX_LED_MEMORY (48*1024) // with PSRAM there is more wiggle room as buffers get moved to PSRAM when needed (prioritize functionality over speed)
#endif
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
#elif defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4)
#define MAX_LED_MEMORY (192*1024) // S3 has ~330k of free heap after boot
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
#define MAX_LED_MEMORY (100*1024) // C3 has ~240k of free heap after boot, even with 8000 LEDs configured (2D) there is 30k of contiguous heap left
Expand Down Expand Up @@ -670,7 +684,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#endif

// Defaults pins, type and counts to configure LED output
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3)
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32P4)
#ifdef WLED_ENABLE_DMX
#define DEFAULT_LED_PIN 1
#warning "Compiling with DMX. The default LED pin has been changed to pin 1."
Expand Down
1 change: 1 addition & 0 deletions wled00/data/settings_wifi.htm
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ <h3>Ethernet Type</h3>
<option value="8">QuinLED Octa & T-ETH-POE</option>
<option value="14">QuinLED v4 Uno/Quad</option>
<option value="15">QuinLED v4 Octa</option>
<option value="16">Waveshare ESP32-P4-ETH/POE</option>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we need a better solution for this - mixing esp32 network modes with P4 network modes just doesn't make sense from architecture perspective.

<option value="10">Serg74-ETH32</option>
<option value="5">TwilightLord-ESP32</option>
<option value="3">WESP32</option>
Expand Down
Loading