From b7ca6e087a2c69f0a9e2aef621af054dd29a6af5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 Jun 2025 01:08:10 -0500 Subject: [PATCH] Add LWIP optimization options to reduce flash usage (#8946) Co-authored-by: Keith Burzinski --- esphome/components/esp32/__init__.py | 40 +++++++++++++++++++--- esphome/config_validation.py | 20 +++++++++++ tests/components/esp32/test.esp32-idf.yaml | 12 +++++++ 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 tests/components/esp32/test.esp32-idf.yaml diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 5b7412a7ac..4037fa3332 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -558,6 +558,10 @@ ARDUINO_FRAMEWORK_SCHEMA = cv.All( ) CONF_SDKCONFIG_OPTIONS = "sdkconfig_options" +CONF_ENABLE_LWIP_DHCP_SERVER = "enable_lwip_dhcp_server" +CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries" +CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface" + ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { @@ -582,6 +586,18 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False ): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, + # DHCP server is needed for WiFi AP mode. When WiFi component is used, + # it will handle disabling DHCP server when AP is not configured. + # Default to false (disabled) when WiFi is not used. + cv.OnlyWithout( + CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_MDNS_QUERIES, default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False + ): cv.boolean, } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -664,7 +680,7 @@ async def to_code(config): conf = config[CONF_FRAMEWORK] cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION]) - if CONF_ADVANCED in conf and conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: + if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") add_extra_script( @@ -708,18 +724,32 @@ async def to_code(config): # Set default CPU frequency add_idf_sdkconfig_option(f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{freq}", True) + # Apply LWIP optimization settings + advanced = conf[CONF_ADVANCED] + # DHCP server: only disable if explicitly set to false + # WiFi component handles its own optimization when AP mode is not used + if ( + CONF_ENABLE_LWIP_DHCP_SERVER in advanced + 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): + 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) + cg.add_platformio_option("board_build.partitions", "partitions.csv") if CONF_PARTITIONS in config: add_extra_build_file( "partitions.csv", CORE.relative_config_path(config[CONF_PARTITIONS]) ) - if assertion_level := conf[CONF_ADVANCED].get(CONF_ASSERTION_LEVEL): + if assertion_level := advanced.get(CONF_ASSERTION_LEVEL): for key, flag in ASSERTION_LEVELS.items(): add_idf_sdkconfig_option(flag, assertion_level == key) add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) - compiler_optimization = conf[CONF_ADVANCED].get(CONF_COMPILER_OPTIMIZATION) + compiler_optimization = advanced.get(CONF_COMPILER_OPTIMIZATION) for key, flag in COMPILER_OPTIMIZATIONS.items(): add_idf_sdkconfig_option(flag, compiler_optimization == key) @@ -728,7 +758,7 @@ async def to_code(config): conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT], ) - if conf[CONF_ADVANCED].get(CONF_IGNORE_EFUSE_MAC_CRC): + if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC): add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True) if (framework_ver.major, framework_ver.minor) >= (4, 4): add_idf_sdkconfig_option( @@ -738,7 +768,7 @@ 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): + if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): _LOGGER.warning( "Using experimental features in ESP-IDF may result in unexpected failures." ) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 54056240ce..964f533215 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1667,6 +1667,26 @@ class OnlyWith(Optional): pass +class OnlyWithout(Optional): + """Set the default value only if the given component is NOT loaded.""" + + def __init__(self, key, component, default=None): + super().__init__(key) + self._component = component + self._default = vol.default_factory(default) + + @property + def default(self): + if self._component not in CORE.loaded_integrations: + return self._default + return vol.UNDEFINED + + @default.setter + def default(self, value): + # Ignore default set from vol.Optional + pass + + def _entity_base_validator(config): if CONF_NAME not in config and CONF_ID not in config: raise Invalid("At least one of 'id:' or 'name:' is required!") diff --git a/tests/components/esp32/test.esp32-idf.yaml b/tests/components/esp32/test.esp32-idf.yaml new file mode 100644 index 0000000000..582b2c22ce --- /dev/null +++ b/tests/components/esp32/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +esp32: + board: esp32dev + framework: + type: esp-idf + advanced: + enable_lwip_mdns_queries: true + enable_lwip_bridge_interface: true + +wifi: + ssid: MySSID + password: password1 +