1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-12 12:55:46 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Jonathan Swoboda
1d71b6b93e Merge pull request #11862 from esphome/bump-2025.11.0b1
2025.11.0b1
2025-11-11 23:27:12 -05:00
Jonathan Swoboda
298813d4fa Bump version to 2025.11.0b1 2025-11-11 22:14:22 -05:00
Jonathan Swoboda
56d141c741 Merge branch 'release' into dev 2025-11-11 20:09:55 -05:00
Jonathan Swoboda
47a7f729dd Merge pull request #11857 from esphome/bump-2025.10.5
2025.10.5
2025-11-11 20:09:41 -05:00
Jonathan Swoboda
7806eb980f Bump version to 2025.12.0-dev 2025-11-11 19:50:47 -05:00
Jonathan Swoboda
a59888224c Bump version to 2025.10.5 2025-11-11 19:44:37 -05:00
Clyde Stubbs
58ad4759f0 [lvgl] Fix rotation with unusual width (#11680) 2025-11-11 19:44:37 -05:00
Clyde Stubbs
87f79290ba [usb_uart] Fixes for transfer queue allocation (#11548) 2025-11-11 19:44:37 -05:00
Jonathan Swoboda
9326d78439 [core] Don't allow python 3.14 (#11527) 2025-11-11 19:44:37 -05:00
Stuart Parmenter
a93887a790 [const] Add CONF_ROWS (#11249) 2025-11-11 19:44:37 -05:00
Kevin Ahrendt
d7fa131a8a [network, psram, speaker wifi] Use CORE.data to enable high performance networking (#11812) 2025-11-11 18:43:06 -06:00
7 changed files with 237 additions and 24 deletions

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2025.11.0-dev
PROJECT_NUMBER = 2025.11.0b1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -1,7 +1,9 @@
import ipaddress
import logging
import esphome.codegen as cg
from esphome.components.esp32 import add_idf_sdkconfig_option
from esphome.components.psram import is_guaranteed as psram_is_guaranteed
import esphome.config_validation as cv
from esphome.const import CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT
from esphome.core import CORE, CoroPriority, coroutine_with_priority
@@ -9,6 +11,13 @@ from esphome.core import CORE, CoroPriority, coroutine_with_priority
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["mdns"]
_LOGGER = logging.getLogger(__name__)
# High performance networking tracking infrastructure
# Components can request high performance networking and this configures lwip and WiFi settings
KEY_HIGH_PERFORMANCE_NETWORKING = "high_performance_networking"
CONF_ENABLE_HIGH_PERFORMANCE = "enable_high_performance"
network_ns = cg.esphome_ns.namespace("network")
IPAddress = network_ns.class_("IPAddress")
@@ -47,6 +56,55 @@ def ip_address_literal(ip: str | int | None) -> cg.MockObj:
return IPAddress(str(ip))
def require_high_performance_networking() -> None:
"""Request high performance networking for network and WiFi.
Call this from components that need optimized network performance for streaming
or high-throughput data transfer. This enables high performance mode which
configures both lwip TCP settings and WiFi driver settings for improved
network performance.
Settings applied (ESP-IDF only):
- lwip: Larger TCP buffers, windows, and mailbox sizes
- WiFi: Increased RX/TX buffers, AMPDU aggregation, PSRAM allocation (set by wifi component)
Configuration is PSRAM-aware:
- With PSRAM guaranteed: Aggressive settings (512 RX buffers, 512KB TCP windows)
- Without PSRAM: Conservative optimized settings (64 buffers, 65KB TCP windows)
Example:
from esphome.components import network
def _request_high_performance_networking(config):
network.require_high_performance_networking()
return config
CONFIG_SCHEMA = cv.All(
...,
_request_high_performance_networking,
)
"""
# Only set up once (idempotent - multiple components can call this)
if not CORE.data.get(KEY_HIGH_PERFORMANCE_NETWORKING, False):
CORE.data[KEY_HIGH_PERFORMANCE_NETWORKING] = True
def has_high_performance_networking() -> bool:
"""Check if high performance networking mode is enabled.
Returns True when high performance networking has been requested by a
component or explicitly enabled in the network configuration. This indicates
that lwip and WiFi will use optimized buffer sizes and settings.
This function should be called during code generation (to_code phase) by
components that need to apply performance-related settings.
Returns:
bool: True if high performance networking is enabled, False otherwise
"""
return CORE.data.get(KEY_HIGH_PERFORMANCE_NETWORKING, False)
CONFIG_SCHEMA = cv.Schema(
{
cv.SplitDefault(
@@ -71,6 +129,7 @@ CONFIG_SCHEMA = cv.Schema(
),
),
cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int,
cv.Optional(CONF_ENABLE_HIGH_PERFORMANCE): cv.All(cv.boolean, cv.only_on_esp32),
}
)
@@ -80,6 +139,70 @@ async def to_code(config):
cg.add_define("USE_NETWORK")
if CORE.using_arduino and CORE.is_esp32:
cg.add_library("Networking", None)
# Apply high performance networking settings
# Config can explicitly enable/disable, or default to component-driven behavior
enable_high_perf = config.get(CONF_ENABLE_HIGH_PERFORMANCE)
component_requested = CORE.data.get(KEY_HIGH_PERFORMANCE_NETWORKING, False)
# Explicit config overrides component request
should_enable = (
enable_high_perf if enable_high_perf is not None else component_requested
)
# Log when user explicitly disables but a component requested it
if enable_high_perf is False and component_requested:
_LOGGER.info(
"High performance networking disabled by user configuration (overriding component request)"
)
if CORE.is_esp32 and CORE.using_esp_idf and should_enable:
# Check if PSRAM is guaranteed (set by psram component during final validation)
psram_guaranteed = psram_is_guaranteed()
if psram_guaranteed:
_LOGGER.info(
"Applying high-performance lwip settings (PSRAM guaranteed): 512KB TCP windows, 512 mailbox sizes"
)
# PSRAM is guaranteed - use aggressive settings
# Higher maximum values are allowed because CONFIG_LWIP_WND_SCALE is set to true
# CONFIG_LWIP_WND_SCALE can only be enabled if CONFIG_SPIRAM_IGNORE_NOTFOUND isn't set
# Based on https://github.com/espressif/esp-adf/issues/297#issuecomment-783811702
# Enable window scaling for much larger TCP windows
add_idf_sdkconfig_option("CONFIG_LWIP_WND_SCALE", True)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RCV_SCALE", 3)
# Large TCP buffers and windows (requires PSRAM)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SND_BUF_DEFAULT", 65534)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_WND_DEFAULT", 512000)
# Large mailboxes for high throughput
add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 512)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 512)
# TCP connection limits
add_idf_sdkconfig_option("CONFIG_LWIP_MAX_ACTIVE_TCP", 16)
add_idf_sdkconfig_option("CONFIG_LWIP_MAX_LISTENING_TCP", 16)
# TCP optimizations
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_MAXRTX", 12)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SYNMAXRTX", 6)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_MSS", 1436)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_MSL", 60000)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_OVERSIZE_MSS", True)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_QUEUE_OOSEQ", True)
else:
_LOGGER.info(
"Applying optimized lwip settings: 65KB TCP windows, 64 mailbox sizes"
)
# PSRAM not guaranteed - use more conservative, but still optimized settings
# Based on https://github.com/espressif/esp-idf/blob/release/v5.4/examples/wifi/iperf/sdkconfig.defaults.esp32
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SND_BUF_DEFAULT", 65534)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_WND_DEFAULT", 65534)
add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 64)
add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 64)
if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None:
cg.add_define("USE_NETWORK_IPV6", enable_ipv6)
if enable_ipv6:

View File

@@ -35,6 +35,9 @@ DOMAIN = "psram"
DEPENDENCIES = [PLATFORM_ESP32]
# PSRAM availability tracking for cross-component coordination
KEY_PSRAM_GUARANTEED = "psram_guaranteed"
_LOGGER = logging.getLogger(__name__)
psram_ns = cg.esphome_ns.namespace(DOMAIN)
@@ -71,6 +74,23 @@ def supported() -> bool:
return variant in SPIRAM_MODES
def is_guaranteed() -> bool:
"""Check if PSRAM is guaranteed to be available.
Returns True when PSRAM is configured with both 'disabled: false' and
'ignore_not_found: false', meaning the device will fail to boot if PSRAM
is not found. This ensures safe use of high buffer configurations that
depend on PSRAM.
This function should be called during code generation (to_code phase) by
components that need to know PSRAM availability for configuration decisions.
Returns:
bool: True if PSRAM is guaranteed, False otherwise
"""
return CORE.data.get(KEY_PSRAM_GUARANTEED, False)
def validate_psram_mode(config):
esp32_config = fv.full_config.get()[PLATFORM_ESP32]
if config[CONF_SPEED] == "120MHZ":
@@ -131,7 +151,22 @@ def get_config_schema(config):
CONFIG_SCHEMA = get_config_schema
FINAL_VALIDATE_SCHEMA = validate_psram_mode
def _store_psram_guaranteed(config):
"""Store PSRAM guaranteed status in CORE.data for other components.
PSRAM is "guaranteed" when it will fail if not found, ensuring safe use
of high buffer configurations in network/wifi components.
Called during final validation to ensure the flag is available
before any to_code() functions run.
"""
psram_guaranteed = not config[CONF_DISABLED] and not config[CONF_IGNORE_NOT_FOUND]
CORE.data[KEY_PSRAM_GUARANTEED] = psram_guaranteed
return config
FINAL_VALIDATE_SCHEMA = cv.All(validate_psram_mode, _store_psram_guaranteed)
async def to_code(config):

View File

@@ -6,7 +6,7 @@ from pathlib import Path
from esphome import automation, external_files
import esphome.codegen as cg
from esphome.components import audio, esp32, media_player, psram, speaker
from esphome.components import audio, esp32, media_player, network, psram, speaker
import esphome.config_validation as cv
from esphome.const import (
CONF_BUFFER_SIZE,
@@ -32,6 +32,7 @@ _LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["audio"]
DEPENDENCIES = ["network"]
CODEOWNERS = ["@kahrendt", "@synesthesiam"]
DOMAIN = "media_player"
@@ -280,6 +281,18 @@ PIPELINE_SCHEMA = cv.Schema(
}
)
def _request_high_performance_networking(config):
"""Request high performance networking for streaming media.
Speaker media player streams audio data, so it always benefits from
optimized WiFi and lwip settings regardless of codec support.
Called during config validation to ensure flags are set before to_code().
"""
network.require_high_performance_networking()
return config
CONFIG_SCHEMA = cv.All(
media_player.media_player_schema(SpeakerMediaPlayer).extend(
{
@@ -304,6 +317,7 @@ CONFIG_SCHEMA = cv.All(
),
cv.only_with_esp_idf,
_validate_repeated_speaker,
_request_high_performance_networking,
)
@@ -321,28 +335,10 @@ FINAL_VALIDATE_SCHEMA = cv.All(
async def to_code(config):
if CORE.data[DOMAIN][config[CONF_ID].id][CONF_CODEC_SUPPORT_ENABLED]:
# Compile all supported audio codecs and optimize the wifi settings
# Compile all supported audio codecs
cg.add_define("USE_AUDIO_FLAC_SUPPORT", True)
cg.add_define("USE_AUDIO_MP3_SUPPORT", True)
# Based on https://github.com/espressif/esp-idf/blob/release/v5.4/examples/wifi/iperf/sdkconfig.defaults.esp32
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM", 16)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM", 64)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM", 64)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_TX_ENABLED", True)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BA_WIN", 32)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_RX_ENABLED", True)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_RX_BA_WIN", 32)
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SND_BUF_DEFAULT", 65534)
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCP_WND_DEFAULT", 65534)
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 64)
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 64)
# Allocate wifi buffers in PSRAM
esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
var = await media_player.new_media_player(config)
await cg.register_component(var, config)

View File

@@ -5,7 +5,11 @@ from esphome.automation import Condition
import esphome.codegen as cg
from esphome.components.const import CONF_USE_PSRAM
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
from esphome.components.network import ip_address_literal
from esphome.components.network import (
has_high_performance_networking,
ip_address_literal,
)
from esphome.components.psram import is_guaranteed as psram_is_guaranteed
from esphome.config_helpers import filter_source_files_from_platform
import esphome.config_validation as cv
from esphome.config_validation import only_with_esp_idf
@@ -56,6 +60,8 @@ _LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["network"]
_LOGGER = logging.getLogger(__name__)
NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2, const.VARIANT_ESP32P4]
CONF_SAVE = "save"
CONF_MIN_AUTH_MODE = "min_auth_mode"
@@ -496,6 +502,56 @@ async def to_code(config):
if config.get(CONF_USE_PSRAM):
add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
# Apply high performance WiFi settings if high performance networking is enabled
if CORE.is_esp32 and CORE.using_esp_idf and has_high_performance_networking():
# Check if PSRAM is guaranteed (set by psram component during final validation)
psram_guaranteed = psram_is_guaranteed()
# Always allocate WiFi buffers in PSRAM if available
add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
if psram_guaranteed:
_LOGGER.info(
"Applying high-performance WiFi settings (PSRAM guaranteed): 512 RX buffers, 32 TX buffers"
)
# PSRAM is guaranteed - use aggressive settings
# Higher maximum values are allowed because CONFIG_LWIP_WND_SCALE is set to true in networking component
# Based on https://github.com/espressif/esp-adf/issues/297#issuecomment-783811702
# Large dynamic RX buffers (requires PSRAM)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM", 16)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM", 512)
# Static TX buffers for better performance
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_TX_BUFFER", True)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BUFFER_TYPE", 0)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM", 32)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM", 8)
# AMPDU settings optimized for PSRAM
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_TX_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BA_WIN", 16)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_RX_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_RX_BA_WIN", 32)
else:
_LOGGER.info(
"Applying optimized WiFi settings: 64 RX buffers, 64 TX buffers"
)
# PSRAM not guaranteed - use more conservative, but still optimized settings
# Based on https://github.com/espressif/esp-idf/blob/release/v5.4/examples/wifi/iperf/sdkconfig.defaults.esp32
# Standard buffer counts
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM", 16)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM", 64)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM", 64)
# Standard AMPDU settings
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_TX_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BA_WIN", 32)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_RX_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_RX_BA_WIN", 32)
cg.add_define("USE_WIFI")
# must register before OTA safe mode check

View File

@@ -4,7 +4,7 @@ from enum import Enum
from esphome.enum import StrEnum
__version__ = "2025.11.0-dev"
__version__ = "2025.11.0b1"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = (

View File

@@ -1 +1,4 @@
<<: !include common.yaml
network:
enable_high_performance: true