From 31c3c09cde84e60557d1b31c4c0c8b18c7812de0 Mon Sep 17 00:00:00 2001 From: Jonny Davey Date: Fri, 6 Feb 2026 20:49:39 +0000 Subject: [PATCH] Add experimental ESP32-C5 support with WiFi 6 dual-band - Add CONFIG_IDF_TARGET_ESP32C5 conditionals alongside ESP32-C3 - Use bit-bang LED output (RMT legacy API conflicts with IDF 5.x) - Add IDF 5.x API compatibility (LEDC, mbedtls, ADC) - Fix Network namespace collision with Arduino-ESP32 v3.x - Prefer 5GHz WiFi connection for hidden SSIDs - Add WiFi band indicator to info API and UI - Exclude C5 from I2S parallel output, touch sensors, analog mic - Add chip ID verification for OTA updates ESP32-C5 features: WiFi 6 dual-band (2.4GHz + 5GHz), RISC-V single core, 240MHz, 2 RMT channels. Requires pioarduino platform fork. Co-Authored-By: Claude Opus 4.5 --- .../include/NeoEsp32RmtHIMethod.h | 8 +- lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp | 11 ++- usermods/audioreactive/audio_reactive.cpp | 32 ++++---- wled00/FX.h | 8 +- wled00/FX_fcn.cpp | 10 +-- wled00/FXparticleSystem.cpp | 14 ++-- wled00/bus_manager.cpp | 20 ++++- wled00/bus_wrapper.h | 50 +++++++++--- wled00/button.cpp | 2 +- wled00/cfg.cpp | 4 +- wled00/const.h | 8 +- wled00/data/index.js | 1 + wled00/dmx_output.cpp | 2 +- wled00/improv.cpp | 2 +- wled00/json.cpp | 6 +- wled00/network.cpp | 2 +- wled00/ota_update.cpp | 7 ++ wled00/pin_manager.cpp | 2 +- wled00/set.cpp | 2 +- wled00/src/dependencies/dmx/ESPDMX.cpp | 2 +- wled00/src/dependencies/dmx/SparkFunDMX.cpp | 2 +- wled00/src/dependencies/network/Network.h | 9 ++- wled00/udp.cpp | 2 + wled00/util.cpp | 25 +++++- wled00/wled.cpp | 79 +++++++++++++++++-- wled00/wled.h | 10 +-- 26 files changed, 233 insertions(+), 87 deletions(-) diff --git a/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h b/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h index 02e066f741..3b654be5a6 100644 --- a/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h +++ b/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h @@ -226,7 +226,7 @@ typedef NeoEsp32RmtHIMethodBase Ne typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1400KbpsMethod; typedef NeoEsp32RmtHI1Ws2805Method NeoEsp32RmtHI1Ws2814Method; -#if !defined(CONFIG_IDF_TARGET_ESP32C3) +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2811Method; typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2812xMethod; @@ -321,7 +321,7 @@ typedef NeoEsp32RmtHIMethodBase Ne typedef NeoEsp32RmtHI7Ws2805Method NeoEsp32RmtHI7Ws2814Method; #endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) -#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) // inverted typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2811InvertedMethod; @@ -369,7 +369,7 @@ typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1400KbpsInvertedMethod; typedef NeoEsp32RmtHI1Ws2805InvertedMethod NeoEsp32RmtHI1Ws2814InvertedMethod; -#if !defined(CONFIG_IDF_TARGET_ESP32C3) +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2811InvertedMethod; typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2812xInvertedMethod; @@ -464,6 +464,6 @@ typedef NeoEsp32RmtHIMethodBase #include "esp_idf_version.h" + +// NeoEsp32RmtHI uses the legacy (IDF 4.x) low-level RMT API which is incompatible with IDF 5.x. +// On IDF 5+ builds (pioarduino / Arduino-ESP32 v3.x), WLED selects standard NeoEsp32RmtN methods +// for RISC-V targets anyway (see bus_wrapper.h), so this implementation is not needed. +#if ESP_IDF_VERSION_MAJOR < 5 + +#include #include "NeoEsp32RmtHIMethod.h" #include "soc/soc.h" #include "soc/rmt_reg.h" @@ -504,4 +510,5 @@ esp_err_t NeoEsp32RmtHiMethodDriver::WaitForTxDone(rmt_channel_t channel, TickTy return rv; } -#endif \ No newline at end of file +#endif // ESP_IDF_VERSION_MAJOR < 5 +#endif // ARDUINO_ARCH_ESP32 \ No newline at end of file diff --git a/usermods/audioreactive/audio_reactive.cpp b/usermods/audioreactive/audio_reactive.cpp index 7baa796894..61168aa59c 100644 --- a/usermods/audioreactive/audio_reactive.cpp +++ b/usermods/audioreactive/audio_reactive.cpp @@ -668,7 +668,7 @@ class AudioReactive : public Usermod { static const char _dynamics[]; static const char _frequency[]; static const char _inputLvl[]; -#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#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) static const char _analogmic[]; #endif static const char _digitalmic[]; @@ -1160,23 +1160,23 @@ class AudioReactive : public Usermod { // Reset I2S peripheral for good measure i2s_driver_uninstall(I2S_NUM_0); // E (696) I2S: i2s_driver_uninstall(2006): I2S port 0 has not installed - #if !defined(CONFIG_IDF_TARGET_ESP32C3) + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) delay(100); - periph_module_reset(PERIPH_I2S0_MODULE); // not possible on -C3 + periph_module_reset(PERIPH_I2S0_MODULE); // not possible on -C3/-C5 #endif delay(100); // Give that poor microphone some time to setup. useBandPassFilter = false; - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) if ((i2sckPin == I2S_PIN_NO_CHANGE) && (i2ssdPin >= 0) && (i2swsPin >= 0) && ((dmType == 1) || (dmType == 4)) ) dmType = 5; // dummy user support: SCK == -1 --means--> PDM microphone #endif switch (dmType) { - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S3) // stub cases for not-yet-supported I2S modes on other ESP32 chips case 0: //ADC analog - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) case 5: //PDM Microphone #endif #endif @@ -1204,7 +1204,7 @@ class AudioReactive : public Usermod { delay(100); if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case 5: DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_PDM_MIC_CHANNEL_TEXT)); audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/4.0f); @@ -1220,7 +1220,7 @@ class AudioReactive : public Usermod { if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3) // ADC over I2S is only possible on "classic" ESP32 case 0: DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only).")); @@ -1797,7 +1797,7 @@ class AudioReactive : public Usermod { top[FPSTR(_addPalettes)] = addPalettes; #ifdef ARDUINO_ARCH_ESP32 - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3) JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); amic["pin"] = audioPin; #endif @@ -1856,16 +1856,16 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes); #ifdef ARDUINO_ARCH_ESP32 - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3) configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin); #else audioPin = -1; // MCU does not support analog mic #endif configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["type"], dmType); - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S3) if (dmType == 0) dmType = SR_DMTYPE; // MCU does not support analog - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) if (dmType == 5) dmType = SR_DMTYPE; // MCU does not support PDM #endif #endif @@ -1903,14 +1903,14 @@ class AudioReactive : public Usermod { #ifdef ARDUINO_ARCH_ESP32 uiScript.print(F("uxp=ux+':digitalmic:pin[]';")); // uxp = shortcut for AudioReactive:digitalmic:pin[] uiScript.print(F("dd=addDropdown(ux,'digitalmic:type');")); - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3) uiScript.print(F("addOption(dd,'Generic Analog',0);")); #endif uiScript.print(F("addOption(dd,'Generic I2S',1);")); uiScript.print(F("addOption(dd,'ES7243',2);")); uiScript.print(F("addOption(dd,'SPH0654',3);")); uiScript.print(F("addOption(dd,'Generic I2S with Mclk',4);")); - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) uiScript.print(F("addOption(dd,'Generic I2S PDM',5);")); #endif uiScript.print(F("addOption(dd,'ES8388',6);")); @@ -1946,7 +1946,7 @@ class AudioReactive : public Usermod { uiScript.print(F("addInfo(uxp,0,'sd/data/dout','I2S SD');")); uiScript.print(F("addInfo(uxp,1,'ws/clk/lrck','I2S WS');")); uiScript.print(F("addInfo(uxp,2,'sck/bclk','I2S SCK');")); - #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S3) uiScript.print(F("addInfo(uxp,3,'only use -1, 0, 1 or 3','I2S MCLK');")); #else uiScript.print(F("addInfo(uxp,3,'master clock','I2S MCLK');")); @@ -2069,7 +2069,7 @@ const char AudioReactive::_config[] PROGMEM = "config"; const char AudioReactive::_dynamics[] PROGMEM = "dynamics"; const char AudioReactive::_frequency[] PROGMEM = "frequency"; const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel"; -#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#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) const char AudioReactive::_analogmic[] PROGMEM = "analogmic"; #endif const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic"; diff --git a/wled00/FX.h b/wled00/FX.h index bcbab69a59..5c6d85e0b6 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -70,10 +70,10 @@ extern byte realtimeMode; // used in getMappedPixelIndex() #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) #define FRAMETIME strip.getFrameTime() -#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) - #define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive -#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) - #define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S2) + #define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive +#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) + #define MIN_FRAME_DELAY 3 // S2/C3/C5 are slower than normal esp32, and only have one core #else #define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY #endif diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index fa891a6d84..6f0b2e8e75 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1161,7 +1161,7 @@ void WS2812FX::finalizeInit() { BusManager::removeAll(); unsigned digitalCount = 0; - #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) // determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT) unsigned maxLedsOnBus = 0; unsigned busType = 0; @@ -1191,7 +1191,7 @@ void WS2812FX::finalizeInit() { bool use_placeholder = false; unsigned busMemUsage = bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // does not include DMA/RMT buffer // estimate maximum I2S memory usage (only relevant for digital non-2pin busses) - #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(ESP8266) #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) const bool usesI2S = ((useParallelI2S && digitalCount <= 8) || (!useParallelI2S && digitalCount == 1)); #elif defined(CONFIG_IDF_TARGET_ESP32S2) @@ -1340,8 +1340,8 @@ static uint8_t _add (uint8_t a, uint8_t b) { unsigned t = a + b; return t static uint8_t _subtract (uint8_t a, uint8_t b) { return b > a ? (b - a) : 0; } static uint8_t _difference(uint8_t a, uint8_t b) { return b > a ? (b - a) : (a - b); } static uint8_t _average (uint8_t a, uint8_t b) { return (a + b) >> 1; } -#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) -static uint8_t _multiply (uint8_t a, uint8_t b) { return ((a * b) + 255) >> 8; } // faster than division on C3 but slightly less accurate +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) +static uint8_t _multiply (uint8_t a, uint8_t b) { return ((a * b) + 255) >> 8; } // faster than division on C3/C5 but slightly less accurate #else static uint8_t _multiply (uint8_t a, uint8_t b) { return (a * b) / 255; } // origianl uses a & b in range [0,1] #endif @@ -1351,7 +1351,7 @@ static uint8_t _darken (uint8_t a, uint8_t b) { return a < b ? a : b; } static uint8_t _screen (uint8_t a, uint8_t b) { return 255 - _multiply(~a,~b); } // 255 - (255-a)*(255-b)/255 static uint8_t _overlay (uint8_t a, uint8_t b) { return b < 128 ? 2 * _multiply(a,b) : (255 - 2 * _multiply(~a,~b)); } static uint8_t _hardlight (uint8_t a, uint8_t b) { return a < 128 ? 2 * _multiply(a,b) : (255 - 2 * _multiply(~a,~b)); } -#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a))) + ((2 * a * b + 256) << 8)) >> 16; } // Pegtop's formula (1 - 2a)b^2 #else static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) + 255 * 2 * a * b) / (255 * 255); } // Pegtop's formula (1 - 2a)b^2 + 2ab diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index 626d936b9a..04d17a5682 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -500,7 +500,7 @@ void ParticleSystem2D::applyGravity(PSparticle &part) { // note: a coefficient smaller than 0 will speed them up (this is a feature, not a bug), coefficient larger than 255 inverts the speed, so don't do that 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) int32_t friction = 256 - coefficient; part.vx = ((int32_t)part.vx * friction + (((int32_t)part.vx >> 31) & 0xFF)) >> 8; // note: (v>>31) & 0xFF)) extracts the sign and adds 255 if negative for correct rounding using shifts part.vy = ((int32_t)part.vy * friction + (((int32_t)part.vy >> 31) & 0xFF)) >> 8; @@ -514,7 +514,7 @@ void ParticleSystem2D::applyFriction(PSparticle &part, const int32_t coefficient // apply friction to all particles // note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is fast anyways void ParticleSystem2D::applyFriction(const int32_t coefficient) { - #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) int32_t friction = 256 - coefficient; for (uint32_t i = 0; i < usedParticles; i++) { particles[i].vx = ((int32_t)particles[i].vx * friction + (((int32_t)particles[i].vx >> 31) & 0xFF)) >> 8; // note: (v>>31) & 0xFF)) extracts the sign and adds 255 if negative for correct rounding using shifts @@ -929,7 +929,7 @@ void WLED_O2_ATTR ParticleSystem2D::collideParticles(PSparticle &particle1, PSpa int32_t surfacehardness = max(collisionHardness, (int32_t)PS_P_MINSURFACEHARDNESS >> 1); // if particles are soft, the impulse must stay above a limit or collisions slip through at higher speeds, 170 seems to be a good value int32_t impulse = (((((-dotProduct) << 15) / distanceSquared) * surfacehardness) >> 8); // note: inverting before bitshift corrects for asymmetry in right-shifts (is slightly faster) - #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) int32_t ximpulse = (impulse * dx + ((dx >> 31) & 0x7FFF)) >> 15; // note: extracting sign bit and adding rounding value to correct for asymmetry in right shifts int32_t yimpulse = (impulse * dy + ((dy >> 31) & 0x7FFF)) >> 15; #else @@ -957,7 +957,7 @@ void WLED_O2_ATTR ParticleSystem2D::collideParticles(PSparticle &particle1, PSpa if (collisionHardness < PS_P_MINSURFACEHARDNESS && (SEGMENT.call & 0x07) == 0) { // if particles are soft, they become 'sticky' i.e. apply some friction (they do pile more nicely and stop sloshing around) const uint32_t coeff = collisionHardness + (255 - PS_P_MINSURFACEHARDNESS); // Note: could call applyFriction, but this is faster and speed is key here - #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) particle1.vx = ((int32_t)particle1.vx * coeff + (((int32_t)particle1.vx >> 31) & 0xFF)) >> 8; // note: (v>>31) & 0xFF)) extracts the sign and adds 255 if negative for correct rounding using shifts particle1.vy = ((int32_t)particle1.vy * coeff + (((int32_t)particle1.vy >> 31) & 0xFF)) >> 8; particle2.vx = ((int32_t)particle2.vx * coeff + (((int32_t)particle2.vx >> 31) & 0xFF)) >> 8; @@ -1406,7 +1406,7 @@ void ParticleSystem1D::applyGravity(PSparticle1D &part, PSparticleFlags1D &partF // slow down particle by friction, the higher the speed, the higher the friction. a high friction coefficient slows them more (255 means instant stop) // note: a coefficient smaller than 0 will speed them up (this is a feature, not a bug), coefficient larger than 255 inverts the speed, so don't do that void ParticleSystem1D::applyFriction(int32_t coefficient) { - #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) int32_t friction = 256 - coefficient; for (uint32_t i = 0; i < usedParticles; i++) { if (particles[i].ttl) @@ -1699,7 +1699,7 @@ void WLED_O2_ATTR ParticleSystem1D::collideParticles(uint32_t partIdx1, uint32_t } int32_t surfacehardness = max(collisionHardness, (int32_t)PS_P_MINSURFACEHARDNESS_1D); // if particles are soft, the impulse must stay above a limit or collisions slip through // Calculate new velocities after collision note: not using dot product like in 2D as impulse is purely speed depnedent - #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) int32_t impulse = (dv * surfacehardness + ((dv >> 31) & 0xFF)) >> 8; // note: (v>>31) & 0xFF)) extracts the sign and adds 255 if negative for correct rounding using shifts #else // division is faster on ESP32, S2 and S3 int32_t impulse = (dv * surfacehardness) / 255; @@ -1720,7 +1720,7 @@ void WLED_O2_ATTR ParticleSystem1D::collideParticles(uint32_t partIdx1, uint32_t if (collisionHardness < PS_P_MINSURFACEHARDNESS_1D && (SEGMENT.call & 0x07) == 0) { // if particles are soft, they become 'sticky' i.e. apply some friction const uint32_t coeff = collisionHardness + (250 - PS_P_MINSURFACEHARDNESS_1D); - #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) particles[partIdx1].vx = ((int32_t)particles[partIdx1].vx * coeff + (((int32_t)particles[partIdx1].vx >> 31) & 0xFF)) >> 8; // note: (v>>31) & 0xFF)) extracts the sign and adds 255 if negative for correct rounding using shifts particles[partIdx2].vx = ((int32_t)particles[partIdx2].vx * coeff + (((int32_t)particles[partIdx2].vx >> 31) & 0xFF)) >> 8; #else // division is faster on ESP32, S2 and S3 diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index a73146ec0f..79c5bf8f5f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -9,7 +9,7 @@ #include "src/dependencies/network/Network.h" // for isConnected() (& WiFi) #include "driver/ledc.h" #include "soc/ledc_struct.h" - #if !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) + #if !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C5)) #define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS) #define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock) extern xSemaphoreHandle _ledc_sys_lock; @@ -426,8 +426,12 @@ BusPwm::BusPwm(const BusConfig &bc) pinMode(_pins[i], OUTPUT); #else unsigned channel = _ledcStart + i; + #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + ledcAttachChannel(_pins[i], _frequency, _depth - (dithering*4), channel); + #else ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit ledcAttachPin(_pins[i], 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) @@ -553,7 +557,11 @@ 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(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + LEDC.channel_group[gr].channel[ch].duty_init.duty = duty << ((!dithering)*4); // lowest 4 bits are used for dithering, shift by 4 bits if not using dithering + #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 + #endif 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 @@ -591,8 +599,12 @@ void BusPwm::deallocatePins() { #ifdef ESP8266 digitalWrite(_pins[i], LOW); //turn off PWM interrupt #else + #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + if (_ledcStart < WLED_MAX_ANALOG_CHANNELS) ledcDetach(_pins[i]); + #else if (_ledcStart < WLED_MAX_ANALOG_CHANNELS) ledcDetachPin(_pins[i]); #endif + #endif } #ifdef ARDUINO_ARCH_ESP32 PinManager::deallocateLedc(_ledcStart, numPins); @@ -1145,12 +1157,12 @@ size_t BusManager::memUsage() { // front buffers are always allocated per bus unsigned size = 0; unsigned maxI2S = 0; - #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(ESP8266) unsigned digitalCount = 0; #endif for (const auto &bus : busses) { size += bus->getBusSize(); - #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(ESP8266) if (bus->isDigital() && !bus->is2Pin()) { digitalCount++; if ((PolyBus::isParallelI2S1Output() && digitalCount <= 8) || (!PolyBus::isParallelI2S1Output() && digitalCount == 1)) { @@ -1256,7 +1268,7 @@ void BusManager::esp32RMTInvertIdle() { unsigned u = 0; for (auto &bus : busses) { if (bus->getLength()==0 || !bus->isDigital() || bus->is2Pin()) continue; - #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) // 2 RMT, only has 1 I2S but NPB does not support it ATM if (u > 1) return; rmt = u; #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, only has 1 I2S bus, supported in NPB diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index b2ff947418..2d15eb3317 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -231,7 +231,7 @@ typedef NeoEsp32I2s0Apa106Method X1Apa106Method; typedef NeoEsp32I2s0Ws2805Method X1Ws2805Method; typedef NeoEsp32I2s0Tm1914Method X1Tm1914Method; -#elif !defined(CONFIG_IDF_TARGET_ESP32C3) +#elif !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) // regular ESP32 will use I2S1 typedef NeoEsp32I2s1Ws2812xMethod X1Ws2812xMethod; typedef NeoEsp32I2s1Sk6812Method X1Sk6812Method; @@ -245,7 +245,28 @@ #endif // RMT driver selection -#if !defined(WLED_USE_SHARED_RMT) && !defined(__riscv) +#if defined(CONFIG_IDF_TARGET_ESP32C5) +// ESP32-C5: NeoPixelBus 2.8.x legacy RMT API conflicts with IDF 5.x driver_ng, use bit-bang instead. +// Bit-bang constructor takes 4 args but NeoPixelBus passes 5 (including channel), so we wrap it. +template +class NeoEsp32C5BitBang { +public: + typedef NeoNoSettings SettingsObject; + NeoEsp32C5BitBang(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel = NeoBusChannel_0) + : _bb(pin, pixelCount, elementSize, settingsSize) {} + bool IsReadyToUpdate() const { return _bb.IsReadyToUpdate(); } + void Initialize() { _bb.Initialize(); } + void Update(bool b) { _bb.Update(b); } + bool AlwaysUpdate() { return _bb.AlwaysUpdate(); } + bool SwapBuffers() { return _bb.SwapBuffers(); } + uint8_t* getData() const { return _bb.getData(); } + size_t getDataSize() const { return _bb.getDataSize(); } + void applySettings(const SettingsObject& settings) { _bb.applySettings(settings); } +private: + T_BB _bb; +}; +#define NeoEsp32RmtMethod(x) NeoEsp32C5BitBang +#elif !defined(WLED_USE_SHARED_RMT) && !defined(__riscv) #include #define NeoEsp32RmtMethod(x) NeoEsp32RmtHIN ## x ## Method #else @@ -447,7 +468,7 @@ class PolyBus { case I_32_RN_TM1914_3: beginTM1914(busPtr); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->Begin(); break; // I2S1 bus or parellel buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; @@ -479,14 +500,14 @@ class PolyBus { static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel) { // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation - #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) if (_useParallelI2S && (channel >= 8)) { // Parallel I2S channels are to be used first, so subtract 8 to get the RMT channel number channel -= 8; } #endif - #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 @@ -559,7 +580,7 @@ class PolyBus { case I_32_RN_TM1914_3: busPtr = new B_32_RN_TM1914_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_SM16825_5: busPtr = new B_32_RN_SM16825_5(len, pins[0], (NeoBusChannel)channel); break; // I2S1 bus or paralell buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: if (_useParallelI2S) busPtr = new B_32_IP_NEO_3(len, pins[0]); else busPtr = new B_32_I2_NEO_3(len, pins[0]); break; case I_32_I2_NEO_4: if (_useParallelI2S) busPtr = new B_32_IP_NEO_4(len, pins[0]); else busPtr = new B_32_I2_NEO_4(len, pins[0]); break; case I_32_I2_400_3: if (_useParallelI2S) busPtr = new B_32_IP_400_3(len, pins[0]); else busPtr = new B_32_I2_400_3(len, pins[0]); break; @@ -658,7 +679,7 @@ class PolyBus { case I_32_RN_TM1914_3: (static_cast(busPtr))->Show(consistent); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->Show(consistent); break; // I2S1 bus or paralell buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; @@ -754,7 +775,7 @@ class PolyBus { case I_32_RN_TM1914_3: return (static_cast(busPtr))->CanShow(); break; case I_32_RN_SM16825_5: return (static_cast(busPtr))->CanShow(); break; // I2S1 bus or paralell buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; case I_32_I2_NEO_4: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; case I_32_I2_400_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; @@ -876,7 +897,7 @@ class PolyBus { case I_32_RN_TM1914_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break; // I2S1 bus or paralell buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, col); else (static_cast(busPtr))->SetPixelColor(pix, col); break; case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; @@ -973,7 +994,7 @@ class PolyBus { case I_32_RN_TM1914_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_32_RN_SM16825_5: { Rgbww80Color c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W // I2S1 bus or paralell buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; case I_32_I2_NEO_4: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; case I_32_I2_400_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; @@ -1088,7 +1109,7 @@ class PolyBus { case I_32_RN_TM1914_3: delete (static_cast(busPtr)); break; case I_32_RN_SM16825_5: delete (static_cast(busPtr)); break; // I2S1 bus or paralell buses - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; case I_32_I2_NEO_4: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; case I_32_I2_400_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; @@ -1185,7 +1206,7 @@ class PolyBus { case I_32_RN_TM1914_3: size = (static_cast(busPtr))->PixelsSize()*2; break; case I_32_RN_SM16825_5: size = (static_cast(busPtr))->PixelsSize()*2; break; // I2S1 bus or paralell buses (front + DMA; DMA = front * cadence, aligned to 4 bytes) - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; case I_32_I2_NEO_4: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; case I_32_I2_400_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; @@ -1264,7 +1285,7 @@ class PolyBus { case I_32_RN_2805_5 : size = (size + 2*count)*2; break; // 5 channels case I_32_RN_SM16825_5: size = (size + 2*count)*2*2; break; // 16bit, 5 channels // I2S1 bus or paralell I2S1 buses (1x front, does not include DMA buffer which is front*cadence, a bit(?) more for LCD) - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) case I_32_I2_NEO_3 : // fallthrough case I_32_I2_400_3 : // fallthrough case I_32_I2_TM2_3 : // fallthrough @@ -1355,6 +1376,9 @@ class PolyBus { // On ESP32-C3 only the first 2 RMT channels are usable for transmitting if (num > 1) return I_NONE; //if (num > 1) offset = 1; // I2S not supported yet (only 1 I2S) + #elif defined(CONFIG_IDF_TARGET_ESP32C5) + // ESP32-C5 uses bit-bang output (max 2 for performance) + if (num > 1) return I_NONE; #elif defined(CONFIG_IDF_TARGET_ESP32S3) // On ESP32-S3 only the first 4 RMT channels are usable for transmitting if (_useParallelI2S) { diff --git a/wled00/button.cpp b/wled00/button.cpp index d544dd73ab..a2703bb3f3 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -109,7 +109,7 @@ bool isButtonPressed(uint8_t b) break; case BTN_TYPE_TOUCH: case BTN_TYPE_TOUCH_SWITCH: - #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) #ifdef SOC_TOUCH_VERSION_2 //ESP32 S2 and S3 provide a function to check touch state (state is updated in interrupt) if (touchInterruptGetLastStatus(pin)) return true; #else diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 08a5f04f9a..909115d3a7 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -177,7 +177,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { uint8_t cctBlending = hw_led[F("cb")] | Bus::getCCTBlend(); Bus::setCCTBlend(cctBlending); strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS - #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) CJSON(useParallelI2S, hw_led[F("prl")]); #endif @@ -947,7 +947,7 @@ void serializeConfig(JsonObject root) { hw_led[F("cb")] = Bus::getCCTBlend(); hw_led["fps"] = strip.getTargetFps(); hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override - #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) hw_led[F("prl")] = BusManager::hasParallelOutput(); #endif diff --git a/wled00/const.h b/wled00/const.h index 40b6a42bd9..d67b44e0bc 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -63,7 +63,7 @@ constexpr size_t FIXED_PALETTE_COUNT = DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_C #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 + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #define WLED_MAX_DIGITAL_CHANNELS 2 //#define WLED_MAX_ANALOG_CHANNELS 6 #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI @@ -508,7 +508,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); #define MAX_LEDS 1536 //can't rely on memory limit to limit this to 1536 LEDs #elif defined(CONFIG_IDF_TARGET_ESP32S2) #define MAX_LEDS 2048 //due to memory constraints S2 - #elif defined(CONFIG_IDF_TARGET_ESP32C3) + #elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) #define MAX_LEDS 4096 #else #define MAX_LEDS 16384 @@ -521,7 +521,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); #else #if defined(ARDUINO_ARCH_ESP32S2) #define MAX_LED_MEMORY 16384 - #elif defined(ARDUINO_ARCH_ESP32C3) + #elif defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32C5) #define MAX_LED_MEMORY 32768 #else #define MAX_LED_MEMORY 65536 @@ -637,7 +637,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_ESP32C5) #ifdef WLED_ENABLE_DMX #define DEFAULT_LED_PIN 1 #warning "Compiling with DMX. The default LED pin has been changed to pin 1." diff --git a/wled00/data/index.js b/wled00/data/index.js index 99c0087d2a..4c9e8f4e39 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -703,6 +703,7 @@ ${urows===""?'':'
"):''} ${inforow("Build",i.vid)} ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} +${i.wifi.band?inforow("WiFi band",i.wifi.band + " (Ch " + i.wifi.channel + ")"):""} ${inforow("Uptime",getRuntimeStr(i.uptime))} ${inforow("Time",i.time)} ${inforow("Free heap",(i.freeheap/1024).toFixed(1)," kB")} diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index eace2145e6..f7b87cb048 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -69,7 +69,7 @@ void handleDMXOutput() } void initDMXOutput() { - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || 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 dmx.initWrite(512); // initialize with bus length diff --git a/wled00/improv.cpp b/wled00/improv.cpp index 0bc7a6698f..46d67beb3d 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -10,7 +10,7 @@ #define DIMPROV_PRINTF(x...) #endif -#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S3) #undef WLED_DISABLE_IMPROV_WIFISCAN #define WLED_DISABLE_IMPROV_WIFISCAN #endif diff --git a/wled00/json.cpp b/wled00/json.cpp index 3e053708c0..614a62ab29 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -795,7 +795,9 @@ void serializeInfo(JsonObject root) int qrssi = WiFi.RSSI(); wifi_info[F("rssi")] = qrssi; wifi_info[F("signal")] = getSignalQuality(qrssi); - wifi_info[F("channel")] = WiFi.channel(); + int wifiChannel = WiFi.channel(); + wifi_info[F("channel")] = wifiChannel; + wifi_info[F("band")] = (wifiChannel >= 36) ? F("5GHz") : F("2.4GHz"); wifi_info[F("ap")] = apActive; JsonObject fs_info = root.createNestedObject("fs"); @@ -810,7 +812,7 @@ void serializeInfo(JsonObject root) wifi_info[F("txPower")] = (int) WiFi.getTxPower(); wifi_info[F("sleep")] = (bool) WiFi.getSleep(); #endif - #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) root[F("arch")] = "esp32"; #else root[F("arch")] = ESP.getChipModel(); diff --git a/wled00/network.cpp b/wled00/network.cpp index 68770a716b..bd243c9100 100644 --- a/wled00/network.cpp +++ b/wled00/network.cpp @@ -248,7 +248,7 @@ bool initEthernet() } // https://github.com/wled/WLED/issues/5247 - if (multiWiFi[0].staticIP != (uint32_t)0x00000000 && multiWiFi[0].staticGW != (uint32_t)0x00000000) { + if ((uint32_t)multiWiFi[0].staticIP != 0 && (uint32_t)multiWiFi[0].staticGW != 0) { ETH.config(multiWiFi[0].staticIP, multiWiFi[0].staticGW, multiWiFi[0].staticSN, dnsAddress); } else { ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); diff --git a/wled00/ota_update.cpp b/wled00/ota_update.cpp index 2f758b2c79..da4c49bbf1 100644 --- a/wled00/ota_update.cpp +++ b/wled00/ota_update.cpp @@ -428,6 +428,13 @@ bool verifyBootloaderImage(const uint8_t* &buffer, size_t &len, String* bootload } *bootloaderErrorMsg = "ESP32-C3 update not supported yet"; return false; + #elif defined(CONFIG_IDF_TARGET_ESP32C5) + if (chipId != 0x0017) { + *bootloaderErrorMsg = "Chip ID mismatch - expected ESP32-C5 (0x0017), got 0x" + String(chipId, HEX); + return false; + } + *bootloaderErrorMsg = "ESP32-C5 update not supported yet"; + return false; #elif defined(CONFIG_IDF_TARGET_ESP32S3) if (chipId != 0x0009) { *bootloaderErrorMsg = "Chip ID mismatch - expected ESP32-S3 (0x0009), got 0x" + String(chipId, HEX); diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 709263e1a3..aa4bc74384 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -208,7 +208,7 @@ bool PinManager::isPinOk(byte gpio, bool output) if (gpio >= WLED_NUM_PINS) return false; // catch error case, to avoid array out-of-bounds access #ifdef ARDUINO_ARCH_ESP32 if (digitalPinIsValid(gpio)) { - #if defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) // strapping pins: 2, 8, & 9 if (gpio > 11 && gpio < 18) return false; // 11-17 SPI FLASH #if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1 diff --git a/wled00/set.cpp b/wled00/set.cpp index ef1afcb84b..a51c8f2da5 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -188,7 +188,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) Bus::setCCTBlend(cctBlending); Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt()); - #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) useParallelI2S = request->hasArg(F("PR")); #endif diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp index a80cad71c8..b1a613375a 100644 --- a/wled00/src/dependencies/dmx/ESPDMX.cpp +++ b/wled00/src/dependencies/dmx/ESPDMX.cpp @@ -11,7 +11,7 @@ // - - - - - /* ----- LIBRARIES ----- */ -#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) #include diff --git a/wled00/src/dependencies/dmx/SparkFunDMX.cpp b/wled00/src/dependencies/dmx/SparkFunDMX.cpp index 064b9ff620..a180869b80 100644 --- a/wled00/src/dependencies/dmx/SparkFunDMX.cpp +++ b/wled00/src/dependencies/dmx/SparkFunDMX.cpp @@ -17,7 +17,7 @@ Distributed as-is; no warranty is given. #if defined(ARDUINO_ARCH_ESP32) #include -#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32S2) #include "SparkFunDMX.h" #include diff --git a/wled00/src/dependencies/network/Network.h b/wled00/src/dependencies/network/Network.h index 9201d514ea..6fa3084e7b 100644 --- a/wled00/src/dependencies/network/Network.h +++ b/wled00/src/dependencies/network/Network.h @@ -8,6 +8,13 @@ #ifndef Network_h #define Network_h +// Arduino-ESP32 v3.x (IDF 5+) defines its own 'Network' global as NetworkManager. +// Rename WLED's Network to avoid the conflict. The #define ensures all existing +// references to 'Network.localIP()' etc. transparently use the renamed instance. +#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + #define Network WLEDNetwork +#endif + class NetworkClass { public: @@ -21,4 +28,4 @@ class NetworkClass extern NetworkClass Network; -#endif \ No newline at end of file +#endif diff --git a/wled00/udp.cpp b/wled00/udp.cpp index f0e0ea7ea0..4b2a944361 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -722,6 +722,8 @@ void sendSysInfoUDP() memcpy((byte *)data + 6, serverDescription, 32); #ifdef ESP8266 data[38] = NODE_TYPE_ID_ESP8266; + #elif defined(CONFIG_IDF_TARGET_ESP32C5) + data[38] = NODE_TYPE_ID_ESP32C5; #elif defined(CONFIG_IDF_TARGET_ESP32C3) data[38] = NODE_TYPE_ID_ESP32C3; #elif defined(CONFIG_IDF_TARGET_ESP32S3) diff --git a/wled00/util.cpp b/wled00/util.cpp index bccb2d6920..af18775a53 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -694,7 +694,7 @@ static void *validateFreeHeap(void *buffer) { void *d_malloc(size_t size) { void *buffer; - #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) // the newer ESP32 variants have byte-accessible fast RTC memory that can be used as heap, access speed is on-par with DRAM // the system does prefer normal DRAM until full, since free RTC memory is ~7.5k only, its below the minimum heap threshold and needs to be allocated explicitly // use RTC RAM for small allocations to improve fragmentation or if DRAM is running low @@ -795,7 +795,7 @@ void *allocate_buffer(size_t size, uint32_t type) { DEBUG_PRINTF_P(PSTR("*Buffer allocated: size:%d, address:%p"), size, (uintptr_t)buffer); if ((uintptr_t)buffer > SOC_DRAM_LOW && (uintptr_t)buffer < SOC_DRAM_HIGH) DEBUG_PRINTLN(F(" in DRAM")); - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) else if ((uintptr_t)buffer > SOC_EXTRAM_DATA_LOW && (uintptr_t)buffer < SOC_EXTRAM_DATA_HIGH) DEBUG_PRINTLN(F(" in PSRAM")); #endif @@ -1142,9 +1142,15 @@ String computeSHA1(const String& input) { mbedtls_sha1_context ctx; mbedtls_sha1_init(&ctx); + #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + mbedtls_sha1_starts(&ctx); + mbedtls_sha1_update(&ctx, (const unsigned char*)input.c_str(), input.length()); + mbedtls_sha1_finish(&ctx, shaResult); + #else mbedtls_sha1_starts_ret(&ctx); mbedtls_sha1_update_ret(&ctx, (const unsigned char*)input.c_str(), input.length()); mbedtls_sha1_finish_ret(&ctx, shaResult); + #endif mbedtls_sha1_free(&ctx); // Convert to hexadecimal string @@ -1159,15 +1165,25 @@ String computeSHA1(const String& input) { } #ifdef ESP32 -#include "esp_adc_cal.h" +#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + #include "esp_chip_info.h" + #include "esp_mac.h" +#else + #include "esp_adc_cal.h" +#endif String generateDeviceFingerprint() { uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint esp_chip_info_t chip_info; esp_chip_info(&chip_info); esp_efuse_mac_get_default((uint8_t*)fp); fp[1] ^= ESP.getFlashChipSize(); + #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 5 + fp[0] ^= chip_info.revision | (chip_info.model << 16); + #else fp[0] ^= chip_info.full_revision | (chip_info.model << 16); - // mix in ADC calibration data: + #endif +#if !defined(ESP_IDF_VERSION_MAJOR) || ESP_IDF_VERSION_MAJOR < 5 + // mix in ADC calibration data (legacy API removed in IDF 5.x): esp_adc_cal_characteristics_t ch; #if SOC_ADC_MAX_BITWIDTH == 13 // S2 has 13 bit ADC constexpr auto myBIT_WIDTH = ADC_WIDTH_BIT_13; @@ -1187,6 +1203,7 @@ String generateDeviceFingerprint() { fp[1] ^= ch.high_curve[i]; } } +#endif char fp_string[17]; // 16 hex chars + null terminator sprintf(fp_string, "%08X%08X", fp[1], fp[0]); return String(fp_string); diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 5b7e1f1345..af6037e5a8 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -174,9 +174,9 @@ void WLED::loop() #ifdef ESP8266 uint32_t heap = getFreeHeapSize(); // ESP8266 needs ~8k of free heap for UI to work properly #else - #ifdef CONFIG_IDF_TARGET_ESP32C3 - // calling getContiguousFreeHeap() during led update causes glitches on C3 - // this can (probably) be removed once RMT driver for C3 is fixed + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) + // calling getContiguousFreeHeap() during led update causes glitches on C3/C5 + // this can (probably) be removed once RMT driver for C3/C5 is fixed unsigned t0 = millis(); while (strip.isUpdating() && (millis() - t0 < 15)) delay(1); // be nice, but not too nice. Waits up to 15ms #endif @@ -379,7 +379,7 @@ void WLED::setup() Serial.setTimeout(50); // this causes troubles on new MCUs that have a "virtual" USB Serial (HWCDC) #else #endif - #if defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && (defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || ARDUINO_USB_CDC_ON_BOOT) + #if defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && (defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || ARDUINO_USB_CDC_ON_BOOT) delay(2500); // allow CDC USB serial to initialise #endif #if !defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DEBUG_HOST) && ARDUINO_USB_CDC_ON_BOOT @@ -685,7 +685,7 @@ void WLED::initConnection() WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N); #endif - if (multiWiFi[selectedWiFi].staticIP != 0U && multiWiFi[selectedWiFi].staticGW != 0U) { + if ((uint32_t)multiWiFi[selectedWiFi].staticIP != 0 && (uint32_t)multiWiFi[selectedWiFi].staticGW != 0) { WiFi.config(multiWiFi[selectedWiFi].staticIP, multiWiFi[selectedWiFi].staticGW, multiWiFi[selectedWiFi].staticSN, dnsAddress); } else { WiFi.config(IPAddress((uint32_t)0), IPAddress((uint32_t)0), IPAddress((uint32_t)0)); @@ -716,7 +716,69 @@ void WLED::initConnection() char hostname[25]; prepareHostname(hostname); -#ifdef WLED_ENABLE_WPA_ENTERPRISE +#if defined(CONFIG_IDF_TARGET_ESP32C5) + // ESP32-C5: Prefer 5GHz WiFi 6 for better performance with hidden SSIDs + #include "esp_wifi.h" + DEBUG_PRINTF_P(PSTR("ESP32-C5: Trying 5GHz for hidden SSID: %s\n"), multiWiFi[selectedWiFi].clientSSID); + + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + delay(100); + WiFi.mode(WIFI_STA); + WiFi.persistent(false); + + // Set country code to enable all 5GHz channels (including DFS) + wifi_country_t country = { + .cc = "US", // US allows most 5GHz channels + .schan = 1, + .nchan = 14, + .max_tx_power = 80, // 20 dBm + .policy = WIFI_COUNTRY_POLICY_MANUAL + }; + esp_wifi_set_country(&country); + DEBUG_PRINTLN(F("ESP32-C5: Country set to US for full 5GHz support")); + + // Set 5GHz only mode + WiFi.setBandMode(WIFI_BAND_MODE_5G_ONLY); + DEBUG_PRINTLN(F("ESP32-C5: Band mode set to 5GHz ONLY")); + + // Configure WiFi for hidden SSID using ESP-IDF API + 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); + esp_wifi_connect(); + + // Wait up to 20 seconds for 5GHz connection (DFS channels need longer) + DEBUG_PRINTLN(F("ESP32-C5: Waiting for 5GHz connection (20s timeout)...")); + unsigned long start = millis(); + while (WiFi.status() != WL_CONNECTED && millis() - start < 20000) { + delay(250); + if ((millis() - start) % 3000 < 250) { + DEBUG_PRINTF_P(PSTR(" ...%lus, status=%d\n"), (millis() - start) / 1000, WiFi.status()); + } + } + + if (WiFi.status() != WL_CONNECTED) { + // 5GHz failed, fall back to AUTO mode + DEBUG_PRINTLN(F("ESP32-C5: 5GHz failed, falling back to AUTO mode")); + esp_wifi_disconnect(); + WiFi.mode(WIFI_OFF); + delay(100); + WiFi.mode(WIFI_STA); + WiFi.setBandMode(WIFI_BAND_MODE_AUTO); + + // Reconfigure for AUTO mode + esp_wifi_set_config(WIFI_IF_STA, &wifi_config); + esp_wifi_connect(); + } else { + DEBUG_PRINTF_P(PSTR("ESP32-C5: SUCCESS! Connected to 5GHz, channel: %d\n"), WiFi.channel()); + } +#elif defined(WLED_ENABLE_WPA_ENTERPRISE) if (multiWiFi[selectedWiFi].encryptionType == WIFI_ENCRYPTION_TYPE_PSK) { DEBUG_PRINTLN(F("Using PSK")); #ifdef ESP8266 @@ -924,6 +986,11 @@ void WLED::handleConnection() DEBUG_PRINTLN(); DEBUG_PRINT(F("Connected! IP address: ")); DEBUG_PRINTLN(Network.localIP()); + #if defined(CONFIG_IDF_TARGET_ESP32C5) + // Show WiFi channel to verify 5GHz connection (channels 36+ = 5GHz) + int32_t channel = WiFi.channel(); + DEBUG_PRINTF_P(PSTR("WiFi channel: %d (%s)\n"), channel, (channel >= 36) ? "5GHz" : "2.4GHz"); + #endif if (improvActive) { if (improvError == 3) sendImprovStateResponse(0x00, true); sendImprovStateResponse(0x04); diff --git a/wled00/wled.h b/wled00/wled.h index e88137aa78..23e7906d8f 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -142,7 +142,7 @@ #endif #ifdef WLED_ENABLE_DMX - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) + #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) #include "src/dependencies/dmx/ESPDMX.h" #else //ESP32 #include "src/dependencies/dmx/SparkFunDMX.h" @@ -315,7 +315,7 @@ WLED_GLOBAL bool rlyOpenDrain _INIT(RLYODRAIN); #define IRTYPE 0 #endif -#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || (defined(RX) && defined(TX)) +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) || (defined(RX) && defined(TX)) // use RX/TX as set by the framework - these boards do _not_ have RX=3 and TX=1 constexpr uint8_t hardwareRX = RX; constexpr uint8_t hardwareTX = TX; @@ -376,7 +376,7 @@ WLED_GLOBAL bool noWifiSleep _INIT(false); WLED_GLOBAL bool force802_3g _INIT(false); #endif // WLED_SAVE_RAM #ifdef ARDUINO_ARCH_ESP32 - #if defined(LOLIN_WIFI_FIX) && (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) + #if defined(LOLIN_WIFI_FIX) && (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_8_5dBm); #else WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_19_5dBm); @@ -403,7 +403,7 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load WLED_GLOBAL bool useGlobalLedBuffer _INIT(false); // double buffering disabled on ESP8266 #else WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on ESP32 - #ifndef CONFIG_IDF_TARGET_ESP32C3 + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) WLED_GLOBAL bool useParallelI2S _INIT(false); // parallel I2S for ESP32 #endif #endif @@ -457,7 +457,7 @@ WLED_GLOBAL bool arlsDisableGammaCorrection _INIT(true); // activate if WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to force max brightness if source has very dark colors that would be black #ifdef WLED_ENABLE_DMX - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) + #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) WLED_GLOBAL DMXESPSerial dmx; #else //ESP32 WLED_GLOBAL SparkFunDMX dmx;