Skip to content

Add experimental ESP32-C5 support (WiFi 6 dual-band)#5355

Merged
softhack007 merged 7 commits intowled:V5-C6from
jonny190:ESP32-C5-V5
Feb 8, 2026
Merged

Add experimental ESP32-C5 support (WiFi 6 dual-band)#5355
softhack007 merged 7 commits intowled:V5-C6from
jonny190:ESP32-C5-V5

Conversation

@jonny190
Copy link

@jonny190 jonny190 commented Feb 7, 2026

Summary

  • Adds ESP32-C5 support to WLED for WiFi 6 dual-band (2.4GHz + 5GHz) capability
  • ESP32-C5 is RISC-V based, treated similarly to C3 (2 RMT channels, no parallel I2S for LEDs)
  • Modifies 25 files with CONFIG_IDF_TARGET_ESP32C5 conditionals alongside existing C3/C6 checks
  • Uses pioarduino platform since Tasmota framework doesn't include C5 Arduino libs yet

Key Changes

  • platformio.ini: New [esp32c5] and [env:esp32c5dev] build sections modeled after C6
  • bus_manager.cpp: LEDC register fix for IDF 5.5.0 (duty_init vs duty on C5)
  • mbedtls_sha1_shim.cpp: Guard for IDF 5.5.0+ where SHA1 is built-in
  • esp_dmx: Excluded via lib_ignore (doesn't support C5 UART registers)
  • OTA/Web UI: C5 chip identification (ID: 0x0017)

Hardware Test Results

  • Board: ESP32-C5-DevKitC-1 (4MB flash, no PSRAM)
  • Build: Compiles successfully (89.3% flash, 27.6% RAM)
  • Boot: Device boots and prints startup message
  • WiFi AP: WLED-AP visible and functional
  • RMT LED output: Shows rmt_tx_wait_all_done: flush timeout - the IDF 5.x RMT driver_ng API works without the legacy CONFLICT crash, but transmissions time out. This likely needs NeoPixelBus upstream C5 support. No LED strip was connected during testing.

Known Limitations

  • Experimental: Requires pioarduino community platform (not Tasmota)
  • RMT flush timeouts: LED output needs NeoPixelBus C5 support (non-fatal, device runs fine)
  • esp_dmx disabled: C5 UART register layout differs from other ESP32 variants
  • IDF 5.5.0: pioarduino uses newer IDF than Tasmota (5.5.0 vs 5.3.3), requiring duty_init and SHA1 shim fixes

Relation to PR #5354

This PR supersedes #5354 (which targeted main). Per @softhack007's feedback, this PR targets V5-C6 and follows the V5-C6 build structure.

Test plan

  • Firmware compiles for esp32c5dev environment
  • Firmware flashes to ESP32-C5-DevKitC-1 via esptool
  • Device boots without crash
  • WLED-AP WiFi network appears
  • LED strip output (needs NeoPixelBus C5 RMT support or bit-bang fallback)
  • 5GHz WiFi connection test
  • Web UI functionality test via WLED-AP

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for ESP32-C5 microcontroller boards.
    • WiFi band information (2.4GHz/5GHz) now displayed in device information.
  • Chores

    • Extended platform configuration and compatibility adjustments for new hardware variants.

ESP32-C5 is a RISC-V based SoC with WiFi 6 (2.4GHz + 5GHz) support.
Treats C5 similarly to C3 for hardware capabilities (2 RMT channels,
no parallel I2S for LEDs, similar GPIO restrictions).

Changes across 25 files:
- Add CONFIG_IDF_TARGET_ESP32C5 conditionals alongside C3/C6 checks
- Add [esp32c5] and [env:esp32c5dev] build environments in platformio.ini
- Uses pioarduino platform (Tasmota framework lacks C5 Arduino libs)
- Exclude esp_dmx library (doesn't support C5 UART registers yet)
- Fix LEDC register access for IDF 5.5.0 (duty_init vs duty)
- Guard mbedtls_sha1_shim for IDF 5.5.0 (SHA1 built-in)
- Add C5 chip identification (ID: 0x0017) in OTA and web UI

Known limitations:
- RMT LED output shows flush timeouts (needs NeoPixelBus C5 support)
- WiFi AP confirmed working on hardware
- esp_dmx/DMX input disabled
- Experimental: requires pioarduino community platform

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@netmindz
Copy link
Member

netmindz commented Feb 7, 2026

As a general comment, I wonder if there is a better way of handling the ever growing list of CONFIG_IDF_TARGET_ESP32* ifdefs, can we invert or define our own values that relate more to the specific feature we are testing for rather than the hardware @softhack007 @willmmiles

Ifdef WLED_HW_FEATURE_X

Rather than say a long list of or conditions for when we say want to know about Hardware serial support or I2S count?

@softhack007
Copy link
Member

CONFIG_IDF_TARGET_ESP32* ifdefs, can we invert or define our own values that relate more to the specific feature we are testing for rather than the hardware @softhack007 @willmmiles

Ifdef WLED_HW_FEATURE_X

@netmindz you're right, I think we have to go that way - maybe with exception of pinmager.cpp and const.h/wled.h which are meant for board specific parameters.

Copy link
Contributor

@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: 3

Caution

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

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

156-161: ⚠️ Potential issue | 🔴 Critical

Add explicit compile-time guard to prevent DMX on ESP32-C5.

The code routes ESP32-C5 to ESPDMX (lines 156-161, 471-476), but the esp_dmx library is explicitly excluded for C5 due to UART register incompatibility (confirmed in PR commit message). When WLED_ENABLE_DMX is enabled, this will fail at runtime. Add a compile-time #error to prevent silent failures:

Example guards
 `#ifdef` WLED_ENABLE_DMX
- `#if` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S2)
+ `#if` defined(CONFIG_IDF_TARGET_ESP32C5)
+  `#error` "ESP32-C5: DMX output is not supported (esp_dmx excluded)"
+ `#elif` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S2)
   `#include` "src/dependencies/dmx/ESPDMX.h"
  `#else` //ESP32
   `#include` "src/dependencies/dmx/SparkFunDMX.h"
  `#endif`
 `#endif`

Also applies to wled.h lines 471-476 and dmx_output.cpp.

🤖 Fix all issues with AI agents
In `@wled00/util.cpp`:
- Around line 635-638: The preprocessor error message inside the BOARD_HAS_PSRAM
guard does not list all excluded targets (it checks IDF_TARGET_ESP32C3,
IDF_TARGET_ESP32C5, IDF_TARGET_ESP32C6, IDF_TARGET_ESP32C61 and ESP8266) but the
error string only mentions C3/C5/ESP8266; update the error text used in the
`#error` directive to include C6 and C61 as well so it accurately reflects the
checked symbols (e.g., change the message referenced in the `#error` line within
the BOARD_HAS_PSRAM conditional).

In `@wled00/wled.cpp`:
- Around line 699-735: The blocking 20s loop in initConnection() (using c5start,
WiFi.status(), esp_wifi_connect()) must be replaced with a non-blocking state
and timeout: in initConnection() start the 5GHz connect (esp_wifi_connect()),
record a timestamp (e.g., c5ConnectStartMs) and a connection state flag (e.g.,
c5Connecting = true) alongside selectedWiFi/multiWiFi context, then return
immediately; in handleConnection() poll WiFi.status() and check millis() against
c5ConnectStartMs to decide success or timeout (e.g., 20s), and on timeout
perform the fallback logic previously in the loop. Ensure you clear c5Connecting
and update state on success or fallback so watchdog/UI remain responsive.
- Around line 706-724: The code hardcodes country and auth mode and uses
deprecated esp_wifi_set_country; replace the static setup in the block using
wifi_country_t and esp_wifi_set_country by calling esp_wifi_set_country_code()
(or a configurable country string) and, if you must use wifi_country_t, populate
its wifi_5g_channel_mask for 5GHz instead of schan/nchan; do not hard-code
"US"—read the country from settings/region detection. For auth mode, stop
forcing wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; instead set
wifi_config.sta.threshold.authmode to WIFI_AUTH_OPEN when
multiWiFi[selectedWiFi].clientPass is empty (or choose the appropriate enum
based on the configured security type) so open networks can be used. Also remove
or make conditional the WiFi.setBandMode(WIFI_BAND_MODE_5G_ONLY) call (use a
user/config flag) and ensure you call esp_wifi_set_config(WIFI_IF_STA,
&wifi_config) after these configurable changes; update references:
wifi_country_t, esp_wifi_set_country/esp_wifi_set_country_code,
wifi_5g_channel_mask, wifi_config.sta.threshold.authmode,
multiWiFi[selectedWiFi].clientPass, and WiFi.setBandMode.
🧹 Nitpick comments (4)
wled00/ota_update.cpp (1)

438-444: LGTM! Chip ID verified — follows the established pattern for unsupported-yet targets.

The ESP32-C5 handling with chip ID 0x0017 is correct and consistent with other unsupported targets (C3, S3, C6). The code validates the chip ID and returns a clear "not supported yet" message.

Update the documentation comment block at lines 406–412 to include ESP32-C5 for completeness:

Documentation comment update
   // 0x000C = ESP32-C2
   // 0x000D = ESP32-C6
   // 0x0010 = ESP32-H2
+  // 0x0017 = ESP32-C5
wled00/json.cpp (1)

799-801: Replace the magic channel threshold with a named constant.
Using a named constant (e.g., WIFI_5GHZ_MIN_CHANNEL) makes the intent clearer and easier to reuse.

♻️ Proposed refinement
-  int wifiChannel = WiFi.channel();
-  wifi_info[F("channel")] = wifiChannel;
-  wifi_info[F("band")] = (wifiChannel >= 36) ? F("5GHz") : F("2.4GHz");
+  constexpr int WIFI_5GHZ_MIN_CHANNEL = 36;
+  int wifiChannel = WiFi.channel();
+  wifi_info[F("channel")] = wifiChannel;
+  wifi_info[F("band")] = (wifiChannel >= WIFI_5GHZ_MIN_CHANNEL) ? F("5GHz") : F("2.4GHz");
Based on learnings: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.
platformio.ini (1)

457-505: Pin the pioarduino platform to a specific release version for consistency and reproducibility.

The stable reference can change over time. All other platforms in platformio.ini use explicit version pinning (e.g., Tasmota's 2025.08.30, ESP32-IDF-V5's 2025.08.30). Consider pinning to a specific pioarduino release once validated.

usermods/audioreactive/audio_reactive.cpp (1)

675-677: Consider capability helper macros to reduce repeated target-guard chains.

These long CONFIG_IDF_TARGET_* chains repeat in multiple places (config/UI/setup). Defining WLED_AR_HAS_ANALOG_MIC / WLED_AR_HAS_PDM_MIC would make intent clearer and reduce future edits.

♻️ Proposed refactor
+// Capability helpers (place near top-level ESP32 config)
+#if defined(ARDUINO_ARCH_ESP32)
+  `#if` !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+    `#define` WLED_AR_HAS_ANALOG_MIC 1
+  `#else`
+    `#define` WLED_AR_HAS_ANALOG_MIC 0
+  `#endif`
+  `#if` !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5)
+    `#define` WLED_AR_HAS_PDM_MIC 1
+  `#else`
+    `#define` WLED_AR_HAS_PDM_MIC 0
+  `#endif`
+#endif
-#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+#if defined(ARDUINO_ARCH_ESP32) && WLED_AR_HAS_ANALOG_MIC

wled00/wled.cpp Outdated
Comment on lines 706 to 724
// Set country code to enable all 5GHz channels
wifi_country_t country = {
.cc = "US",
.schan = 1,
.nchan = 14,
.max_tx_power = 80,
.policy = WIFI_COUNTRY_POLICY_MANUAL
};
esp_wifi_set_country(&country);
WiFi.setBandMode(WIFI_BAND_MODE_5G_ONLY);
DEBUG_PRINTLN(F("ESP32-C5: Band mode set to 5GHz ONLY"));

wifi_config_t wifi_config = {};
strncpy((char*)wifi_config.sta.ssid, multiWiFi[selectedWiFi].clientSSID, sizeof(wifi_config.sta.ssid) - 1);
strncpy((char*)wifi_config.sta.password, multiWiFi[selectedWiFi].clientPass, sizeof(wifi_config.sta.password) - 1);
wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL;
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 7, 2026

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, check the file and context around lines 706-724
head -750 wled00/wled.cpp | tail -50

Repository: wled/WLED

Length of output: 2056


🏁 Script executed:

# Search for other country code configurations in the codebase
rg -i "country|wifi_country" wled00/ -B 2 -A 2

Repository: wled/WLED

Length of output: 644


🏁 Script executed:

# Search for authmode configurations
rg -i "authmode" wled00/ -B 2 -A 2

Repository: wled/WLED

Length of output: 387


🏁 Script executed:

# Check if there's any existing WiFi configuration flexibility
rg -i "clientSSID|clientPass" wled00/ -B 3 -A 3 | head -100

Repository: wled/WLED

Length of output: 5884


🌐 Web query:

ESP-IDF 5.5 ESP32-C5 WiFi country code configuration best practices and authmode handling for open vs secured networks

💡 Result:

Country code / regulatory domain (ESP-IDF 5.5, ESP32-C5)

Prefer esp_wifi_set_country_code() over esp_wifi_set_country(). Espressif explicitly discourages calling esp_wifi_set_country() because it “doesn’t validate the per-country rules” and recommends using esp_wifi_set_country_code instead. [1]

Choose the ieee80211d_enabled behavior intentionally:

  • ieee80211d_enabled = true (default): if the AP you connect to advertises a different country, the STA will use the AP’s country info; on disconnect it reverts to the configured country. This can also update the country IE your SoftAP beacons/probe responses advertise (in STA+SoftAP). The setting is stored in flash, and changing it switches PHY init data to match the country. [2]
  • ieee80211d_enabled = false: always use your configured country. [2]

Best-practice pattern

  • Set the country once during “Wi-Fi configuration phase” (before esp_wifi_start()) to avoid reconnect churn. Espressif notes that changing Wi‑Fi configuration APIs can cause reconnects and is best done early if it won’t change later. [3]
  • If you ship a product, avoid “world safe mode” unless you truly need it: default is "01" (world safe mode). [2]

Minimal example:

ESP_ERROR_CHECK(esp_wifi_set_country_code("US", true));   // or false if you must pin it

(Use your actual deployment country code; supported codes are listed in the API docs.) [2]


Authmode handling: open vs secured networks (STA)

1) Use wifi_sta_config_t.threshold.authmode to prevent unwanted downgrades

ESP-IDF’s fast-scan threshold lets you require that candidates be at least as secure as a chosen authmode. When set, only APs with authmode “more secure than the selected auth mode” (and RSSI above threshold) will be used. [4]

Practical mapping:

  • You want to allow open networks: set
    • password = "" (empty)
    • threshold.authmode = WIFI_AUTH_OPEN
  • You want to require WPA2 or better: set
    • threshold.authmode = WIFI_AUTH_WPA2_PSK (or stronger)

This is a key mitigation against accidentally joining an “evil twin” open AP with the same SSID.

2) Don’t rely on implicit defaults

ESP-IDF notes: if threshold.authmode is not set and your password looks like WPA2 (len ≥ 8), it defaults to WPA2, and the device won’t connect to deprecated WEP/WPA networks unless you explicitly lower the threshold. [5]

3) If you support WPA3, set PMF consciously

ESP-IDF 5.5 documents PMF as Optional/Required/Disabled, with default “PMF Optional”; “PMF Required” forces connecting only to PMF-capable APs. WPA3 mandates PMF, and ESP-IDF calls this out in the WPA3 guidance. [6]


Recommended connect snippet (open vs secured)

wifi_config_t cfg = { 0 };
strlcpy((char*)cfg.sta.ssid, ssid, sizeof(cfg.sta.ssid));

if (is_open) {
    cfg.sta.password[0] = 0;
    cfg.sta.threshold.authmode = WIFI_AUTH_OPEN;
} else {
    strlcpy((char*)cfg.sta.password, pass, sizeof(cfg.sta.password));
    cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; // or WIFI_AUTH_WPA3_PSK if you require it
    // cfg.sta.pmf_cfg.required = true; // if you want to require PMF
}

ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &cfg));
ESP_ERROR_CHECK(esp_wifi_connect());

Sources

[1] ESP-IDF v5.5.2 ESP32-C5 esp_wifi_set_country() docs (discouraged; use esp_wifi_set_country_code) (docs.espressif.com)
[2] ESP-IDF v5.5.2 ESP32-C5 esp_wifi_set_country_code() docs (11d behavior, flash storage, defaults, supported codes, PHY switch) (docs.espressif.com)
[3] ESP-IDF v5.5.2 ESP32-C5 Wi‑Fi guide (configure early; config changes may reconnect) (docs.espressif.com)
[4] ESP-IDF v5.5.2 ESP32-C5 wifi_sta_config_t threshold semantics (docs.espressif.com)
[5] ESP-IDF v5.5.2 ESP32-C5 wifi_scan_threshold_t.authmode note (implicit WPA2 default if password len ≥ 8) (docs.espressif.com)
[6] ESP-IDF v5.5.2 Wi‑Fi security guide (PMF modes; PMF required; WPA3/PMF relationship) (docs.espressif.com)

Citations:


🌐 Web query:

ESP32-C5 5GHz WiFi channel configuration schan nchan ESP-IDF documentation

💡 Result:

On ESP32-C5, wifi_country_t.schan and wifi_country_t.nchan apply only to the 2.4 GHz band (start channel + number of channels). For 5 GHz, ESP-IDF uses wifi_country_t.wifi_5g_channel_mask instead. [1][2]

What to use for 5 GHz channel configuration

  • 2.4 GHz allowed range: schan … (schan + nchan - 1) [1]
  • 5 GHz allowed channels: wifi_5g_channel_mask = bitmask of allowed 5 GHz channels; the bit mapping is defined by wifi_5g_channel_bit_t (channels like 36, 40, 44, 48, 52… up to 177 are enumerated there). [3][4]
  • The configured 5 GHz mask only takes effect when policy == WIFI_COUNTRY_POLICY_MANUAL. [2]
  • If wifi_5g_channel_mask == 0, 5 GHz channels are allowed according to local regulatory rules (per ESP-IDF). [2]

Ensure the radio is allowed to use 5 GHz

Set the band mode with esp_wifi_set_band_mode():

  • WIFI_BAND_MODE_5G_ONLY (5 GHz only) or
  • WIFI_BAND_MODE_AUTO (2.4/5 GHz automatic selection; not simultaneous dual-band). [5]

Minimal example (manual policy + restrict 5 GHz scan/operation)

wifi_country_t c = {
  .cc = "US",
  .schan = 1,
  .nchan = 11,
  .policy = WIFI_COUNTRY_POLICY_MANUAL,
  .wifi_5g_channel_mask = (1U << WIFI_CHANNEL_36) | (1U << WIFI_CHANNEL_40),
};
esp_wifi_set_country(&c);
esp_wifi_set_band_mode(WIFI_BAND_MODE_5G_ONLY);

(Use wifi_5g_channel_bit_t enumerators for the channels you want in the mask.) [2][4][5]

Notes on scanning behavior: with WIFI_COUNTRY_POLICY_MANUAL, ESP-IDF scans 2.4 GHz using schan/nchan and scans 5 GHz based on wifi_5g_channel_mask. [3]

References:
[1] ESP-IDF ESP32-C5 wifi_country_t fields (schan, nchan) (v5.4 API reference) (docs.espressif.com)
[2] ESP-IDF ESP32-C5 wifi_5g_channel_mask semantics and manual-policy requirement (stable API reference) (docs.espressif.com)
[3] ESP32-C5 Wi-Fi driver overview: wifi_5g_channel_mask, default example, and scan behavior notes (docs.espressif.com)
[4] wifi_5g_channel_bit_t channel enumerators (36…177) (stable API reference) (docs.espressif.com)
[5] ESP32-C5 band mode configuration via esp_wifi_set_band_mode() (docs.espressif.com)

Citations:


Hard-coded country and authmode prevent configuration flexibility and open networks; country API is deprecated.

Lines 706–724 hard-code country code "US" and WIFI_AUTH_WPA2_PSK, creating two problems:

  1. Country code: Setting a fixed country code violates local regulatory settings outside the US. Additionally, esp_wifi_set_country() is discouraged by Espressif; use esp_wifi_set_country_code() instead.

  2. Authmode: Forcing WIFI_AUTH_WPA2_PSK prevents connecting to open networks (or other auth modes) that users may configure. Set authmode based on whether a password is provided.

  3. 5 GHz channels: The comment claims "enable all 5GHz channels," but schan=1, nchan=14 configures 2.4 GHz channels only. For 5 GHz, wifi_country_t.wifi_5g_channel_mask must be used instead.

🔧 Example authmode guard
-    wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
+    wifi_config.sta.threshold.authmode =
+      (strlen(multiWiFi[selectedWiFi].clientPass) > 0) ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN;

Consider also using esp_wifi_set_country_code() and allowing country configuration (via settings or region detection) rather than hard-coding.

🤖 Prompt for AI Agents
In `@wled00/wled.cpp` around lines 706 - 724, The code hardcodes country and auth
mode and uses deprecated esp_wifi_set_country; replace the static setup in the
block using wifi_country_t and esp_wifi_set_country by calling
esp_wifi_set_country_code() (or a configurable country string) and, if you must
use wifi_country_t, populate its wifi_5g_channel_mask for 5GHz instead of
schan/nchan; do not hard-code "US"—read the country from settings/region
detection. For auth mode, stop forcing wifi_config.sta.threshold.authmode =
WIFI_AUTH_WPA2_PSK; instead set wifi_config.sta.threshold.authmode to
WIFI_AUTH_OPEN when multiWiFi[selectedWiFi].clientPass is empty (or choose the
appropriate enum based on the configured security type) so open networks can be
used. Also remove or make conditional the
WiFi.setBandMode(WIFI_BAND_MODE_5G_ONLY) call (use a user/config flag) and
ensure you call esp_wifi_set_config(WIFI_IF_STA, &wifi_config) after these
configurable changes; update references: wifi_country_t,
esp_wifi_set_country/esp_wifi_set_country_code, wifi_5g_channel_mask,
wifi_config.sta.threshold.authmode, multiWiFi[selectedWiFi].clientPass, and
WiFi.setBandMode.

Copy link
Member

Choose a reason for hiding this comment

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

@troyhacks as you already managed to get the C5 connect to 5ghz networks - what would you recommend?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

@softhack007 softhack007 Feb 7, 2026

Choose a reason for hiding this comment

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

@@CodeRabbit is it possible to achieve the same with the Arduino-style WiFi or Network class?

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

🌐 Web query:

Arduino ESP32 WiFi library ESP32-C5 5GHz setBandMode setCountry authmode configuration

💡 Result:

On ESP32‑C5 (dual‑band 2.4/5 GHz), the “Arduino ESP32 WiFi library” (arduino‑esp32) generally doesn’t expose high-level helpers like WiFi.setBandMode() / WiFi.setCountry(); instead you configure band, country/regulatory domain, and auth mode via the underlying ESP‑IDF Wi‑Fi APIs (esp_wifi_*). The key pieces are:

1) Force / prefer 5 GHz: esp_wifi_set_band_mode()

ESP32‑C5 supports WIFI_BAND_MODE_2G_ONLY, WIFI_BAND_MODE_5G_ONLY, and WIFI_BAND_MODE_AUTO via esp_wifi_set_band_mode() (note: AUTO is not simultaneous dual-band; it’s selection). [1]

2) Country / regulatory domain: esp_wifi_set_country_code() (preferred) or esp_wifi_set_country()

ESP‑IDF discourages esp_wifi_set_country() (easy to misconfigure) and recommends esp_wifi_set_country_code(country, ieee80211d_enabled) instead. [2]
If you do use wifi_country_t, ESP32‑C5 adds wifi_5g_channel_mask (bitmask of allowed 5 GHz channels; 0 means “allowed per local rules”, and the mask applies when policy is manual). [3]

3) Auth mode configuration (authmode)

For SoftAP, wifi_ap_config_t.authmode selects AP auth (Open/WPA2/WPA3/etc.). ESP32‑C5 Wi‑Fi driver docs call out the AP config field authmode and that invalid values default to open. [4]
For Station, auth constraints are typically set via wifi_config_t.sta.threshold.authmode (a wifi_auth_mode_t). WPA3 also requires PMF settings; ESP‑IDF notes PMF is mandated by WPA3, and WPA3 SoftAP requires PMF required. [5]


Arduino-style sketch snippet (using ESP‑IDF APIs under Arduino)

`#include` <WiFi.h>
extern "C" {
  `#include` "esp_wifi.h"
}

void setup() {
  WiFi.mode(WIFI_MODE_STA);
  WiFi.begin("ssid", "pass");

  // 1) Force 5 GHz only (ESP32-C5)
  esp_wifi_set_band_mode(WIFI_BAND_MODE_5G_ONLY);

  // 2) Set regulatory country (preferred API in ESP-IDF)
  // esp_wifi_set_country_code("US", true);

  // (Optional) If you need manual control incl. 5 GHz channel mask:
  // wifi_country_t c = {};
  // memcpy(c.cc, "US", 2);
  // c.schan = 1; c.nchan = 11;
  // c.policy = WIFI_COUNTRY_POLICY_MANUAL;
  // c.wifi_5g_channel_mask = 0; // allow per local rules (ESP-IDF)
  // esp_wifi_set_country(&c);
}

void loop() {}

Practical notes

  • If esp_wifi_set_band_mode(WIFI_BAND_MODE_5G_ONLY) returns ESP_ERR_INVALID_ARG, your build/target likely doesn’t have 5 GHz enabled/support compiled in (ESP‑IDF requires 5 GHz capability support for non‑2.4G modes). [1]
  • For WPA3 (STA or SoftAP), ensure the relevant WPA3/SAE support is enabled in the underlying ESP‑IDF configuration used by your Arduino core, and configure PMF appropriately. [5]

Sources:
[1] ESP32‑C5 Wi‑Fi band mode (esp_wifi_set_band_mode, 2G/5G/AUTO) — ESP‑IDF docs. (docs.espressif.com)
[2] ESP‑IDF guidance recommending esp_wifi_set_country_code over esp_wifi_set_country — ESP‑IDF API reference. (docs.espressif.com)
[3] wifi_country_t on ESP32‑C5 including wifi_5g_channel_mask — ESP‑IDF API reference. (docs.espressif.com)
[4] AP config field authmode description — ESP32‑C5 Wi‑Fi driver docs. (docs.espressif.com)
[5] WPA3 requires PMF; WPA3 SoftAP needs PMF required and uses WIFI_AUTH_WPA3_PSK — ESP‑IDF Wi‑Fi security docs. (docs.espressif.com)

Citations:


@softhack007, unfortunately no – the Arduino ESP32 WiFi library doesn't expose high-level methods like WiFi.setBandMode() or WiFi.setCountry() for ESP32-C5. You must use the underlying ESP-IDF native APIs (esp_wifi_set_band_mode(), esp_wifi_set_country_code(), esp_wifi_set_config(), etc.) even in Arduino sketches.

The good news is that the code in the diff is already using the correct approach by mixing Arduino WiFi class calls with ESP-IDF APIs. This is the standard pattern for accessing advanced features not wrapped by Arduino.

Here's a typical Arduino-style setup for ESP32-C5:

`#include` <WiFi.h>
extern "C" {
  `#include` "esp_wifi.h"
}

void setup() {
  WiFi.mode(WIFI_MODE_STA);
  
  // Use ESP-IDF APIs for advanced configuration
  esp_wifi_set_band_mode(WIFI_BAND_MODE_5G_ONLY);  // Force 5GHz
  esp_wifi_set_country_code("US", true);            // Set country
  
  // Then use Arduino WiFi class for connection
  WiFi.begin("ssid", "password");
}

The issues I raised in my review still apply:

  1. Don't hardcode country – use esp_wifi_set_country_code() with a configurable country string
  2. Set authmode dynamically – check if password is empty to support open networks
  3. 5GHz channels – use wifi_5g_channel_mask in wifi_country_t for 5GHz, not schan/nchan (which only apply to 2.4GHz)

So the answer is: you're already using the right API approach, just make the configuration more flexible instead of hardcoded values.


🧠 Learnings used
Learnt from: blazoncek
Repo: wled/WLED PR: 4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.199Z
Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.

Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.cpp:40-46
Timestamp: 2025-08-29T00:22:34.115Z
Learning: For ESP32/ESP8266 HTTP requests in WLED, set timeouts on the underlying WiFiClient (using client.setTimeout()) before calling http_.begin(), rather than using HTTPClient::setTimeout(). This pattern is used successfully in the Klipper usermod to prevent connection hangs.

Learnt from: willmmiles
Repo: wled/WLED PR: 4890
File: lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h:31-36
Timestamp: 2025-09-02T01:45:58.047Z
Learning: For the NeoEsp32RmtHI driver, RISC-V ESP32-C3 support is currently disabled via bus_wrapper.h rather than compile-time guards, as the maintainer willmmiles is working on resolving underlying nested interrupt issues and prefers to centralize the workaround in one location.

Copy link
Member

Choose a reason for hiding this comment

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

@coderabbitai could we omit esp_wifi_set_country_code("US,..) ?

I think that all boards will come with some default country, and the wifi AP (wifi router) will any way provide its supported channel list to clients?

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

🌐 Web query:

ESP32-C5 ESP-IDF default country code IEEE 802.11d enabled behavior

💡 Result:

For ESP32‑C5 in ESP‑IDF (stable / v5.5.x docs), the Wi‑Fi driver defaults to:

  • Country code: "01" (world safe mode) (with {.cc="01", .schan=1, .nchan=11, .policy=WIFI_COUNTRY_POLICY_AUTO}) and the default for esp_wifi_set_country_code() is "01". [1]
  • IEEE 802.11d: enabled by default (ieee80211d_enabled = true). [1]

Behavior when 802.11d is enabled (default on ESP32‑C5):

  • The device (STA) will adopt the country information advertised by the AP it connects to (from the AP’s country info / 802.11d information), even if you configured a different country. [2]
  • When the STA disconnects, it reverts back to the station’s configured country. [2]
  • If the country changes (by configuration or due to connecting to an AP with different country), the SoftAP’s beacon/probe country IE is updated accordingly. [2]
  • If the current country is "01" (world safe mode), SoftAP mode will not include a country IE. [1]

(For comparison: on classic ESP32 docs, the default country is listed as "CN" with 802.11d enabled—ESP32‑C5 differs by defaulting to "01".) [3]

Sources
[1] ESP32‑C5 Wi‑Fi API (ESP‑IDF stable): default country “01”, 802.11d default TRUE, SoftAP country IE note
[2] ESP32‑C5 esp_wifi_set_country_code() docs: 802.11d enabled/disabled behavior, revert-on-disconnect, SoftAP IE updates, stored in flash
[3] ESP32 Wi‑Fi API (ESP‑IDF v4.4): default country “CN” and 802.11d default TRUE


@softhack007, you're correct about the 802.11d mechanism – when enabled (which is the default), the ESP32 STA will adopt the country information advertised by the AP it connects to.

However, there are important caveats:

  1. Default country is "01" (world safe mode) – not a specific country. This is very restrictive and may limit the initial WiFi scan before connection, especially for 5GHz channels.

  2. 802.11d only works after connection – During the initial scan to find available APs, the device uses its configured country. With "01", it may not scan all 5GHz channels your AP uses, potentially failing to find the AP at all.

  3. For ESP32-C5 with 5GHz-only mode (as in this PR), starting with world safe mode "01" is particularly problematic because many 5GHz channels won't be scanned initially.

Recommendation for this PR:

Since the code explicitly sets WIFI_BAND_MODE_5G_ONLY, you should configure a country code to ensure proper 5GHz channel scanning. The options are:

A. Make it configurable (best approach):

// Allow users to set their country in settings, default to "01"
const char* wifiCountry = strlen(wifiCountryCode) > 0 ? wifiCountryCode : "01";
esp_wifi_set_country_code(wifiCountry, true); // ieee80211d enabled

B. Use a reasonable default like "US" or "EU" for development, but document it clearly

C. Omit it entirely (simplest) – will work once connected via 802.11d, but initial scan will be restricted

For this specific 5GHz-focused PR, I'd recommend option A (configurable country) to ensure optimal 5GHz scanning, since 802.11d won't help if the device can't find the AP during the initial scan.


🧠 Learnings used
Learnt from: blazoncek
Repo: wled/WLED PR: 4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.199Z
Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.

Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.cpp:40-46
Timestamp: 2025-08-29T00:22:34.115Z
Learning: For ESP32/ESP8266 HTTP requests in WLED, set timeouts on the underlying WiFiClient (using client.setTimeout()) before calling http_.begin(), rather than using HTTPClient::setTimeout(). This pattern is used successfully in the Klipper usermod to prevent connection hangs.

Learnt from: ksedgwic
Repo: wled/WLED PR: 4919
File: usermods/usermod_v2_departstrip/readme.md:39-49
Timestamp: 2025-09-09T23:21:00.549Z
Learning: For the DepartStrip usermod on ESP8266/ESP32 devices, HTTP is preferred over HTTPS in documentation examples and templates due to heap memory constraints. The combined memory requirements of TLS handshake and JSON parsing for transit API responses can exceed available memory on these embedded devices.

Learnt from: willmmiles
Repo: wled/WLED PR: 4890
File: lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h:31-36
Timestamp: 2025-09-02T01:45:58.047Z
Learning: For the NeoEsp32RmtHI driver, RISC-V ESP32-C3 support is currently disabled via bus_wrapper.h rather than compile-time guards, as the maintainer willmmiles is working on resolving underlying nested interrupt issues and prefers to centralize the workaround in one location.

Learnt from: ksedgwic
Repo: wled/WLED PR: 4919
File: usermods/usermod_v2_departstrip/readme.md:15-23
Timestamp: 2025-09-09T23:20:44.936Z
Learning: For the DepartStrip usermod on ESP8266/ESP32 devices, HTTP is used instead of HTTPS for API endpoints due to heap memory constraints. HTTPS would require too much memory for the combined TLS handshake and JSON parsing operations on these embedded devices, making HTTP a necessary compromise despite security considerations.

@jonny190
Copy link
Author

jonny190 commented Feb 7, 2026

@netmindz @softhack007
Agreed, the growing list of CONFIG_IDF_TARGET_ESP32* conditionals is becoming unwieldy. Introducing feature-based defines like WLED_HW_FEATURE_I2S_PARALLEL, WLED_HW_FEATURE_RMT_CHANNELS, etc. set once in const.h based on the target would be much cleaner. Happy to help with that refactor as a follow-up if desired, but i feel it's a bigger change that probably deserves its own PR?

@softhack007
Copy link
Member

Happy to help with that refactor as a follow-up if desired, but i feel it's a bigger change that probably deserves its own PR?

I agree with you. This clean up is better in a separate PR.

@jonny190
Copy link
Author

jonny190 commented Feb 7, 2026

Suggested changes have been made and tested on my ESP32-C5 and show to be working at least in my limited testing

Copy link
Contributor

@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 (3)
wled00/dmx_output.cpp (1)

71-76: ⚠️ Potential issue | 🟡 Minor

Align preprocessor indentation with the 2‑space rule.

Line 72 (and the paired #else/#endif) uses a single leading space, which breaks the 2‑space indentation rule for .cpp files.

Suggested fix
- `#if` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2)
+  `#if` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2)
   dmx.init(512);        // initialize with bus length
- `#else`
+  `#else`
   dmx.initWrite(512);  // initialize with bus length
- `#endif`
+  `#endif`

As per coding guidelines, "wled00/**/*.cpp: Use 2-space indentation for C++ source files (.cpp)".

wled00/bus_wrapper.h (1)

499-509: ⚠️ Potential issue | 🟠 Major

Exclude ESP32‑C5 from the classic ESP32 channel remap.

Line 506 still applies the classic ESP32 “I2S1 first bus” channel decrement to C5 (because it isn’t excluded). That can collapse RMT channels (e.g., channel 1 → 0) and break multi‑bus output on C5.

🐛 Proposed fix
-#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
+#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5))
 // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
 if (!_useParallelI2S && channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
 `#endif`
wled00/wled.h (1)

156-161: ⚠️ Potential issue | 🟠 Major

Fix indentation and guard DMX on ESP32-C5.

The build system explicitly ignores esp_dmx for C5 (platformio.ini:478: "esp_dmx library doesn't support C5 UART registers yet"), so including C5 in the ESPDMX path will fail. Additionally, the indentation violates the 2-space requirement for C++ header files—the preprocessor directives should use 2-space indentation consistently.

🛠️ Suggested change
 `#ifdef` WLED_ENABLE_DMX
- `#if` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S2)
-  `#include` "src/dependencies/dmx/ESPDMX.h"
- `#else` //ESP32
-  `#include` "src/dependencies/dmx/SparkFunDMX.h"
- `#endif`
+  `#if` defined(CONFIG_IDF_TARGET_ESP32C5)
+    `#error` "DMX is not supported on ESP32-C5 yet"
+  `#elif` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S2)
+    `#include` "src/dependencies/dmx/ESPDMX.h"
+  `#else` //ESP32
+    `#include` "src/dependencies/dmx/SparkFunDMX.h"
+  `#endif`
 `#endif`
@@
 `#ifdef` WLED_ENABLE_DMX
- `#if` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S2)
-  WLED_GLOBAL DMXESPSerial dmx;
- `#else` //ESP32
-  WLED_GLOBAL SparkFunDMX dmx;
- `#endif`
+  `#if` defined(CONFIG_IDF_TARGET_ESP32C5)
+    `#error` "DMX is not supported on ESP32-C5 yet"
+  `#elif` defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S2)
+    WLED_GLOBAL DMXESPSerial dmx;
+  `#else` //ESP32
+    WLED_GLOBAL SparkFunDMX dmx;
+  `#endif`
 `#endif`

Also applies to lines 471–476.

🤖 Fix all issues with AI agents
In `@usermods/audioreactive/audio_reactive.cpp`:
- Around line 675-677: The current preprocessor check that defines static const
char _analogmic[] treats ESP32-C5 like other non-original ESP32 variants and
therefore disables ADC-via-I2S and PDM wholesale; update the conditionals so
only the ADC-via-I2S path remains excluded for non-original ESP32 targets while
allowing PDM on ESP32-C5 (but flagged as experimental). Concretely, adjust the
`#if/`#elif logic around the _analogmic declaration and any PDM-specific guards to
keep CONFIG_IDF_TARGET_ESP32 as the sole positive for ADC-via-I2S, while adding
a separate branch that enables PDM support for CONFIG_IDF_TARGET_ESP32C5 (e.g.,
define an experimental macro or comment) instead of lumping it with ESP32C3/S3;
reference the symbol _analogmic and any PDM enable guards in audio_reactive.cpp
to locate and change the conditionals.

In `@wled00/json.cpp`:
- Around line 799-801: The code currently sets wifi_info[F("band")] based solely
on wifiChannel from WiFi.channel(), which can be 0 when disconnected and thus
incorrectly emit "2.4GHz"; modify the logic around WiFi.channel() (variable
wifiChannel and the existing assignment to wifi_info[F("band")]) to only set the
band when the channel is valid (e.g., wifiChannel > 0 or when WiFi is
connected), otherwise omit the band field or set it to a sentinel like
null/empty so no incorrect "2.4GHz" is reported.
🧹 Nitpick comments (2)
wled00/wled.cpp (1)

876-878: Optional: name the 5 GHz cutoff instead of the raw 36.
This improves readability for future maintainers.

♻️ Suggested tweak
 `#if` defined(CONFIG_IDF_TARGET_ESP32C5)
-{ int32_t ch = WiFi.channel(); DEBUG_PRINTF_P(PSTR("WiFi channel: %d (%s)\n"), ch, (ch >= 36) ? "5GHz" : "2.4GHz"); }
+{
+  constexpr int kFirst5GHzChannel = 36;
+  int32_t ch = WiFi.channel();
+  DEBUG_PRINTF_P(PSTR("WiFi channel: %d (%s)\n"), ch, (ch >= kFirst5GHzChannel) ? "5GHz" : "2.4GHz");
+}
 `#endif`
Based on learnings "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."
platformio.ini (1)

457-479: Pin the pioarduino platform version and re‑validate inherited flags.

Using a moving “stable” URL can break reproducibility; consider pinning to a specific pioarduino release. Also please verify that inheriting ${esp32_idf_V5.build_flags} (e.g., WLED_DISABLE_MQTT / WLED_DISABLE_INFRARED) is still desired for the pioarduino‑based C5 toolchain.

Also applies to: 480-507

@softhack007
Copy link
Member

platformio.ini (1)

457-505: Pin the pioarduino platform to a specific release version for consistency and reproducibility.
The stable reference can change over time. All other platforms in platformio.ini use explicit version pinning (e.g., Tasmota's 2025.08.30, ESP32-IDF-V5's 2025.08.30). Consider pinning to a specific pioarduino release once validated.

@jonny190 this minor finding from the rabbit makes sense, please pin the platform version explicitly

@softhack007
Copy link
Member

wled00/bus_wrapper.h (1)

499-509: ⚠️ Potential issue | 🟠 Major
Exclude ESP32‑C5 from the classic ESP32 channel remap.
Line 506 still applies the classic ESP32 “I2S1 first bus” channel decrement to C5 (because it isn’t excluded). That can collapse RMT channels (e.g., channel 1 → 0) and break multi‑bus output on C5.

@jonny190 another one that should get fixed.

@Jerrys-modz
Copy link

I would love to help test. I have a few c5s laying around but I don’t see it building any bin files for the c5.

@softhack007
Copy link
Member

wled00/wled.cpp (1)

876-878: Optional: name the 5 GHz cutoff instead of the raw 36.
This improves readability for future maintainers

@jonny190 please check - wled actually has "technical debt" on magic numbers, it would be better to have named constants.

@softhack007
Copy link
Member

softhack007 commented Feb 7, 2026

I would love to help test. I have a few c5s laying around but I don’t see it building any bin files for the c5.

@Jerrys-modz very much appreciated ❤️
As this PR progresses very quickly, it should be part of the C6-V5 branch soon, and you should find bins in the CI builds for that branch after merge.

@jonny190 please add your C5 buildenv to the default list here

https://github.com/jonny190/WLED/blob/11f5be1861f96e92b24aac6040bbae9c8c552379/platformio.ini#L32

I will try to build your branch locally later tonight, as a final check before merge.

void ParticleSystem2D::applyFriction(PSparticle &part, const int32_t coefficient) {
// note: not checking if particle is dead can be done by caller (or can be omitted)
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ESP8266) // use bitshifts with rounding instead of division (2x faster)
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(ESP8266) // use bitshifts with rounding instead of division (2x faster)
Copy link
Member

Choose a reason for hiding this comment

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

Is there a build flag for RISC-V we can use here @DedeHai ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

there is such a flag, it is used in buswrapper for high priority RMT driver.

Copy link
Collaborator

@DedeHai DedeHai Feb 7, 2026

Choose a reason for hiding this comment

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

however, this is not about risc V per se but slow hardware division.
edit:see #4206

Copy link
Member

Choose a reason for hiding this comment

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

#ifdef ESP32

#if defined(__riscv) 
// for all riscv based chips: P4,C3, C5, C6
#endif

#if defined(__XTENSA__)
// for S3, S2, and classic esp32
#endif

#endif

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@jonny190
Copy link
Author

jonny190 commented Feb 7, 2026

I've pushed the suggested changes to hopefully tie things up

@jonny190
Copy link
Author

jonny190 commented Feb 7, 2026

The CI build for esp32c5dev was failing because the upstream NeoPixelBus (pinned at netmindz/NeoPixelBus#2f05279) doesn't include ESP32-C5 in the exclusion guard for its legacy I2S driver (Esp32_i2s.c). This file includes soc/rtc_cntl_reg.h which doesn't exist on C5, causing a fatal compile error.

This only showed up in CI because local builds had a cached .o file from a previously patched copy. CI downloads a fresh library each time and hits the missing header.

Fix: Added a PlatformIO pre-build script (pio-scripts/patch_neopixelbus_c5.py) that automatically patches the NeoPixelBus Esp32_i2s.c file after download, adding !defined(CONFIG_IDF_TARGET_ESP32C5) to the I2S exclusion guard. This is the same approach used elsewhere in PlatformIO projects to handle upstream library incompatibilities without forking.

The script only runs for C5 builds and is idempotent (skips if already patched). Once upstream NeoPixelBus adds native C5 support, the script can be removed.

Can I push this fix to the branch?

@netmindz
Copy link
Member

netmindz commented Feb 7, 2026

Rather than pio-scripts/patch_neopixelbus_c5.py - please update to point to a fork of NeoPixelBus that has the right bits we need, so this can properly then be merged upstream, hacking the source of a library is really nasty @jonny190

Replace upstream NeoPixelBus with jonny190/NeoPixelBus fork (d4bbe3f)
which adds C5 to the I2S exclusion guard and fixes the bit-bang LED
output method (cycle counter CSR 0xC00, GPIO register types).

Spell out C5 lib_deps explicitly to avoid pulling both the upstream
and fork NeoPixelBus, which causes duplicate compilation errors.

Upstream PR: netmindz/NeoPixelBus#1
@jonny190
Copy link
Author

jonny190 commented Feb 7, 2026

PR's are in for both

@softhack007
Copy link
Member

softhack007 commented Feb 7, 2026

@netmindz initially my idea was to postpone solving these extra long #ifdef chains for later.
But I like the idea of relying on SOC_CPU_... , __riscv__ , __XTENSA__ .
Maybe we address a few of the cases here so ideas don't get lost. But no need to solve everything in this one PR.

- Fix WiFi.macAddress() returning all zeros on ESP32-C5 by falling back
  to esp_read_mac() when WiFi netif is not yet created
- Update NeoPixelBus fork to latest with channel ctor overload fix
- Enable WLED_DEBUG, remove WLED_DISABLE_WEBSOCKETS for C5 build
@jonny190
Copy link
Author

jonny190 commented Feb 8, 2026

Latest push (1a025f5):

MAC address fix (wled00/wled.cpp):
On ESP32-C5, WiFi.macAddress() returns all zeros (000000000000) because it's called at line 442 before WiFi.mode(WIFI_STA) at line 481. At that point the WiFi netif hasn't been created yet, so esp_netif_get_mac() fails internally — and Arduino ESP32 v3.x has no fallback. Added a guard that detects the all-zeros case and falls back to esp_read_mac(mac, ESP_MAC_WIFI_STA) to read the base MAC directly from eFuse. This is wrapped in #ifdef ARDUINO_ARCH_ESP32 so it only applies to ESP32 targets.

NeoPixelBus fork update (platformio.ini):
Updated the NeoPixelBus fork pin from d4bbe3f to 103cfb5, which includes a channel constructor overload fix needed for C5 builds.

Build flags (platformio.ini):

  • Enabled WLED_DEBUG for the C5 build to aid testing/development
  • Removed WLED_DISABLE_WEBSOCKETS — websockets work fine on C5

Both changes have been tested on hardware with confirmed WiFi 6 connectivity on 5GHz (channel 44).

@softhack007
Copy link
Member

@jonny190 thanks 👍
My "it compiles" check also was successful, so I'll merge your PR back into C6-V5.

@softhack007 softhack007 merged commit f0acc62 into wled:V5-C6 Feb 8, 2026
11 of 235 checks passed
@netmindz
Copy link
Member

netmindz commented Feb 8, 2026

@netmindz initially my idea was to postpone solving these extra long #ifdef chains for later. But I like the idea of relying on SOC_CPU_... , __riscv__ , __XTENSA__ . Maybe we address a few of the cases here so ideas don't get lost. But no need to solve everything in this one PR.

I've messaged @willmmiles on discord and he likes the idea too.

In terms of what we do here, yeah the choice would be too either

  • "phase in" by using this new style here for the required changes
  • create a new PR that cleans up all the legacy then merge that into this one

@jonny190
Copy link
Author

jonny190 commented Feb 8, 2026

@netmindz @softhack007 @willmmiles Agreed - a separate cleanup PR is the way to go. This PR is scoped to C5 support and is ready to merge, so bolting a refactor onto it now would just risk delays and regressions.

For the follow-up PR I'm thinking something like:

  • Define feature macros in const.h based on target (e.g. WLED_HW_HAS_I2S_PARALLEL, WLED_HW_RMT_CHANNELS, WLED_HW_IS_SINGLE_CORE)
  • Could also lean on existing IDF/arch macros where they already express what we need (SOC_CPU_CORES_NUM, __riscv__, __XTENSA__)
  • Then sweep through bus_wrapper.h, FX.h, wled.cpp, etc. replacing the long CONFIG_IDF_TARGET_* chains

Happy to take that on once this one lands. Should make adding future targets (C61, H4, etc.) much less painful.

@jonny190
Copy link
Author

jonny190 commented Feb 8, 2026

The esp32c5dev CI build is failing with:

NeoPixelBus@src-.../internal/methods/ESP/ESP32/Core_2_x/Esp32_i2s.c:44:
fatal error: soc/rtc_cntl_reg.h: No such file or directory

Root cause: PlatformIO always merges the root [env] lib_deps into every environment. Line 170 of platformio.ini pulls in netmindz/NeoPixelBus.git#2f05279 globally. The [esp32c5] section adds the C5-patched fork (jonny190/NeoPixelBus.git#103cfb5), but it can't override the global entry. So PIO downloads both copies - the CI dependency graph confirms this:

|-- NeoPixelBus @ 2.9.0+sha.2f05279    <-- upstream (from global [env] lib_deps)
|-- NeoPixelBus @ 2.9.0+sha.103cfb5    <-- C5-patched fork

The upstream copy gets compiled for C5 and hits the missing soc/rtc_cntl_reg.h header (which doesn't exist on C5). Local builds didn't catch this because PIO had a cached .o from a previously patched copy.

Proposed fix: Add NeoPixelBus to lib_ignore in the [esp32c5] section. The fork installed via git URL gets an internal name of NeoPixelBus@src-<hash>, which PIO treats differently from the plain library name, so the ignore filter should only block the upstream copy.

lib_ignore = ${esp32_idf_V5.lib_ignore}
  esp_dmx
  NeoPixelBus  ;; ignore upstream; C5-patched fork uses NeoPixelBus@src-* name

I'll test this locally and push if it works.

@jonny190
Copy link
Author

jonny190 commented Feb 8, 2026

@netmindz The esp32c5dev CI failure is because PlatformIO always merges the global [env] lib_deps into every environment - so the upstream NeoPixelBus (netmindz/NeoPixelBus#2f05279) gets pulled in alongside the C5-patched fork, and the upstream copy fails to compile on C5 due to the missing soc/rtc_cntl_reg.h in Esp32_i2s.c.

I tried using lib_ignore = NeoPixelBus but that blocks both copies since they share the same library name.

The cleanest fix would be merging the C5 support PR into your NeoPixelBus fork (netmindz/NeoPixelBus#1), then updating the global commit hash on line 170 of platformio.ini. That way there's only one NeoPixelBus reference and it works for all targets including C5 - no separate fork needed.

Would you be able to take a look at that PR?

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.

5 participants