From e706bdbadd95e4fb041b03ddf08f178c1ac1487d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Apr 2026 11:04:21 -1000 Subject: [PATCH] [remote_receiver] Place RMT receive functions in IRAM for cache safety When flash cache is disabled during background flash operations (NVS writes by WiFi, BLE, Zigbee, Thread, power management, etc.), the RMT receive function will crash if it is in flash. This places the RMT receive functions in IRAM by setting CONFIG_RMT_RECV_FUNC_IN_IRAM when the remote_receiver component is used. Adds require_rmt_recv_iram() helper and rmt_recv_in_iram advanced config option. --- esphome/components/esp32/__init__.py | 24 +++++++++++++++++++ .../components/remote_receiver/__init__.py | 1 + 2 files changed, 25 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 2974028b50..a352a8d343 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -1058,6 +1058,7 @@ CONF_DISABLE_MBEDTLS_PEER_CERT = "disable_mbedtls_peer_cert" CONF_DISABLE_MBEDTLS_PKCS7 = "disable_mbedtls_pkcs7" CONF_DISABLE_REGI2C_IN_IRAM = "disable_regi2c_in_iram" CONF_DISABLE_FATFS = "disable_fatfs" +CONF_RMT_RECV_IN_IRAM = "rmt_recv_in_iram" # VFS requirement tracking # Components that need VFS features can call require_vfs_*() functions @@ -1071,6 +1072,7 @@ KEY_MBEDTLS_PEER_CERT_REQUIRED = "mbedtls_peer_cert_required" KEY_MBEDTLS_PKCS7_REQUIRED = "mbedtls_pkcs7_required" KEY_FATFS_REQUIRED = "fatfs_required" KEY_MBEDTLS_SHA512_REQUIRED = "mbedtls_sha512_required" +KEY_RMT_RECV_IRAM_REQUIRED = "rmt_recv_iram_required" def require_vfs_select() -> None: @@ -1168,6 +1170,17 @@ def require_fatfs() -> None: CORE.data[KEY_ESP32][KEY_FATFS_REQUIRED] = True +def require_rmt_recv_iram() -> None: + """Mark that RMT receive IRAM safety is required by a component. + + Call this from components that use the RMT receive driver. When flash cache is + disabled (e.g., during NVS writes by WiFi, BLE, Zigbee, or power management), + the RMT receive function must be in IRAM to avoid crashes. + This sets CONFIG_RMT_RECV_FUNC_IN_IRAM. + """ + CORE.data[KEY_ESP32][KEY_RMT_RECV_IRAM_REQUIRED] = True + + def _parse_idf_component(value: str) -> ConfigType: """Parse IDF component shorthand syntax like 'owner/component^version'""" # Match operator followed by version-like string (digit or *) @@ -1268,6 +1281,7 @@ FRAMEWORK_SCHEMA = cv.Schema( cv.Optional(CONF_DISABLE_MBEDTLS_PEER_CERT, default=True): cv.boolean, cv.Optional(CONF_DISABLE_MBEDTLS_PKCS7, default=True): cv.boolean, cv.Optional(CONF_DISABLE_REGI2C_IN_IRAM, default=True): cv.boolean, + cv.Optional(CONF_RMT_RECV_IN_IRAM, default=False): cv.boolean, cv.Optional(CONF_DISABLE_FATFS, default=True): cv.boolean, } ), @@ -2068,6 +2082,16 @@ async def to_code(config): if advanced[CONF_DISABLE_REGI2C_IN_IRAM]: add_idf_sdkconfig_option("CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM", False) + # Place RMT receive functions in IRAM for cache safety + # When flash cache is disabled (during NVS writes by WiFi, BLE, Zigbee, Thread, + # power management, etc.), RMT receive will crash if these functions are in flash. + # Components using RMT receive call require_rmt_recv_iram() to force this. + if ( + CORE.data[KEY_ESP32].get(KEY_RMT_RECV_IRAM_REQUIRED, False) + or advanced[CONF_RMT_RECV_IN_IRAM] + ): + add_idf_sdkconfig_option("CONFIG_RMT_RECV_FUNC_IN_IRAM", True) + # Disable FATFS support # Components that need FATFS (SD card, etc.) can call require_fatfs() if CORE.data[KEY_ESP32].get(KEY_FATFS_REQUIRED, False): diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 53a0f8fb77..6937764683 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -201,6 +201,7 @@ async def to_code(config): if CORE.is_esp32 and esp32.get_esp32_variant() not in esp32_rmt.VARIANTS_NO_RMT: # Re-enable ESP-IDF's RMT driver (excluded by default to save compile time) esp32.include_builtin_idf_component("esp_driver_rmt") + esp32.require_rmt_recv_iram() var = cg.new_Pvariable(config[CONF_ID], pin) cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))