From 4f5aacdb3a113819294e2fcb8c9700d6dc44a12d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 01:25:26 +0200 Subject: [PATCH 1/9] Optimize SafeModeComponent memory layout to reduce padding (#9228) --- esphome/components/safe_mode/safe_mode.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/safe_mode/safe_mode.h b/esphome/components/safe_mode/safe_mode.h index 37e2c3a3d6..028b7b11cb 100644 --- a/esphome/components/safe_mode/safe_mode.h +++ b/esphome/components/safe_mode/safe_mode.h @@ -33,12 +33,15 @@ class SafeModeComponent : public Component { void write_rtc_(uint32_t val); uint32_t read_rtc_(); - bool boot_successful_{false}; ///< set to true after boot is considered successful + // Group all 4-byte aligned members together to avoid padding uint32_t safe_mode_boot_is_good_after_{60000}; ///< The amount of time after which the boot is considered successful uint32_t safe_mode_enable_time_{60000}; ///< The time safe mode should remain active for uint32_t safe_mode_rtc_value_{0}; uint32_t safe_mode_start_time_{0}; ///< stores when safe mode was enabled + // Group 1-byte members together to minimize padding + bool boot_successful_{false}; ///< set to true after boot is considered successful uint8_t safe_mode_num_attempts_{0}; + // Larger objects at the end ESPPreferenceObject rtc_; CallbackManager safe_mode_callback_{}; From 87321ce10b8611a5f8ea14787e497dcc8a278051 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:51:13 -0400 Subject: [PATCH 2/9] [esp32_hosted] Add support for remote wifi (#8833) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/esp32_hosted/__init__.py | 101 ++++++++++++++++++ .../esp32_hosted/esp32_hosted.py.script | 12 +++ esphome/idf_component.yml | 12 +++ tests/components/esp32_hosted/common.yaml | 15 +++ .../esp32_hosted/test.esp32-p4-idf.yaml | 1 + 6 files changed, 142 insertions(+) create mode 100644 esphome/components/esp32_hosted/__init__.py create mode 100644 esphome/components/esp32_hosted/esp32_hosted.py.script create mode 100644 tests/components/esp32_hosted/common.yaml create mode 100644 tests/components/esp32_hosted/test.esp32-p4-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 4eb4c42a6d..832c571ae4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -146,6 +146,7 @@ esphome/components/esp32_ble_client/* @jesserockz esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz esphome/components/esp32_camera_web_server/* @ayufan esphome/components/esp32_can/* @Sympatron +esphome/components/esp32_hosted/* @swoboda1337 esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_rmt/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz diff --git a/esphome/components/esp32_hosted/__init__.py b/esphome/components/esp32_hosted/__init__.py new file mode 100644 index 0000000000..330800df12 --- /dev/null +++ b/esphome/components/esp32_hosted/__init__.py @@ -0,0 +1,101 @@ +import os + +from esphome import pins +from esphome.components import esp32 +import esphome.config_validation as cv +from esphome.const import ( + CONF_CLK_PIN, + CONF_RESET_PIN, + CONF_VARIANT, + KEY_CORE, + KEY_FRAMEWORK_VERSION, +) +from esphome.core import CORE + +CODEOWNERS = ["@swoboda1337"] + +CONF_ACTIVE_HIGH = "active_high" +CONF_CMD_PIN = "cmd_pin" +CONF_D0_PIN = "d0_pin" +CONF_D1_PIN = "d1_pin" +CONF_D2_PIN = "d2_pin" +CONF_D3_PIN = "d3_pin" +CONF_SLOT = "slot" + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_VARIANT): cv.one_of(*esp32.VARIANTS, upper=True), + cv.Required(CONF_ACTIVE_HIGH): cv.boolean, + cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_CMD_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_D0_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_D1_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_D2_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_D3_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_SLOT, default=1): cv.int_range(min=0, max=1), + } + ), +) + + +async def to_code(config): + if config[CONF_ACTIVE_HIGH]: + esp32.add_idf_sdkconfig_option( + "CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH", + True, + ) + else: + esp32.add_idf_sdkconfig_option( + "CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_LOW", + True, + ) + esp32.add_idf_sdkconfig_option( + "CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE", # NOLINT + config[CONF_RESET_PIN], + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_SLAVE_IDF_TARGET_{config[CONF_VARIANT]}", # NOLINT + True, + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_SDIO_SLOT_{config[CONF_SLOT]}", + True, + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_{config[CONF_SLOT]}", + config[CONF_CLK_PIN], + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_{config[CONF_SLOT]}", + config[CONF_CMD_PIN], + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_{config[CONF_SLOT]}", + config[CONF_D0_PIN], + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_{config[CONF_SLOT]}", + config[CONF_D1_PIN], + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_{config[CONF_SLOT]}", + config[CONF_D2_PIN], + ) + esp32.add_idf_sdkconfig_option( + f"CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_{config[CONF_SLOT]}", + config[CONF_D3_PIN], + ) + esp32.add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_CUSTOM_SDIO_PINS", True) + + framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + os.environ["ESP_IDF_VERSION"] = f"{framework_ver.major}.{framework_ver.minor}" + esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="0.10.2") + esp32.add_idf_component(name="espressif/eppp_link", ref="0.2.0") + esp32.add_idf_component(name="espressif/esp_hosted", ref="2.0.11") + esp32.add_extra_script( + "post", + "esp32_hosted.py", + os.path.join(os.path.dirname(__file__), "esp32_hosted.py.script"), + ) diff --git a/esphome/components/esp32_hosted/esp32_hosted.py.script b/esphome/components/esp32_hosted/esp32_hosted.py.script new file mode 100644 index 0000000000..4be297c500 --- /dev/null +++ b/esphome/components/esp32_hosted/esp32_hosted.py.script @@ -0,0 +1,12 @@ +# pylint: disable=E0602 +Import("env") # noqa + +# Workaround whole archive issue +if "__LIB_DEPS" in env and "libespressif__esp_hosted.a" in env["__LIB_DEPS"]: + env.Append( + LINKFLAGS=[ + "-Wl,--whole-archive", + env["BUILD_DIR"] + "/esp-idf/espressif__esp_hosted/libespressif__esp_hosted.a", + "-Wl,--no-whole-archive", + ] + ) diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 8460de5638..7dcfe918eb 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -11,3 +11,15 @@ dependencies: path: components/mdns rules: - if: "idf_version >=5.0" + espressif/esp_wifi_remote: + version: 0.10.2 + rules: + - if: "target in [esp32h2, esp32p4]" + espressif/eppp_link: + version: 0.2.0 + rules: + - if: "target in [esp32h2, esp32p4]" + espressif/esp_hosted: + version: 2.0.11 + rules: + - if: "target in [esp32h2, esp32p4]" diff --git a/tests/components/esp32_hosted/common.yaml b/tests/components/esp32_hosted/common.yaml new file mode 100644 index 0000000000..ab029e5064 --- /dev/null +++ b/tests/components/esp32_hosted/common.yaml @@ -0,0 +1,15 @@ +esp32_hosted: + variant: ESP32C6 + slot: 1 + active_high: true + reset_pin: GPIO15 + cmd_pin: GPIO13 + clk_pin: GPIO12 + d0_pin: GPIO11 + d1_pin: GPIO10 + d2_pin: GPIO9 + d3_pin: GPIO8 + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/esp32_hosted/test.esp32-p4-idf.yaml b/tests/components/esp32_hosted/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/esp32_hosted/test.esp32-p4-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 61dfd5541f491d3306fc38106e5a8ba08513185f Mon Sep 17 00:00:00 2001 From: Thomas Rupprecht Date: Fri, 27 Jun 2025 04:40:42 +0200 Subject: [PATCH 3/9] use c++17 `[[fallthrough]];` (#9149) --- esphome/components/audio/audio_decoder.cpp | 2 +- .../esp32_ble_client/ble_client_base.cpp | 10 +++++----- esphome/components/pn7150/pn7150.cpp | 14 +++++++------- esphome/components/pn7160/pn7160.cpp | 14 +++++++------- esphome/components/shelly_dimmer/stm32flash.cpp | 3 +-- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/esphome/components/audio/audio_decoder.cpp b/esphome/components/audio/audio_decoder.cpp index c74b028c4b..90ba1aec1e 100644 --- a/esphome/components/audio/audio_decoder.cpp +++ b/esphome/components/audio/audio_decoder.cpp @@ -312,7 +312,7 @@ FileDecoderState AudioDecoder::decode_mp3_() { if (err) { switch (err) { case esp_audio_libs::helix_decoder::ERR_MP3_OUT_OF_MEMORY: - // Intentional fallthrough + [[fallthrough]]; case esp_audio_libs::helix_decoder::ERR_MP3_NULL_POINTER: return FileDecoderState::FAILED; break; diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 8ae1eb1bac..7d0a3bbfd5 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -496,17 +496,17 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { if (length > 2) { return (float) encode_uint16(value[1], value[2]); } - // fall through + [[fallthrough]]; case 0x7: // uint24. if (length > 3) { return (float) encode_uint24(value[1], value[2], value[3]); } - // fall through + [[fallthrough]]; case 0x8: // uint32. if (length > 4) { return (float) encode_uint32(value[1], value[2], value[3], value[4]); } - // fall through + [[fallthrough]]; case 0xC: // int8. return (float) ((int8_t) value[1]); case 0xD: // int12. @@ -514,12 +514,12 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { if (length > 2) { return (float) ((int16_t) (value[1] << 8) + (int16_t) value[2]); } - // fall through + [[fallthrough]]; case 0xF: // int24. if (length > 3) { return (float) ((int32_t) (value[1] << 16) + (int32_t) (value[2] << 8) + (int32_t) (value[3])); } - // fall through + [[fallthrough]]; case 0x10: // int32. if (length > 4) { return (float) ((int32_t) (value[1] << 24) + (int32_t) (value[2] << 16) + (int32_t) (value[3] << 8) + diff --git a/esphome/components/pn7150/pn7150.cpp b/esphome/components/pn7150/pn7150.cpp index 971ddd23cb..f827bd151a 100644 --- a/esphome/components/pn7150/pn7150.cpp +++ b/esphome/components/pn7150/pn7150.cpp @@ -584,7 +584,7 @@ void PN7150::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::NFCC_INIT); } - // fall through + [[fallthrough]]; case NCIState::NFCC_INIT: if (this->init_core_() != nfc::STATUS_OK) { @@ -594,7 +594,7 @@ void PN7150::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::NFCC_CONFIG); } - // fall through + [[fallthrough]]; case NCIState::NFCC_CONFIG: if (this->send_init_config_() != nfc::STATUS_OK) { @@ -605,7 +605,7 @@ void PN7150::nci_fsm_transition_() { this->config_refresh_pending_ = false; this->nci_fsm_set_state_(NCIState::NFCC_SET_DISCOVER_MAP); } - // fall through + [[fallthrough]]; case NCIState::NFCC_SET_DISCOVER_MAP: if (this->set_discover_map_() != nfc::STATUS_OK) { @@ -615,7 +615,7 @@ void PN7150::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::NFCC_SET_LISTEN_MODE_ROUTING); } - // fall through + [[fallthrough]]; case NCIState::NFCC_SET_LISTEN_MODE_ROUTING: if (this->set_listen_mode_routing_() != nfc::STATUS_OK) { @@ -625,7 +625,7 @@ void PN7150::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::RFST_IDLE); } - // fall through + [[fallthrough]]; case NCIState::RFST_IDLE: if (this->nci_state_error_ == NCIState::RFST_DISCOVERY) { @@ -650,14 +650,14 @@ void PN7150::nci_fsm_transition_() { case NCIState::RFST_W4_HOST_SELECT: select_endpoint_(); - // fall through + [[fallthrough]]; // All cases below are waiting for NOTIFICATION messages case NCIState::RFST_DISCOVERY: if (this->config_refresh_pending_) { this->refresh_core_config_(); } - // fall through + [[fallthrough]]; case NCIState::RFST_LISTEN_ACTIVE: case NCIState::RFST_LISTEN_SLEEP: diff --git a/esphome/components/pn7160/pn7160.cpp b/esphome/components/pn7160/pn7160.cpp index 2a1de20657..a8edfadd8e 100644 --- a/esphome/components/pn7160/pn7160.cpp +++ b/esphome/components/pn7160/pn7160.cpp @@ -609,7 +609,7 @@ void PN7160::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::NFCC_INIT); } - // fall through + [[fallthrough]]; case NCIState::NFCC_INIT: if (this->init_core_() != nfc::STATUS_OK) { @@ -619,7 +619,7 @@ void PN7160::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::NFCC_CONFIG); } - // fall through + [[fallthrough]]; case NCIState::NFCC_CONFIG: if (this->send_init_config_() != nfc::STATUS_OK) { @@ -630,7 +630,7 @@ void PN7160::nci_fsm_transition_() { this->config_refresh_pending_ = false; this->nci_fsm_set_state_(NCIState::NFCC_SET_DISCOVER_MAP); } - // fall through + [[fallthrough]]; case NCIState::NFCC_SET_DISCOVER_MAP: if (this->set_discover_map_() != nfc::STATUS_OK) { @@ -640,7 +640,7 @@ void PN7160::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::NFCC_SET_LISTEN_MODE_ROUTING); } - // fall through + [[fallthrough]]; case NCIState::NFCC_SET_LISTEN_MODE_ROUTING: if (this->set_listen_mode_routing_() != nfc::STATUS_OK) { @@ -650,7 +650,7 @@ void PN7160::nci_fsm_transition_() { } else { this->nci_fsm_set_state_(NCIState::RFST_IDLE); } - // fall through + [[fallthrough]]; case NCIState::RFST_IDLE: if (this->nci_state_error_ == NCIState::RFST_DISCOVERY) { @@ -675,14 +675,14 @@ void PN7160::nci_fsm_transition_() { case NCIState::RFST_W4_HOST_SELECT: select_endpoint_(); - // fall through + [[fallthrough]]; // All cases below are waiting for NOTIFICATION messages case NCIState::RFST_DISCOVERY: if (this->config_refresh_pending_) { this->refresh_core_config_(); } - // fall through + [[fallthrough]]; case NCIState::RFST_LISTEN_ACTIVE: case NCIState::RFST_LISTEN_SLEEP: diff --git a/esphome/components/shelly_dimmer/stm32flash.cpp b/esphome/components/shelly_dimmer/stm32flash.cpp index 3871d89a2f..b052c0cee9 100644 --- a/esphome/components/shelly_dimmer/stm32flash.cpp +++ b/esphome/components/shelly_dimmer/stm32flash.cpp @@ -445,8 +445,7 @@ template stm32_err_t stm32_check_ack_timeout(const stm32_err_t s_err return STM32_ERR_OK; case STM32_ERR_NACK: log(); - // TODO: c++17 [[fallthrough]] - /* fallthrough */ + [[fallthrough]]; default: return STM32_ERR_UNKNOWN; } From 1f94e4cc14f5225270875f62d83d3b7b45d6f80e Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 26 Jun 2025 23:37:30 -0400 Subject: [PATCH 4/9] [esp32] Update IDF components to use the registry (#9223) --- esphome/components/esp32_camera/__init__.py | 6 +----- esphome/components/mdns/__init__.py | 7 +------ esphome/components/micro_wake_word/__init__.py | 6 +----- esphome/idf_component.yml | 18 ++++++------------ 4 files changed, 9 insertions(+), 28 deletions(-) diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index cfca0ed6fc..8dc2ede372 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -310,11 +310,7 @@ async def to_code(config): cg.add_define("USE_ESP32_CAMERA") if CORE.using_esp_idf: - add_idf_component( - name="esp32-camera", - repo="https://github.com/espressif/esp32-camera.git", - ref="v2.0.15", - ) + add_idf_component(name="espressif/esp32-camera", ref="2.0.15") for conf in config.get(CONF_ON_STREAM_START, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 4b5e40dfea..2f81068e8a 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -88,12 +88,7 @@ async def to_code(config): if CORE.using_esp_idf and CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version( 5, 0, 0 ): - add_idf_component( - name="mdns", - repo="https://github.com/espressif/esp-protocols.git", - ref="mdns-v1.8.2", - path="components/mdns", - ) + add_idf_component(name="espressif/mdns", ref="1.8.2") cg.add_define("USE_MDNS") diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 0efe2ac288..cde8752157 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -449,11 +449,7 @@ async def to_code(config): cg.add_define("USE_MICRO_WAKE_WORD") cg.add_define("USE_OTA_STATE_CALLBACK") - esp32.add_idf_component( - name="esp-tflite-micro", - repo="https://github.com/espressif/esp-tflite-micro", - ref="v1.3.3.1", - ) + esp32.add_idf_component(name="espressif/esp-tflite-micro", ref="1.3.3~1") cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 7dcfe918eb..6299909033 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -1,16 +1,10 @@ dependencies: - esp-tflite-micro: - git: https://github.com/espressif/esp-tflite-micro.git - version: v1.3.1 - esp32_camera: - git: https://github.com/espressif/esp32-camera.git - version: v2.0.15 - mdns: - git: https://github.com/espressif/esp-protocols.git - version: mdns-v1.8.2 - path: components/mdns - rules: - - if: "idf_version >=5.0" + espressif/esp-tflite-micro: + version: 1.3.3~1 + espressif/esp32-camera: + version: 2.0.15 + espressif/mdns: + version: 1.8.2 espressif/esp_wifi_remote: version: 0.10.2 rules: From 62f28902c5d54793efeded201eb4059074bdf9e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 08:50:26 +0200 Subject: [PATCH 5/9] [wifi] Reduce memory usage (#9232) --- esphome/components/wifi/wifi_component.h | 48 +++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index efd43077d1..64797a5801 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -62,7 +62,7 @@ struct SavedWifiFastConnectSettings { uint8_t channel; } PACKED; // NOLINT -enum WiFiComponentState { +enum WiFiComponentState : uint8_t { /** Nothing has been initialized yet. Internal AP, if configured, is disabled at this point. */ WIFI_COMPONENT_STATE_OFF = 0, /** WiFi is disabled. */ @@ -146,14 +146,14 @@ class WiFiAP { protected: std::string ssid_; - optional bssid_; std::string password_; + optional bssid_; #ifdef USE_WIFI_WPA2_EAP optional eap_; #endif // USE_WIFI_WPA2_EAP - optional channel_; - float priority_{0}; optional manual_ip_; + float priority_{0}; + optional channel_; bool hidden_{false}; }; @@ -177,14 +177,14 @@ class WiFiScanResult { bool operator==(const WiFiScanResult &rhs) const; protected: - bool matches_{false}; bssid_t bssid_; std::string ssid_; + float priority_{0.0f}; uint8_t channel_; int8_t rssi_; + bool matches_{false}; bool with_auth_; bool is_hidden_; - float priority_{0.0f}; }; struct WiFiSTAPriority { @@ -192,7 +192,7 @@ struct WiFiSTAPriority { float priority; }; -enum WiFiPowerSaveMode { +enum WiFiPowerSaveMode : uint8_t { WIFI_POWER_SAVE_NONE = 0, WIFI_POWER_SAVE_LIGHT, WIFI_POWER_SAVE_HIGH, @@ -383,28 +383,36 @@ class WiFiComponent : public Component { std::string use_address_; std::vector sta_; std::vector sta_priorities_; + std::vector scan_result_; WiFiAP selected_ap_; - bool fast_connect_{false}; - bool retry_hidden_{false}; - - bool has_ap_{false}; WiFiAP ap_; - WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; - bool handled_connected_state_{false}; + optional output_power_; + ESPPreferenceObject pref_; + ESPPreferenceObject fast_connect_pref_; + + // Group all 32-bit integers together uint32_t action_started_; - uint8_t num_retried_{0}; uint32_t last_connected_{0}; uint32_t reboot_timeout_{}; uint32_t ap_timeout_{}; + + // Group all 8-bit values together + WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE}; + uint8_t num_retried_{0}; +#if USE_NETWORK_IPV6 + uint8_t num_ipv6_addresses_{0}; +#endif /* USE_NETWORK_IPV6 */ + + // Group all boolean values together + bool fast_connect_{false}; + bool retry_hidden_{false}; + bool has_ap_{false}; + bool handled_connected_state_{false}; bool error_from_callback_{false}; - std::vector scan_result_; bool scan_done_{false}; bool ap_setup_{false}; - optional output_power_; bool passive_scan_{false}; - ESPPreferenceObject pref_; - ESPPreferenceObject fast_connect_pref_; bool has_saved_wifi_settings_{false}; #ifdef USE_WIFI_11KV_SUPPORT bool btm_{false}; @@ -412,10 +420,8 @@ class WiFiComponent : public Component { #endif bool enable_on_boot_; bool got_ipv4_address_{false}; -#if USE_NETWORK_IPV6 - uint8_t num_ipv6_addresses_{0}; -#endif /* USE_NETWORK_IPV6 */ + // Pointers at the end (naturally aligned) Trigger<> *connect_trigger_{new Trigger<>()}; Trigger<> *disconnect_trigger_{new Trigger<>()}; }; From 7931423e8c99cd0a22d16f2f84d75c0d4f79aed6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 08:52:12 +0200 Subject: [PATCH 6/9] Reduce ethernet component memory usage by 8 bytes (#9231) --- .../components/ethernet/ethernet_component.h | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 7a205d89f0..0f0eff5ded 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -15,7 +15,7 @@ namespace esphome { namespace ethernet { -enum EthernetType { +enum EthernetType : uint8_t { ETHERNET_TYPE_UNKNOWN = 0, ETHERNET_TYPE_LAN8720, ETHERNET_TYPE_RTL8201, @@ -42,7 +42,7 @@ struct PHYRegister { uint32_t page; }; -enum class EthernetComponentState { +enum class EthernetComponentState : uint8_t { STOPPED, CONNECTING, CONNECTED, @@ -119,25 +119,31 @@ class EthernetComponent : public Component { uint32_t polling_interval_{0}; #endif #else - uint8_t phy_addr_{0}; + // Group all 32-bit members first int power_pin_{-1}; - uint8_t mdc_pin_{23}; - uint8_t mdio_pin_{18}; emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO}; std::vector phy_registers_{}; -#endif - EthernetType type_{ETHERNET_TYPE_UNKNOWN}; - optional manual_ip_{}; + // Group all 8-bit members together + uint8_t phy_addr_{0}; + uint8_t mdc_pin_{23}; + uint8_t mdio_pin_{18}; +#endif + optional manual_ip_{}; + uint32_t connect_begin_; + + // Group all uint8_t types together (enums and bools) + EthernetType type_{ETHERNET_TYPE_UNKNOWN}; + EthernetComponentState state_{EthernetComponentState::STOPPED}; bool started_{false}; bool connected_{false}; bool got_ipv4_address_{false}; #if LWIP_IPV6 uint8_t ipv6_count_{0}; #endif /* LWIP_IPV6 */ - EthernetComponentState state_{EthernetComponentState::STOPPED}; - uint32_t connect_begin_; + + // Pointers at the end (naturally aligned) esp_netif_t *eth_netif_{nullptr}; esp_eth_handle_t eth_handle_; esp_eth_phy_t *phy_{nullptr}; From 13512440ac57954c0246933a9f80e9455a1ab1d0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 08:53:40 +0200 Subject: [PATCH 7/9] [gpio] Reduce ESP32 memory usage by optimizing struct padding (#9230) --- esphome/components/esp32/gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32/gpio.h b/esphome/components/esp32/gpio.h index d69ac1c493..0fefc1c058 100644 --- a/esphome/components/esp32/gpio.h +++ b/esphome/components/esp32/gpio.h @@ -29,9 +29,9 @@ class ESP32InternalGPIOPin : public InternalGPIOPin { void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; gpio_num_t pin_; - bool inverted_; gpio_drive_cap_t drive_strength_; gpio::Flags flags_; + bool inverted_; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static bool isr_service_installed; }; From 837dd46adffe2f6dbb4184f54b5a34b79acb8bec Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 08:56:54 +0200 Subject: [PATCH 8/9] Reduce component_iterator memory usage (#9205) --- esphome/core/component_iterator.cpp | 2 +- esphome/core/component_iterator.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/core/component_iterator.cpp b/esphome/core/component_iterator.cpp index da593340c1..b06c964b7c 100644 --- a/esphome/core/component_iterator.cpp +++ b/esphome/core/component_iterator.cpp @@ -375,7 +375,7 @@ void ComponentIterator::advance() { } if (advance_platform) { - this->state_ = static_cast(static_cast(this->state_) + 1); + this->state_ = static_cast(static_cast(this->state_) + 1); this->at_ = 0; } else if (success) { this->at_++; diff --git a/esphome/core/component_iterator.h b/esphome/core/component_iterator.h index 9e187f6c57..4b41872db7 100644 --- a/esphome/core/component_iterator.h +++ b/esphome/core/component_iterator.h @@ -93,7 +93,9 @@ class ComponentIterator { virtual bool on_end(); protected: - enum class IteratorState { + // Iterates over all ESPHome entities (sensors, switches, lights, etc.) + // Supports up to 256 entity types and up to 65,535 entities of each type + enum class IteratorState : uint8_t { NONE = 0, BEGIN, #ifdef USE_BINARY_SENSOR @@ -167,7 +169,7 @@ class ComponentIterator { #endif MAX, } state_{IteratorState::NONE}; - size_t at_{0}; + uint16_t at_{0}; // Supports up to 65,535 entities per type bool include_internal_{false}; }; From c0b1f32889c6e240aa8b52b0e19d708a2c2342a1 Mon Sep 17 00:00:00 2001 From: scaiper Date: Fri, 27 Jun 2025 13:43:18 +0300 Subject: [PATCH 9/9] [esp32] Change ``enable_lwip_mdns_queries`` default to ``True`` (#9188) --- esphome/components/esp32/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 4e2a6ab852..8319ed5e74 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -610,7 +610,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False ): cv.boolean, cv.Optional( - CONF_ENABLE_LWIP_MDNS_QUERIES, default=False + CONF_ENABLE_LWIP_MDNS_QUERIES, default=True ): cv.boolean, cv.Optional( CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False @@ -770,7 +770,7 @@ async def to_code(config): and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER] ): add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) - if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, False): + if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, True): add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False) if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False): add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)