diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index d66369f789..912a8bf94b 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -67,6 +67,7 @@ AUTO_LOAD = ["preferences"] IS_TARGET_PLATFORM = True CONF_RELEASE = "release" +CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features" def set_core_data(config): @@ -506,6 +507,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False ): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, + cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -645,6 +647,11 @@ async def to_code(config): add_idf_sdkconfig_option( "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False ) + if conf[CONF_ADVANCED].get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): + _LOGGER.warning( + "Using experimental features in ESP-IDF may result in unexpected failures." + ) + add_idf_sdkconfig_option("CONFIG_IDF_EXPERIMENTAL_FEATURES", True) cg.add_define( "USE_ESP_IDF_VERSION_CODE", diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index e83cf3ed99..f268d5747f 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -1,17 +1,40 @@ +import logging + import esphome.codegen as cg -from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant +from esphome.components.esp32 import ( + CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES, + VARIANT_ESP32, + add_idf_sdkconfig_option, + get_esp32_variant, + only_on_variant, +) +from esphome.components.esp32.const import VARIANT_ESP32S2, VARIANT_ESP32S3 import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_MODE, CONF_SPEED +from esphome.const import ( + CONF_ADVANCED, + CONF_FRAMEWORK, + CONF_ID, + CONF_MODE, + CONF_SPEED, + PLATFORM_ESP32, +) from esphome.core import CORE +import esphome.final_validate as fv CODEOWNERS = ["@esphome/core"] +DEPENDENCIES = [PLATFORM_ESP32] + +_LOGGER = logging.getLogger(__name__) + psram_ns = cg.esphome_ns.namespace("psram") PsramComponent = psram_ns.class_("PsramComponent", cg.Component) TYPE_QUAD = "quad" TYPE_OCTAL = "octal" +CONF_ENABLE_ECC = "enable_ecc" + SPIRAM_MODES = { TYPE_QUAD: "CONFIG_SPIRAM_MODE_QUAD", TYPE_OCTAL: "CONFIG_SPIRAM_MODE_OCT", @@ -26,7 +49,25 @@ SPIRAM_SPEEDS = { def validate_psram_mode(config): if config[CONF_MODE] == TYPE_OCTAL and config[CONF_SPEED] == 120e6: - raise cv.Invalid("PSRAM 120MHz is not supported in octal mode") + esp32_config = fv.full_config.get()[PLATFORM_ESP32] + if ( + esp32_config[CONF_FRAMEWORK] + .get(CONF_ADVANCED, {}) + .get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES) + ): + _LOGGER.warning( + "120MHz PSRAM in octal mode is an experimental feature - use at your own risk" + ) + else: + raise cv.Invalid("PSRAM 120MHz is not supported in octal mode") + if config[CONF_MODE] != TYPE_OCTAL and config[CONF_ENABLE_ECC]: + raise cv.Invalid("ECC is only available in octal mode.") + if config[CONF_MODE] == TYPE_OCTAL: + variant = get_esp32_variant() + if variant != VARIANT_ESP32S3: + raise cv.Invalid( + f"Octal PSRAM is only supported on ESP32-S3, not {variant}" + ) return config @@ -37,19 +78,25 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_MODE, default=TYPE_QUAD): cv.enum( SPIRAM_MODES, lower=True ), + cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean, cv.Optional(CONF_SPEED, default=40e6): cv.All( cv.frequency, cv.one_of(*SPIRAM_SPEEDS) ), } ), - cv.only_on_esp32, - validate_psram_mode, + only_on_variant( + supported=[VARIANT_ESP32, VARIANT_ESP32S3, VARIANT_ESP32S2], + ), ) +FINAL_VALIDATE_SCHEMA = validate_psram_mode + async def to_code(config): if CORE.using_arduino: cg.add_build_flag("-DBOARD_HAS_PSRAM") + if config[CONF_MODE] == TYPE_OCTAL: + cg.add_platformio_option("board_build.arduino.memory_type", "qio_opi") if CORE.using_esp_idf: add_idf_sdkconfig_option( @@ -62,6 +109,14 @@ async def to_code(config): add_idf_sdkconfig_option(f"{SPIRAM_MODES[config[CONF_MODE]]}", True) add_idf_sdkconfig_option(f"{SPIRAM_SPEEDS[config[CONF_SPEED]]}", True) + if config[CONF_MODE] == TYPE_OCTAL and config[CONF_SPEED] == 120e6: + add_idf_sdkconfig_option("CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240", True) + # This works only on IDF 5.4.x but does no harm on earlier versions + add_idf_sdkconfig_option( + "CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR", True + ) + if config[CONF_ENABLE_ECC]: + add_idf_sdkconfig_option("CONFIG_SPIRAM_ECC_ENABLE", True) cg.add_define("USE_PSRAM") diff --git a/esphome/components/psram/psram.cpp b/esphome/components/psram/psram.cpp index d9a5bd101f..f592ada246 100644 --- a/esphome/components/psram/psram.cpp +++ b/esphome/components/psram/psram.cpp @@ -1,25 +1,36 @@ -#include "psram.h" #ifdef USE_ESP32 +#include "psram.h" +#ifdef USE_ESP_IDF +#include +#endif // USE_ESP_IDF #include "esphome/core/log.h" #include -#include namespace esphome { namespace psram { - static const char *const TAG = "psram"; void PsramComponent::dump_config() { + ESP_LOGCONFIG(TAG, "PSRAM:"); +#ifdef USE_ESP_IDF + bool available = esp_psram_is_initialized(); + + ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available)); + if (available) { + ESP_LOGCONFIG(TAG, " Size: %zu KB", esp_psram_get_size() / 1024); +#if CONFIG_SPIRAM_ECC_ENABLE + ESP_LOGCONFIG(TAG, " ECC enabled: YES"); +#endif + } +#else // Technically this can be false if the PSRAM is full, but heap_caps_get_total_size() isn't always available, and it's // very unlikely for the PSRAM to be full. bool available = heap_caps_get_free_size(MALLOC_CAP_SPIRAM) > 0; - - ESP_LOGCONFIG(TAG, "PSRAM:"); ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available)); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) + if (available) { const size_t psram_total_size_bytes = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); const float psram_total_size_kb = psram_total_size_bytes / 1024.0f; @@ -30,7 +41,7 @@ void PsramComponent::dump_config() { ESP_LOGCONFIG(TAG, " Size: %zu bytes", psram_total_size_bytes); } } -#endif +#endif // USE_ESP_IDF } } // namespace psram diff --git a/tests/components/psram/common.yaml b/tests/components/psram/common.yaml index cfd39f77fe..9c6e3f2a60 100644 --- a/tests/components/psram/common.yaml +++ b/tests/components/psram/common.yaml @@ -1,3 +1,2 @@ psram: - mode: octal speed: 80MHz diff --git a/tests/components/psram/test.esp32-c3-ard.yaml b/tests/components/psram/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/psram/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-c3-idf.yaml b/tests/components/psram/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/psram/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-s3-ard.yaml b/tests/components/psram/test.esp32-s3-ard.yaml index dade44d145..cfd39f77fe 100644 --- a/tests/components/psram/test.esp32-s3-ard.yaml +++ b/tests/components/psram/test.esp32-s3-ard.yaml @@ -1 +1,3 @@ -<<: !include common.yaml +psram: + mode: octal + speed: 80MHz diff --git a/tests/components/psram/test.esp32-s3-idf.yaml b/tests/components/psram/test.esp32-s3-idf.yaml index dade44d145..e0e7fb52f6 100644 --- a/tests/components/psram/test.esp32-s3-idf.yaml +++ b/tests/components/psram/test.esp32-s3-idf.yaml @@ -1 +1,10 @@ -<<: !include common.yaml +esp32: + framework: + type: esp-idf + advanced: + enable_idf_experimental_features: yes + +psram: + mode: octal + speed: 120MHz + enable_ecc: true