From 8dd1aec60672f26d1ec2a2d16a576752bde14e64 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:17:11 -0500 Subject: [PATCH 1/5] [esp32] Add warning for experimental 400MHz on ESP32-P4 (#13433) Co-authored-by: Claude Opus 4.5 --- esphome/components/esp32/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 07446ab046..e23a70a373 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -182,6 +182,12 @@ def set_core_data(config): path=[CONF_CPU_FREQUENCY], ) + if variant == VARIANT_ESP32P4 and cpu_frequency == "400MHZ": + _LOGGER.warning( + "400MHz on ESP32-P4 is experimental and may not boot. " + "Consider using 360MHz instead. See https://github.com/esphome/esphome/issues/13425" + ) + CORE.data[KEY_ESP32] = {} CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP32 conf = config[CONF_FRAMEWORK] From ebf589560d88844dfd0c2bb56376124d18fba59e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 21 Jan 2026 14:03:49 -1000 Subject: [PATCH 2/5] [wifi] Fix bk72xx manual_ip preventing API connection (#13426) --- esphome/components/wifi/wifi_component_libretiny.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 162ed4e835..cc9f4ec193 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -460,13 +460,15 @@ void WiFiComponent::wifi_process_event_(LTWiFiEvent *event) { listener->on_wifi_connect_state(StringRef(it.ssid, it.ssid_len), it.bssid); } #endif - // For static IP configurations, GOT_IP event may not fire, so notify IP listeners here -#if defined(USE_WIFI_IP_STATE_LISTENERS) && defined(USE_WIFI_MANUAL_IP) + // For static IP configurations, GOT_IP event may not fire, so set connected state here +#ifdef USE_WIFI_MANUAL_IP if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_manual_ip().has_value()) { s_sta_state = LTWiFiSTAState::CONNECTED; +#ifdef USE_WIFI_IP_STATE_LISTENERS for (auto *listener : this->ip_state_listeners_) { listener->on_ip_state(this->wifi_sta_ip_addresses(), this->get_dns_address(0), this->get_dns_address(1)); } +#endif } #endif break; From ce5ec7a78f3cc5be9871e8dd09c6b8c042d289d8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 21 Jan 2026 14:04:07 -1000 Subject: [PATCH 3/5] [spi] Fix display init failure by marking displays as write-only for half-duplex mode (#13431) --- esphome/components/epaper_spi/display.py | 2 +- esphome/components/ili9xxx/display.py | 2 +- esphome/components/max7219/display.py | 2 +- esphome/components/max7219digit/display.py | 2 +- esphome/components/mipi_rgb/display.py | 2 +- esphome/components/mipi_spi/display.py | 4 +--- esphome/components/pcd8544/display.py | 2 +- esphome/components/qspi_dbi/display.py | 2 +- esphome/components/spi/__init__.py | 7 ++++++- esphome/components/spi/spi_esp_idf.cpp | 5 ++++- esphome/components/ssd1306_spi/display.py | 2 +- esphome/components/ssd1322_spi/display.py | 2 +- esphome/components/ssd1325_spi/display.py | 2 +- esphome/components/ssd1327_spi/display.py | 2 +- esphome/components/ssd1331_spi/display.py | 2 +- esphome/components/ssd1351_spi/display.py | 2 +- esphome/components/st7567_spi/display.py | 2 +- esphome/components/st7701s/display.py | 2 +- esphome/components/st7735/display.py | 2 +- esphome/components/st7789v/display.py | 2 +- esphome/components/st7920/display.py | 2 +- esphome/components/waveshare_epaper/display.py | 2 +- 22 files changed, 30 insertions(+), 24 deletions(-) diff --git a/esphome/components/epaper_spi/display.py b/esphome/components/epaper_spi/display.py index a77e291237..8cc7b2663c 100644 --- a/esphome/components/epaper_spi/display.py +++ b/esphome/components/epaper_spi/display.py @@ -190,7 +190,7 @@ async def to_code(config): # Rotation is handled by setting the transform display_config = {k: v for k, v in config.items() if k != CONF_ROTATION} await display.register_display(var, display_config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 9588bccd55..bfb2300f4f 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -223,7 +223,7 @@ async def to_code(config): var = cg.Pvariable(config[CONF_ID], rhs) await display.register_display(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) if init_sequences := config.get(CONF_INIT_SEQUENCE): diff --git a/esphome/components/max7219/display.py b/esphome/components/max7219/display.py index c9d10f3c45..a434125148 100644 --- a/esphome/components/max7219/display.py +++ b/esphome/components/max7219/display.py @@ -29,7 +29,7 @@ CONFIG_SCHEMA = ( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) await display.register_display(var, config) cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) diff --git a/esphome/components/max7219digit/display.py b/esphome/components/max7219digit/display.py index fef121ff10..e6d53efc5d 100644 --- a/esphome/components/max7219digit/display.py +++ b/esphome/components/max7219digit/display.py @@ -86,7 +86,7 @@ CONFIG_SCHEMA = ( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) await display.register_display(var, config) cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) diff --git a/esphome/components/mipi_rgb/display.py b/esphome/components/mipi_rgb/display.py index 96e167b2e6..084fe6de14 100644 --- a/esphome/components/mipi_rgb/display.py +++ b/esphome/components/mipi_rgb/display.py @@ -260,7 +260,7 @@ async def to_code(config): cg.add(var.set_enable_pins(enable)) if CONF_SPI_ID in config: - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) sequence, madctl = model.get_sequence(config) cg.add(var.set_init_sequence(sequence)) cg.add(var.set_madctl(madctl)) diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index 69bf133c68..8dccfa3a92 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -443,6 +443,4 @@ async def to_code(config): ) cg.add(var.set_writer(lambda_)) await display.register_display(var, config) - await spi.register_spi_device(var, config) - # Displays are write-only, set the SPI device to write-only as well - cg.add(var.set_write_only(True)) + await spi.register_spi_device(var, config, write_only=True) diff --git a/esphome/components/pcd8544/display.py b/esphome/components/pcd8544/display.py index 2c24b133da..9d993c2105 100644 --- a/esphome/components/pcd8544/display.py +++ b/esphome/components/pcd8544/display.py @@ -44,7 +44,7 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await display.register_display(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/qspi_dbi/display.py b/esphome/components/qspi_dbi/display.py index e4440c9b81..48d1f6d12e 100644 --- a/esphome/components/qspi_dbi/display.py +++ b/esphome/components/qspi_dbi/display.py @@ -161,7 +161,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await display.register_display(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) chip = DriverChip.chips[config[CONF_MODEL]] if chip.initsequence: diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index e890567abf..931882be8d 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -39,6 +39,7 @@ from esphome.const import ( ) from esphome.core import CORE, CoroPriority, coroutine_with_priority import esphome.final_validate as fv +from esphome.types import ConfigType CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") @@ -448,9 +449,13 @@ def spi_device_schema( ) -async def register_spi_device(var, config): +async def register_spi_device( + var: cg.Pvariable, config: ConfigType, write_only: bool = False +) -> None: parent = await cg.get_variable(config[CONF_SPI_ID]) cg.add(var.set_spi_parent(parent)) + if write_only: + cg.add(var.set_write_only(True)) if cs_pin := config.get(CONF_CS_PIN): pin = await cg.gpio_pin_expression(cs_pin) cg.add(var.set_cs_pin(pin)) diff --git a/esphome/components/spi/spi_esp_idf.cpp b/esphome/components/spi/spi_esp_idf.cpp index a1837fa58d..107b6a3f1a 100644 --- a/esphome/components/spi/spi_esp_idf.cpp +++ b/esphome/components/spi/spi_esp_idf.cpp @@ -195,8 +195,11 @@ class SPIDelegateHw : public SPIDelegate { config.post_cb = nullptr; if (this->bit_order_ == BIT_ORDER_LSB_FIRST) config.flags |= SPI_DEVICE_BIT_LSBFIRST; - if (this->write_only_) + if (this->write_only_) { config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY; + ESP_LOGD(TAG, "SPI device with CS pin %d using half-duplex mode (write-only)", + Utility::get_pin_no(this->cs_pin_)); + } esp_err_t const err = spi_bus_add_device(this->channel_, &config, &this->handle_); if (err != ESP_OK) { ESP_LOGE(TAG, "Add device failed - err %X", err); diff --git a/esphome/components/ssd1306_spi/display.py b/esphome/components/ssd1306_spi/display.py index 4af41073d4..26953b4f39 100644 --- a/esphome/components/ssd1306_spi/display.py +++ b/esphome/components/ssd1306_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ssd1306_base.setup_ssd1306(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ssd1322_spi/display.py b/esphome/components/ssd1322_spi/display.py index 849e71abee..3d01caf874 100644 --- a/esphome/components/ssd1322_spi/display.py +++ b/esphome/components/ssd1322_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ssd1322_base.setup_ssd1322(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ssd1325_spi/display.py b/esphome/components/ssd1325_spi/display.py index e18db33c68..dbb9a14ac2 100644 --- a/esphome/components/ssd1325_spi/display.py +++ b/esphome/components/ssd1325_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ssd1325_base.setup_ssd1325(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ssd1327_spi/display.py b/esphome/components/ssd1327_spi/display.py index b622c098ec..f052764a91 100644 --- a/esphome/components/ssd1327_spi/display.py +++ b/esphome/components/ssd1327_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ssd1327_base.setup_ssd1327(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ssd1331_spi/display.py b/esphome/components/ssd1331_spi/display.py index 50895b3175..c16780302f 100644 --- a/esphome/components/ssd1331_spi/display.py +++ b/esphome/components/ssd1331_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ssd1331_base.setup_ssd1331(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ssd1351_spi/display.py b/esphome/components/ssd1351_spi/display.py index bd7033c3d4..2a6e984029 100644 --- a/esphome/components/ssd1351_spi/display.py +++ b/esphome/components/ssd1351_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ssd1351_base.setup_ssd1351(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/st7567_spi/display.py b/esphome/components/st7567_spi/display.py index 305aa35024..02cd2c105c 100644 --- a/esphome/components/st7567_spi/display.py +++ b/esphome/components/st7567_spi/display.py @@ -32,7 +32,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await st7567_base.setup_st7567(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/st7701s/display.py b/esphome/components/st7701s/display.py index 3078158d25..a8b12dfa28 100644 --- a/esphome/components/st7701s/display.py +++ b/esphome/components/st7701s/display.py @@ -173,7 +173,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await display.register_display(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) sequence = [] for seq in config[CONF_INIT_SEQUENCE]: diff --git a/esphome/components/st7735/display.py b/esphome/components/st7735/display.py index 2761214315..9dc69f27ff 100644 --- a/esphome/components/st7735/display.py +++ b/esphome/components/st7735/display.py @@ -99,7 +99,7 @@ async def to_code(config): config[CONF_INVERT_COLORS], ) await setup_st7735(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py index 8259eacf2d..c9f4199616 100644 --- a/esphome/components/st7789v/display.py +++ b/esphome/components/st7789v/display.py @@ -177,7 +177,7 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await display.register_display(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) cg.add(var.set_model_str(config[CONF_MODEL])) diff --git a/esphome/components/st7920/display.py b/esphome/components/st7920/display.py index de7b2247dd..ef33fac6c6 100644 --- a/esphome/components/st7920/display.py +++ b/esphome/components/st7920/display.py @@ -28,7 +28,7 @@ CONFIG_SCHEMA = ( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) if CONF_LAMBDA in config: lambda_ = await cg.process_lambda( diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index cea0b2be5e..5db7a1fc3d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -239,7 +239,7 @@ async def to_code(config): raise NotImplementedError() await display.register_display(var, config) - await spi.register_spi_device(var, config) + await spi.register_spi_device(var, config, write_only=True) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) cg.add(var.set_dc_pin(dc)) From 19c1d3aee70133d9e59564791c522bdac5b6c29b Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:41:59 -0500 Subject: [PATCH 4/5] [esp32] Bump Arduino to 3.3.6, platform to 55.03.36 (#13438) Co-authored-by: Claude Opus 4.5 --- .clang-tidy.hash | 2 +- esphome/components/esp32/__init__.py | 23 +++++++++++++++-------- esphome/core/defines.h | 2 +- platformio.ini | 6 +++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.clang-tidy.hash b/.clang-tidy.hash index 0204b50264..0a272d21ba 100644 --- a/.clang-tidy.hash +++ b/.clang-tidy.hash @@ -1 +1 @@ -c0335c9688ce9defb4a7d4446b93460547e22df055668bacd7d963c770f0c65f +15dc295268b2dcf75942f42759b3ddec64eba89f75525698eb39c95a7f4b14ce diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index e23a70a373..da49fdc070 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -348,7 +348,12 @@ def add_extra_build_file(filename: str, path: Path) -> bool: def _format_framework_arduino_version(ver: cv.Version) -> str: # format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to # a PIO pioarduino/framework-arduinoespressif32 value - return f"pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/{str(ver)}/esp32-{str(ver)}.zip" + # 3.3.6+ changed filename from esp32-{ver}.zip to esp32-core-{ver}.tar.xz + if ver >= cv.Version(3, 3, 6): + filename = f"esp32-core-{ver}.tar.xz" + else: + filename = f"esp32-{ver}.zip" + return f"pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/{ver}/{filename}" def _format_framework_espidf_version(ver: cv.Version, release: str) -> str: @@ -383,11 +388,12 @@ def _is_framework_url(source: str) -> bool: # The default/recommended arduino framework version # - https://github.com/espressif/arduino-esp32/releases ARDUINO_FRAMEWORK_VERSION_LOOKUP = { - "recommended": cv.Version(3, 3, 5), - "latest": cv.Version(3, 3, 5), - "dev": cv.Version(3, 3, 5), + "recommended": cv.Version(3, 3, 6), + "latest": cv.Version(3, 3, 6), + "dev": cv.Version(3, 3, 6), } ARDUINO_PLATFORM_VERSION_LOOKUP = { + cv.Version(3, 3, 6): cv.Version(55, 3, 36), cv.Version(3, 3, 5): cv.Version(55, 3, 35), cv.Version(3, 3, 4): cv.Version(55, 3, 31, "2"), cv.Version(3, 3, 3): cv.Version(55, 3, 31, "2"), @@ -405,6 +411,7 @@ ARDUINO_PLATFORM_VERSION_LOOKUP = { # These versions correspond to pioarduino/esp-idf releases # See: https://github.com/pioarduino/esp-idf/releases ARDUINO_IDF_VERSION_LOOKUP = { + cv.Version(3, 3, 6): cv.Version(5, 5, 2), cv.Version(3, 3, 5): cv.Version(5, 5, 2), cv.Version(3, 3, 4): cv.Version(5, 5, 1), cv.Version(3, 3, 3): cv.Version(5, 5, 1), @@ -427,7 +434,7 @@ ESP_IDF_FRAMEWORK_VERSION_LOOKUP = { "dev": cv.Version(5, 5, 2), } ESP_IDF_PLATFORM_VERSION_LOOKUP = { - cv.Version(5, 5, 2): cv.Version(55, 3, 35), + cv.Version(5, 5, 2): cv.Version(55, 3, 36), cv.Version(5, 5, 1): cv.Version(55, 3, 31, "2"), cv.Version(5, 5, 0): cv.Version(55, 3, 31, "2"), cv.Version(5, 4, 3): cv.Version(55, 3, 32), @@ -444,9 +451,9 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = { # The platform-espressif32 version # - https://github.com/pioarduino/platform-espressif32/releases PLATFORM_VERSION_LOOKUP = { - "recommended": cv.Version(55, 3, 35), - "latest": cv.Version(55, 3, 35), - "dev": cv.Version(55, 3, 35), + "recommended": cv.Version(55, 3, 36), + "latest": cv.Version(55, 3, 36), + "dev": cv.Version(55, 3, 36), } diff --git a/esphome/core/defines.h b/esphome/core/defines.h index c229d1df7d..7c13823fba 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -234,7 +234,7 @@ #define USB_HOST_MAX_REQUESTS 16 #ifdef USE_ARDUINO -#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 5) +#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 6) #define USE_ETHERNET #define USE_ETHERNET_KSZ8081 #define USE_ETHERNET_MANUAL_IP diff --git a/platformio.ini b/platformio.ini index b109284933..e9a588e4fd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -133,9 +133,9 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script ; This are common settings for the ESP32 (all variants) using Arduino. [common:esp32-arduino] extends = common:arduino -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.35/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.36/platform-espressif32.zip platform_packages = - pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.3.5/esp32-3.3.5.zip + pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.3.6/esp32-core-3.3.6.tar.xz framework = arduino, espidf ; Arduino as an ESP-IDF component lib_deps = @@ -168,7 +168,7 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 (all variants) using IDF. [common:esp32-idf] extends = common:idf -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.35/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.36/platform-espressif32.zip platform_packages = pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.5.2/esp-idf-v5.5.2.tar.xz From 645832a0708573c8763ee50b7fc5564fabb619ea Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 22 Jan 2026 03:10:12 +0100 Subject: [PATCH 5/5] [nextion] Add configurable startup and queue timeout constants (#11098) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: Keith Burzinski --- esphome/components/nextion/base_component.py | 2 ++ esphome/components/nextion/display.py | 18 ++++++++++ esphome/components/nextion/nextion.cpp | 36 ++++++++++++-------- esphome/components/nextion/nextion.h | 29 ++++++++++++++-- tests/components/nextion/common.yaml | 2 ++ 5 files changed, 70 insertions(+), 17 deletions(-) diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index 392481e39a..86551cbe23 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -16,6 +16,7 @@ CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" CONF_FONT_ID = "font_id" CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" CONF_MAX_COMMANDS_PER_LOOP = "max_commands_per_loop" +CONF_MAX_QUEUE_AGE = "max_queue_age" CONF_MAX_QUEUE_SIZE = "max_queue_size" CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow" CONF_ON_PAGE = "on_page" @@ -25,6 +26,7 @@ CONF_ON_WAKE = "on_wake" CONF_PRECISION = "precision" CONF_SKIP_CONNECTION_HANDSHAKE = "skip_connection_handshake" CONF_START_UP_PAGE = "start_up_page" +CONF_STARTUP_OVERRIDE_MS = "startup_override_ms" CONF_TFT_URL = "tft_url" CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout" CONF_VARIABLE_NAME = "variable_name" diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 0b4ba3a171..ffc509fc64 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -23,6 +23,7 @@ from .base_component import ( CONF_DUMP_DEVICE_INFO, CONF_EXIT_REPARSE_ON_START, CONF_MAX_COMMANDS_PER_LOOP, + CONF_MAX_QUEUE_AGE, CONF_MAX_QUEUE_SIZE, CONF_ON_BUFFER_OVERFLOW, CONF_ON_PAGE, @@ -31,6 +32,7 @@ from .base_component import ( CONF_ON_WAKE, CONF_SKIP_CONNECTION_HANDSHAKE, CONF_START_UP_PAGE, + CONF_STARTUP_OVERRIDE_MS, CONF_TFT_URL, CONF_TOUCH_SLEEP_TIMEOUT, CONF_WAKE_UP_PAGE, @@ -65,6 +67,12 @@ CONFIG_SCHEMA = ( ), cv.Optional(CONF_DUMP_DEVICE_INFO, default=False): cv.boolean, cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, + cv.Optional(CONF_MAX_QUEUE_AGE, default="8000ms"): cv.All( + cv.positive_time_period_milliseconds, + cv.Range( + min=TimePeriod(milliseconds=0), max=TimePeriod(milliseconds=65535) + ), + ), cv.Optional(CONF_MAX_COMMANDS_PER_LOOP): cv.uint16_t, cv.Optional(CONF_MAX_QUEUE_SIZE): cv.positive_int, cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( @@ -100,6 +108,12 @@ CONFIG_SCHEMA = ( } ), cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, + cv.Optional(CONF_STARTUP_OVERRIDE_MS, default="8000ms"): cv.All( + cv.positive_time_period_milliseconds, + cv.Range( + min=TimePeriod(milliseconds=0), max=TimePeriod(milliseconds=65535) + ), + ), cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, cv.Optional(CONF_TFT_URL): cv.url, cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.Any( @@ -138,6 +152,8 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await uart.register_uart_device(var, config) + cg.add(var.set_max_queue_age(config[CONF_MAX_QUEUE_AGE])) + if max_queue_size := config.get(CONF_MAX_QUEUE_SIZE): cg.add_define("USE_NEXTION_MAX_QUEUE_SIZE") cg.add(var.set_max_queue_size(max_queue_size)) @@ -146,6 +162,8 @@ async def to_code(config): cg.add_define("USE_NEXTION_COMMAND_SPACING") cg.add(var.set_command_spacing(command_spacing.total_milliseconds)) + cg.add(var.set_startup_override_ms(config[CONF_STARTUP_OVERRIDE_MS])) + if CONF_BRIGHTNESS in config: cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 354288e1a3..4bba33f961 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -152,21 +152,25 @@ void Nextion::dump_config() { #else // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE ESP_LOGCONFIG(TAG, #ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO - " Device Model: %s\n" - " FW Version: %s\n" - " Serial Number: %s\n" - " Flash Size: %s\n" + " Device Model: %s\n" + " FW Version: %s\n" + " Serial Number: %s\n" + " Flash Size: %s\n" + " Max queue age: %u ms\n" + " Startup override: %u ms\n", #endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO #ifdef USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START - " Exit reparse: YES\n" + " Exit reparse: YES\n" #endif // USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START - " Wake On Touch: %s\n" - " Touch Timeout: %" PRIu16, + " Wake On Touch: %s\n" + " Touch Timeout: %" PRIu16, #ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO this->device_model_.c_str(), this->firmware_version_.c_str(), this->serial_number_.c_str(), - this->flash_size_.c_str(), + this->flash_size_.c_str(), this->max_q_age_ms_, + this->startup_override_ms_ #endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO - YESNO(this->connection_state_.auto_wake_on_touch_), this->touch_sleep_timeout_); + YESNO(this->connection_state_.auto_wake_on_touch_), + this->touch_sleep_timeout_); #endif // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP @@ -174,21 +178,21 @@ void Nextion::dump_config() { #endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP if (this->wake_up_page_ != 255) { - ESP_LOGCONFIG(TAG, " Wake Up Page: %u", this->wake_up_page_); + ESP_LOGCONFIG(TAG, " Wake Up Page: %u", this->wake_up_page_); } #ifdef USE_NEXTION_CONF_START_UP_PAGE if (this->start_up_page_ != 255) { - ESP_LOGCONFIG(TAG, " Start Up Page: %u", this->start_up_page_); + ESP_LOGCONFIG(TAG, " Start Up Page: %u", this->start_up_page_); } #endif // USE_NEXTION_CONF_START_UP_PAGE #ifdef USE_NEXTION_COMMAND_SPACING - ESP_LOGCONFIG(TAG, " Cmd spacing: %u ms", this->command_pacer_.get_spacing()); + ESP_LOGCONFIG(TAG, " Cmd spacing: %u ms", this->command_pacer_.get_spacing()); #endif // USE_NEXTION_COMMAND_SPACING #ifdef USE_NEXTION_MAX_QUEUE_SIZE - ESP_LOGCONFIG(TAG, " Max queue size: %zu", this->max_queue_size_); + ESP_LOGCONFIG(TAG, " Max queue size: %zu", this->max_queue_size_); #endif } @@ -336,7 +340,8 @@ void Nextion::loop() { if (this->started_ms_ == 0) this->started_ms_ = App.get_loop_component_start_time(); - if (this->started_ms_ + this->startup_override_ms_ < App.get_loop_component_start_time()) { + if (this->startup_override_ms_ > 0 && + this->started_ms_ + this->startup_override_ms_ < App.get_loop_component_start_time()) { ESP_LOGV(TAG, "Manual ready set"); this->connection_state_.nextion_reports_is_setup_ = true; } @@ -845,7 +850,8 @@ void Nextion::process_nextion_commands_() { const uint32_t ms = App.get_loop_component_start_time(); - if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { + if (this->max_q_age_ms_ > 0 && !this->nextion_queue_.empty() && + this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { for (size_t i = 0; i < this->nextion_queue_.size(); i++) { NextionComponentBase *component = this->nextion_queue_[i]->component; if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) { diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 331e901578..c543e14bfe 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1309,6 +1309,30 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ bool is_connected() { return this->connection_state_.is_connected_; } + /** + * @brief Set the maximum age for queue items + * @param age_ms Maximum age in milliseconds before queue items are removed + */ + inline void set_max_queue_age(uint16_t age_ms) { this->max_q_age_ms_ = age_ms; } + + /** + * @brief Get the maximum age for queue items + * @return Maximum age in milliseconds + */ + inline uint16_t get_max_queue_age() const { return this->max_q_age_ms_; } + + /** + * @brief Set the startup override timeout + * @param timeout_ms Time in milliseconds to wait before forcing setup complete + */ + inline void set_startup_override_ms(uint16_t timeout_ms) { this->startup_override_ms_ = timeout_ms; } + + /** + * @brief Get the startup override timeout + * @return Startup override timeout in milliseconds + */ + inline uint16_t get_startup_override_ms() const { return this->startup_override_ms_; } + protected: #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP uint16_t max_commands_per_loop_{1000}; @@ -1479,9 +1503,10 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void reset_(bool reset_nextion = true); std::string command_data_; - const uint16_t startup_override_ms_ = 8000; - const uint16_t max_q_age_ms_ = 8000; uint32_t started_ms_ = 0; + + uint16_t startup_override_ms_ = 8000; ///< Timeout before forcing setup complete + uint16_t max_q_age_ms_ = 8000; ///< Maximum age for queue items in ms }; } // namespace nextion diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index f5ee12a51c..48a7292f43 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -277,6 +277,8 @@ display: command_spacing: 5ms max_commands_per_loop: 20 max_queue_size: 50 + startup_override_ms: 10000ms # Wait 10s for display ready + max_queue_age: 5000ms # Remove queue items after 5s on_sleep: then: lambda: 'ESP_LOGD("display","Display went to sleep");'