diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index bc91f29a42..48bb7bf6a1 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -214,11 +214,13 @@ COMPILER_OPTIMIZATIONS = { # builds that need them. DEFAULT_EXCLUDED_IDF_COMPONENTS = ( "app_trace", # CPU trace/SystemView support - unused by ESPHome + "bt", # Bluetooth stack - re-included by request_bluetooth(); its REQUIRES pulls the WiFi stack back "cmock", # Unit testing mock framework - ESPHome doesn't use IDF's testing "console", # Console REPL - unused by ESPHome; espressif/mdns pulls it back when configured "driver", # Legacy driver shim - only needed by esp32_touch, esp32_can for legacy headers "esp-tls", # TLS wrapper - re-included by http_request, mqtt, web_server_idf "esp_adc", # ADC driver - only needed by adc component + "esp_coex", # WiFi/BT coexistence - re-included by esp32_ble_tracker, zigbee; esp_wifi/bt pull it back "esp_driver_cam", # Camera driver - the esp32-camera managed component pulls it back "esp_driver_dac", # DAC driver - only needed by esp32_dac component "esp_driver_gptimer", # General purpose timer - re-included by ac_dimmer, opentherm, Arduino BLE libs @@ -236,6 +238,7 @@ DEFAULT_EXCLUDED_IDF_COMPONENTS = ( "esp_driver_twai", # TWAI/CAN driver - only needed by esp32_can component "esp_eth", # Ethernet driver - only needed by ethernet component "esp_gdbstub", # GDB stub panic handler - unused by ESPHome; bt pulls it back + "esp_hal_ieee802154", # 802.15.4 HAL - ieee802154 pulls it back "esp_hid", # HID host/device support - ESPHome doesn't implement HID functionality "esp_http_client", # HTTP client - only needed by http_request component "esp_http_server", # HTTP server - re-included by web_server_idf, esp32_camera_web_server @@ -243,8 +246,11 @@ DEFAULT_EXCLUDED_IDF_COMPONENTS = ( "esp_https_server", # HTTPS server - ESPHome has its own web server "esp_lcd", # LCD controller drivers - only needed by display component "esp_local_ctrl", # Local control over HTTPS/BLE - ESPHome has native API + "esp_phy", # RF PHY - esp_wifi/bt/ieee802154 pull it back when they are in the build + "esp_wifi", # WiFi stack - re-included by request_wifi(), espnow; bt pulls it back for BLE builds "espcoredump", # Core dump support - ESPHome has its own debug component "fatfs", # FAT filesystem - ESPHome doesn't use filesystem storage + "ieee802154", # 802.15.4 radio - IDF openthread and the Zigbee libs pull it back "json", # cJSON library - ESPHome uses ArduinoJson instead "mqtt", # ESP-IDF MQTT library - ESPHome has its own MQTT implementation "nvs_sec_provider", # NVS encryption key provider - re-included when CONFIG_NVS_ENCRYPTION is set @@ -260,6 +266,7 @@ DEFAULT_EXCLUDED_IDF_COMPONENTS = ( "unity", # Unit testing framework - ESPHome doesn't use IDF's testing "wear_levelling", # Flash wear levelling for fatfs - unused since fatfs unused "wifi_provisioning", # WiFi provisioning - ESPHome uses its own improv implementation + "wpa_supplicant", # WPA supplicant - re-included by request_wifi() for esp_eap_client.h ) # Additional IDF managed components to exclude for Arduino framework builds @@ -709,6 +716,9 @@ def request_wifi(ap: bool = False) -> None: net.wifi = True if ap: net.wifi_ap = True + include_builtin_idf_component("esp_wifi") + # wifi_component.cpp includes esp_eap_client.h/esp_wpa2.h + include_builtin_idf_component("wpa_supplicant") def request_ethernet() -> None: @@ -720,11 +730,14 @@ def request_bluetooth() -> None: """Request the Bluetooth controller.""" net = _network_sdkconfig() net.bluetooth = True + include_builtin_idf_component("bt") def request_software_coexistence() -> None: """Request WiFi/BT software coexistence (only valid alongside WiFi).""" _network_sdkconfig().software_coexistence = True + # Callers include esp_coexist.h directly. + include_builtin_idf_component("esp_coex") def add_idf_component( @@ -2304,6 +2317,8 @@ async def _reconcile_network_sdkconfig() -> None: # WiFi stack: disable only when Ethernet is present and WiFi is not. WiFi # relies on the IDF default (enabled), so it is never written True here. + # esp_wifi is excluded by default on IDF, so this only matters for Arduino + # or when bt pulls it back. wifi_disabled = net.ethernet and not net.wifi if wifi_disabled: set_idf_sdkconfig_default("CONFIG_ESP_WIFI_ENABLED", False) diff --git a/esphome/components/espnow/__init__.py b/esphome/components/espnow/__init__.py index ee3732c406..5541a6ee97 100644 --- a/esphome/components/espnow/__init__.py +++ b/esphome/components/espnow/__init__.py @@ -155,6 +155,11 @@ async def to_code(config: ConfigType) -> None: cg.add_define("USE_ESPNOW") cg.add_define("USE_ESPNOW_MAX_PAYLOAD_SIZE", config[CONF_MAX_PAYLOAD_SIZE]) + if CORE.is_esp32: + from esphome.components.esp32 import include_builtin_idf_component + + include_builtin_idf_component("esp_wifi") + if CONF_WIFI in CORE.config: # Track the Wi-Fi channel via connect events instead of polling every loop wifi.request_wifi_connect_state_listener() diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index c9334ea97a..70bbf71410 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -1,5 +1,5 @@ import esphome.codegen as cg -from esphome.components.esp32 import add_idf_component +from esphome.components.esp32 import add_idf_component, add_idf_sdkconfig_option from esphome.config_helpers import filter_source_files_from_platform, get_logger_level import esphome.config_validation as cv from esphome.const import ( @@ -209,6 +209,13 @@ async def to_code(config: ConfigType) -> None: if CORE.is_esp32: add_idf_component(name="espressif/mdns", ref="1.11.3") + # The mdns console CLI is never used by ESPHome + add_idf_sdkconfig_option("CONFIG_MDNS_ENABLE_CONSOLE_CLI", False) + if "wifi" not in CORE.config: + # Without WiFi the predefined STA/AP interface handlers are dead + # code; disabling them lets mdns build without the WiFi stack. + add_idf_sdkconfig_option("CONFIG_MDNS_PREDEF_NETIF_STA", False) + add_idf_sdkconfig_option("CONFIG_MDNS_PREDEF_NETIF_AP", False) cg.add_define("USE_MDNS") diff --git a/esphome/components/zigbee/zigbee_esp32.py b/esphome/components/zigbee/zigbee_esp32.py index ade45e8cc3..57fa3b2a00 100644 --- a/esphome/components/zigbee/zigbee_esp32.py +++ b/esphome/components/zigbee/zigbee_esp32.py @@ -9,6 +9,7 @@ from esphome.components.esp32 import ( add_idf_component, add_idf_sdkconfig_option, add_partition, + include_builtin_idf_component, require_vfs_select, ) import esphome.config_validation as cv @@ -288,6 +289,10 @@ async def esp32_to_code(config: ConfigType) -> "MockObj": ref="2.0.4", ) + if CONF_WIFI in CORE.config: + # zigbee_esp32.cpp uses esp_coexist.h when WiFi is present + include_builtin_idf_component("esp_coex") + # add sdkconfigs later so they can overwrite esp32 defaults CORE.add_job(_zigbee_add_sdkconfigs, config) diff --git a/tests/component_tests/esp32/config/exclusion_reincludes_espnow.yaml b/tests/component_tests/esp32/config/exclusion_reincludes_espnow.yaml new file mode 100644 index 0000000000..2ede6c42df --- /dev/null +++ b/tests/component_tests/esp32/config/exclusion_reincludes_espnow.yaml @@ -0,0 +1,11 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +espnow: + channel: 1 + auto_add_peer: true diff --git a/tests/component_tests/esp32/config/exclusion_reincludes_wifi_ble.yaml b/tests/component_tests/esp32/config/exclusion_reincludes_wifi_ble.yaml new file mode 100644 index 0000000000..883abdb5fa --- /dev/null +++ b/tests/component_tests/esp32/config/exclusion_reincludes_wifi_ble.yaml @@ -0,0 +1,13 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: MySSID + password: password1 + +esp32_ble_tracker: diff --git a/tests/component_tests/esp32/config/network_ethernet_only.yaml b/tests/component_tests/esp32/config/network_ethernet_only.yaml index 73d11e0a13..4f357e40e6 100644 --- a/tests/component_tests/esp32/config/network_ethernet_only.yaml +++ b/tests/component_tests/esp32/config/network_ethernet_only.yaml @@ -6,6 +6,8 @@ esp32: framework: type: esp-idf +mdns: + ethernet: type: W5500 clk_pin: 19 diff --git a/tests/component_tests/esp32/config/network_wifi_only.yaml b/tests/component_tests/esp32/config/network_wifi_only.yaml index 61dfde3e03..3abc17e324 100644 --- a/tests/component_tests/esp32/config/network_wifi_only.yaml +++ b/tests/component_tests/esp32/config/network_wifi_only.yaml @@ -6,6 +6,8 @@ esp32: framework: type: esp-idf +mdns: + wifi: ssid: "test_ssid" password: "test_password" diff --git a/tests/component_tests/esp32/test_esp32.py b/tests/component_tests/esp32/test_esp32.py index 297844b4e6..c72c4c3a6b 100644 --- a/tests/component_tests/esp32/test_esp32.py +++ b/tests/component_tests/esp32/test_esp32.py @@ -24,6 +24,7 @@ from esphome.components.esp32 import ( ) from esphome.components.esp32.const import ( KEY_ESP32, + KEY_EXCLUDE_COMPONENTS, KEY_NETWORK_SDKCONFIG, KEY_SDKCONFIG_OPTIONS, KEY_VARIANT, @@ -298,6 +299,20 @@ def test_esp32_configuration_errors( ("esp-tls", "esp_http_client"), id="nextion", ), + pytest.param( + # esp_wifi/wpa_supplicant from request_wifi(), bt from + # request_bluetooth(), esp_coex from esp32_ble_tracker's software + # coexistence (defaults on with wifi). esp_phy stays excluded; + # IDF requirement expansion pulls it back via esp_wifi. + "exclusion_reincludes_wifi_ble.yaml", + ("esp_wifi", "wpa_supplicant", "bt", "esp_coex"), + id="wifi_ble", + ), + pytest.param( + "exclusion_reincludes_espnow.yaml", + ("esp_wifi",), + id="espnow", + ), ], ) def test_default_exclusions_reincluded_by_owning_components( @@ -309,8 +324,6 @@ def test_default_exclusions_reincluded_by_owning_components( """Components whose IDF driver is excluded by default must re-include it during codegen; a dropped include_builtin_idf_component() call would only surface as a missing-header failure in a full compile job.""" - from esphome.components.esp32.const import KEY_EXCLUDE_COMPONENTS - generate_main(component_config_path(config_file)) excluded = CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] @@ -329,8 +342,6 @@ def test_nvs_sec_provider_stays_excluded_when_encryption_is_off( component_config_path: Callable[[str], Path], ) -> None: """An explicit CONFIG_NVS_ENCRYPTION=n keeps nvs_sec_provider excluded.""" - from esphome.components.esp32.const import KEY_EXCLUDE_COMPONENTS - generate_main(component_config_path("exclusion_stays_nvs_sdkconfig_off.yaml")) assert "nvs_sec_provider" in CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] @@ -939,6 +950,14 @@ def test_network_wifi_only_reconciles_end_to_end( sdkconfig = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] assert sdkconfig.get("CONFIG_ESP_WIFI_SOFTAP_SUPPORT") is False assert sdkconfig.get("CONFIG_LWIP_DHCPS") is False + # request_wifi() also puts the WiFi components back in the build set; + # esp_phy stays excluded, IDF requirement expansion pulls it back. + excluded = CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] + assert "esp_wifi" not in excluded + assert "wpa_supplicant" not in excluded + assert "esp_phy" in excluded + # With wifi present mdns keeps its predefined interfaces. + assert "CONFIG_MDNS_PREDEF_NETIF_STA" not in sdkconfig # WiFi stack stays enabled (no ethernet) and no Bluetooth requested. assert "CONFIG_ESP_WIFI_ENABLED" not in sdkconfig assert "CONFIG_BT_ENABLED" not in sdkconfig @@ -954,6 +973,12 @@ def test_network_ethernet_only_reconciles_end_to_end( sdkconfig = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] assert sdkconfig.get("CONFIG_ESP_WIFI_ENABLED") is False assert sdkconfig.get("CONFIG_SW_COEXIST_ENABLE") is False + # The whole radio stack stays out of the build set as well. + excluded = CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] + assert {"esp_wifi", "wpa_supplicant", "esp_phy", "esp_coex", "bt"} <= excluded + # Without wifi, mdns drops its predefined STA/AP interfaces. + assert sdkconfig.get("CONFIG_MDNS_PREDEF_NETIF_STA") is False + assert sdkconfig.get("CONFIG_MDNS_PREDEF_NETIF_AP") is False def test_network_wifi_ble_coexistence_reconciles_end_to_end(