[esp32] Only warn about S3 PSRAM pins (GPIO33-37) in octal mode (#17222)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
Franck Nijhof
2026-06-27 17:26:47 +02:00
committed by GitHub
parent 436938b931
commit 24ec65e68e
7 changed files with 129 additions and 8 deletions
+4
View File
@@ -1102,6 +1102,8 @@ def final_validate(config):
# Imported locally to avoid circular import issues
from esphome.components.psram import DOMAIN as PSRAM_DOMAIN
from .gpio import final_validate_pins
errs = []
conf_fw = config[CONF_FRAMEWORK]
advanced = conf_fw[CONF_ADVANCED]
@@ -1185,6 +1187,8 @@ def final_validate(config):
)
)
final_validate_pins(full_config)
if (
config[CONF_FLASH_SIZE] == "32MB"
and "ota" in full_config
+15 -1
View File
@@ -18,6 +18,7 @@ from esphome.const import (
PLATFORM_ESP32,
)
from esphome.core import CORE
from esphome.types import ConfigType
from . import boards
from .const import (
@@ -50,7 +51,11 @@ from .gpio_esp32_h4 import esp32_h4_validate_gpio_pin, esp32_h4_validate_support
from .gpio_esp32_h21 import esp32_h21_validate_gpio_pin, esp32_h21_validate_supports
from .gpio_esp32_p4 import esp32_p4_validate_gpio_pin, esp32_p4_validate_supports
from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports
from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports
from .gpio_esp32_s3 import (
esp32_s3_final_validate_pins,
esp32_s3_validate_gpio_pin,
esp32_s3_validate_supports,
)
from .gpio_esp32_s31 import esp32_s31_validate_gpio_pin, esp32_s31_validate_supports
ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin)
@@ -96,6 +101,7 @@ def _translate_pin(value):
class ESP32ValidationFunctions:
pin_validation: Callable[[int], int]
usage_validation: Callable[[dict[str, Any]], dict[str, Any]]
final_validate: Callable[[ConfigType], None] | None = None
_esp32_validations = {
@@ -145,6 +151,7 @@ _esp32_validations = {
VARIANT_ESP32S3: ESP32ValidationFunctions(
pin_validation=esp32_s3_validate_gpio_pin,
usage_validation=esp32_s3_validate_supports,
final_validate=esp32_s3_final_validate_pins,
),
VARIANT_ESP32S31: ESP32ValidationFunctions(
pin_validation=esp32_s31_validate_gpio_pin,
@@ -261,3 +268,10 @@ async def esp32_pin_to_code(config):
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var
def final_validate_pins(full_config: ConfigType) -> None:
"""Run the active variant's pin final-validation, if it defines one."""
funcs = _esp32_validations.get(CORE.data[KEY_ESP32][KEY_VARIANT])
if funcs is not None and funcs.final_validate is not None:
funcs.final_validate(full_config)
+38 -7
View File
@@ -2,8 +2,15 @@ import logging
from typing import Any
import esphome.config_validation as cv
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
from esphome.pins import check_strapping_pin
from esphome.const import (
CONF_DISABLED,
CONF_INPUT,
CONF_MODE,
CONF_NUMBER,
PLATFORM_ESP32,
)
from esphome.pins import PIN_SCHEMA_REGISTRY, check_strapping_pin
from esphome.types import ConfigType
_ESP32S3_SPI_PSRAM_PINS = {
26: "SPICS1",
@@ -38,11 +45,9 @@ def esp32_s3_validate_gpio_pin(value: int) -> int:
raise cv.Invalid(
f"This pin cannot be used on ESP32-S3s and is already used by the SPI/PSRAM interface(function: {_ESP32S3_SPI_PSRAM_PINS[value]})"
)
if value in _ESP32S3R8_PSRAM_PINS:
_LOGGER.warning(
"GPIO%d is used by the PSRAM interface on ESP32-S3R8 / ESP32-S3R8V and should be avoided on these models",
value,
)
# GPIO33-37 (_ESP32S3R8_PSRAM_PINS) are only taken by the PSRAM interface in
# octal mode -- whether that applies isn't known here, so the warning is
# deferred to final_validate_pins() in gpio.py once the PSRAM mode is resolved.
if value in (22, 23, 24, 25):
# These pins are not exposed in GPIO mux (reason unknown)
@@ -71,3 +76,29 @@ def esp32_s3_validate_supports(value: dict[str, Any]) -> dict[str, Any]:
check_strapping_pin(value, _ESP32S3_STRAPPING_PINS, _LOGGER)
return value
def esp32_s3_final_validate_pins(full_config: ConfigType) -> None:
"""Warn about GPIO33-37 usage, but only when octal PSRAM (which uses them) is set.
These pins are only taken by the PSRAM interface in octal mode (ESP32-S3R8 /
S3R8V); on quad-PSRAM variants -- or when the psram block is disabled, so the
octal interface is never configured -- they are free. The per-pin validator
can't know the PSRAM mode, so the check is deferred here, where
PIN_SCHEMA_REGISTRY.pins_used already lists every used pin.
"""
# Imported locally to avoid circular import issues
from esphome.components.psram import DOMAIN as PSRAM_DOMAIN, TYPE_OCTAL
psram_config = full_config.get(PSRAM_DOMAIN, {})
if psram_config.get(CONF_DISABLED) or psram_config.get(CONF_MODE) != TYPE_OCTAL:
return
for number in sorted(
number
for key, _client_id, number in PIN_SCHEMA_REGISTRY.pins_used
if key == PLATFORM_ESP32 and number in _ESP32S3R8_PSRAM_PINS
):
_LOGGER.warning(
"GPIO%d is used by the PSRAM interface in octal mode and should be avoided",
number,
)
@@ -0,0 +1,16 @@
esphome:
name: test
esp32:
variant: esp32s3
framework:
type: esp-idf
psram:
mode: octal
disabled: true
binary_sensor:
- platform: gpio
pin: GPIO34
name: test
@@ -0,0 +1,15 @@
esphome:
name: test
esp32:
variant: esp32s3
framework:
type: esp-idf
psram:
mode: octal
binary_sensor:
- platform: gpio
pin: GPIO34
name: test
@@ -0,0 +1,15 @@
esphome:
name: test
esp32:
variant: esp32s3
framework:
type: esp-idf
psram:
mode: quad
binary_sensor:
- platform: gpio
pin: GPIO34
name: test
+26
View File
@@ -213,6 +213,32 @@ def test_execute_from_psram_p4_sdkconfig(
assert "CONFIG_SPIRAM_RODATA" not in sdkconfig
@pytest.mark.parametrize(
("fixture", "expect_warning"),
[
("psram_quad_gpio34.yaml", False),
("psram_octal_gpio34.yaml", True),
("psram_octal_disabled_gpio34.yaml", False),
],
)
def test_s3_psram_pin_warning_only_for_octal(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
caplog: pytest.LogCaptureFixture,
fixture: str,
expect_warning: bool,
) -> None:
"""GPIO33-37 are only used by the PSRAM interface in octal mode.
Using such a pin must only warn when octal PSRAM is configured; on quad
PSRAM the pins are free and warning would be a false positive (#16857).
"""
with caplog.at_level("WARNING"):
generate_main(component_config_path(fixture))
warned = "GPIO34 is used by the PSRAM interface in octal mode" in caplog.text
assert warned == expect_warning
def test_ignore_pin_validation_error_on_clean_pin_warns(
set_core_config: SetCoreConfigCallable,
caplog: pytest.LogCaptureFixture,