mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 14:55:05 +00:00
[esp32] Add ESP32-S31, ESP32-H4 and ESP32-H21 variant scaffolding (#16700)
This commit is contained in:
@@ -78,9 +78,12 @@ from .const import (
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32C61,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32H4,
|
||||
VARIANT_ESP32H21,
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32S31,
|
||||
VARIANT_FRIENDLY,
|
||||
VARIANTS,
|
||||
)
|
||||
@@ -403,9 +406,12 @@ CPU_FREQUENCIES = {
|
||||
VARIANT_ESP32C6: get_cpu_frequencies(80, 120, 160),
|
||||
VARIANT_ESP32C61: get_cpu_frequencies(80, 120, 160),
|
||||
VARIANT_ESP32H2: get_cpu_frequencies(16, 32, 48, 64, 96),
|
||||
VARIANT_ESP32H4: get_cpu_frequencies(48, 64, 96),
|
||||
VARIANT_ESP32H21: get_cpu_frequencies(48, 64, 96),
|
||||
VARIANT_ESP32P4: get_cpu_frequencies(40, 360, 400),
|
||||
VARIANT_ESP32S2: get_cpu_frequencies(80, 160, 240),
|
||||
VARIANT_ESP32S3: get_cpu_frequencies(80, 160, 240),
|
||||
VARIANT_ESP32S31: get_cpu_frequencies(240, 320),
|
||||
}
|
||||
|
||||
# Make sure not missed here if a new variant added.
|
||||
@@ -907,11 +913,16 @@ def _validate_toolchain(value) -> Toolchain:
|
||||
return Toolchain(cv.one_of(*(t.value for t in Toolchain), lower=True)(value))
|
||||
|
||||
|
||||
def _check_versions(config):
|
||||
def _resolve_toolchain(value: ConfigType) -> ConfigType:
|
||||
# Resolve toolchain: CLI (already on CORE.toolchain) > YAML > default.
|
||||
# Runs before _detect_variant so downstream validators can rely on
|
||||
# CORE.toolchain instead of re-resolving it from the config dict.
|
||||
if CORE.toolchain is None:
|
||||
CORE.toolchain = config.get(CONF_TOOLCHAIN, Toolchain.PLATFORMIO)
|
||||
CORE.toolchain = value.get(CONF_TOOLCHAIN, Toolchain.PLATFORMIO)
|
||||
return value
|
||||
|
||||
|
||||
def _check_versions(config: ConfigType) -> ConfigType:
|
||||
if CORE.using_toolchain_esp_idf:
|
||||
return _check_esp_idf_versions(config)
|
||||
return _check_pio_versions(config)
|
||||
@@ -933,7 +944,21 @@ def _detect_variant(value):
|
||||
variant = value.get(CONF_VARIANT)
|
||||
if variant and board is None:
|
||||
# If variant is set, we can derive the board from it
|
||||
# variant has already been validated against the known set
|
||||
# variant has already been validated against the known set.
|
||||
# PlatformIO needs a real board name to find its board file; the
|
||||
# ESP-IDF toolchain only uses CONF_BOARD as the informational
|
||||
# ESPHOME_BOARD string, so synthesize one from the friendly variant
|
||||
# name rather than carrying a PIO board name through the IDF build.
|
||||
if CORE.using_toolchain_esp_idf:
|
||||
value = value.copy()
|
||||
value[CONF_BOARD] = VARIANT_FRIENDLY[variant].lower()
|
||||
return value
|
||||
if variant not in STANDARD_BOARDS:
|
||||
raise cv.Invalid(
|
||||
f"No default board is known for {variant}. "
|
||||
f"Please specify the `board:` option explicitly.",
|
||||
path=[CONF_VARIANT],
|
||||
)
|
||||
value = value.copy()
|
||||
value[CONF_BOARD] = STANDARD_BOARDS[variant]
|
||||
if variant == VARIANT_ESP32P4:
|
||||
@@ -1606,6 +1631,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
}
|
||||
),
|
||||
_resolve_toolchain,
|
||||
_detect_variant,
|
||||
_set_default_framework,
|
||||
_check_versions,
|
||||
|
||||
@@ -9,7 +9,6 @@ from .const import (
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANTS,
|
||||
)
|
||||
|
||||
STANDARD_BOARDS = {
|
||||
@@ -25,9 +24,6 @@ STANDARD_BOARDS = {
|
||||
VARIANT_ESP32S3: "esp32-s3-devkitc-1",
|
||||
}
|
||||
|
||||
# Make sure not missed here if a new variant added.
|
||||
assert all(v in STANDARD_BOARDS for v in VARIANTS)
|
||||
|
||||
ESP32_BASE_PINS = {
|
||||
"TX": 1,
|
||||
"RX": 3,
|
||||
|
||||
@@ -24,9 +24,12 @@ VARIANT_ESP32C5 = "ESP32C5"
|
||||
VARIANT_ESP32C6 = "ESP32C6"
|
||||
VARIANT_ESP32C61 = "ESP32C61"
|
||||
VARIANT_ESP32H2 = "ESP32H2"
|
||||
VARIANT_ESP32H4 = "ESP32H4"
|
||||
VARIANT_ESP32H21 = "ESP32H21"
|
||||
VARIANT_ESP32P4 = "ESP32P4"
|
||||
VARIANT_ESP32S2 = "ESP32S2"
|
||||
VARIANT_ESP32S3 = "ESP32S3"
|
||||
VARIANT_ESP32S31 = "ESP32S31"
|
||||
VARIANTS = [
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C2,
|
||||
@@ -35,9 +38,12 @@ VARIANTS = [
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32C61,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32H4,
|
||||
VARIANT_ESP32H21,
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32S31,
|
||||
]
|
||||
|
||||
VARIANT_FRIENDLY = {
|
||||
@@ -48,9 +54,12 @@ VARIANT_FRIENDLY = {
|
||||
VARIANT_ESP32C6: "ESP32-C6",
|
||||
VARIANT_ESP32C61: "ESP32-C61",
|
||||
VARIANT_ESP32H2: "ESP32-H2",
|
||||
VARIANT_ESP32H4: "ESP32-H4",
|
||||
VARIANT_ESP32H21: "ESP32-H21",
|
||||
VARIANT_ESP32P4: "ESP32-P4",
|
||||
VARIANT_ESP32S2: "ESP32-S2",
|
||||
VARIANT_ESP32S3: "ESP32-S3",
|
||||
VARIANT_ESP32S31: "ESP32-S31",
|
||||
}
|
||||
|
||||
esp32_ns = cg.esphome_ns.namespace("esp32")
|
||||
|
||||
@@ -31,9 +31,12 @@ from .const import (
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32C61,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32H4,
|
||||
VARIANT_ESP32H21,
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32S31,
|
||||
esp32_ns,
|
||||
)
|
||||
from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports
|
||||
@@ -43,9 +46,12 @@ from .gpio_esp32_c5 import esp32_c5_validate_gpio_pin, esp32_c5_validate_support
|
||||
from .gpio_esp32_c6 import esp32_c6_validate_gpio_pin, esp32_c6_validate_supports
|
||||
from .gpio_esp32_c61 import esp32_c61_validate_gpio_pin, esp32_c61_validate_supports
|
||||
from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports
|
||||
from .gpio_esp32_h4 import esp32_h4_validate_gpio_pin, esp32_h4_validate_supports
|
||||
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_s31 import esp32_s31_validate_gpio_pin, esp32_s31_validate_supports
|
||||
|
||||
ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin)
|
||||
|
||||
@@ -120,6 +126,14 @@ _esp32_validations = {
|
||||
pin_validation=esp32_h2_validate_gpio_pin,
|
||||
usage_validation=esp32_h2_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32H4: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_h4_validate_gpio_pin,
|
||||
usage_validation=esp32_h4_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32H21: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_h21_validate_gpio_pin,
|
||||
usage_validation=esp32_h21_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32P4: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_p4_validate_gpio_pin,
|
||||
usage_validation=esp32_p4_validate_supports,
|
||||
@@ -132,6 +146,10 @@ _esp32_validations = {
|
||||
pin_validation=esp32_s3_validate_gpio_pin,
|
||||
usage_validation=esp32_s3_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32S31: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_s31_validate_gpio_pin,
|
||||
usage_validation=esp32_s31_validate_supports,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
34
esphome/components/esp32/gpio_esp32_h21.py
Normal file
34
esphome/components/esp32/gpio_esp32_h21.py
Normal file
@@ -0,0 +1,34 @@
|
||||
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
|
||||
|
||||
# Partial set from the ESP-IDF / esptool boot-mode docs:
|
||||
# https://docs.espressif.com/projects/esptool/en/latest/esp32h21/advanced-topics/boot-mode-selection.html
|
||||
# The full list awaits the ESP32-H21 datasheet's "Strapping Pins" section.
|
||||
_ESP32H21_STRAPPING_PINS: set[int] = {13, 14}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def esp32_h21_validate_gpio_pin(value: int) -> int:
|
||||
if value < 0 or value > 25:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-25)")
|
||||
return value
|
||||
|
||||
|
||||
def esp32_h21_validate_supports(value: dict[str, Any]) -> dict[str, Any]:
|
||||
num = value[CONF_NUMBER]
|
||||
mode = value[CONF_MODE]
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 25:
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-25)")
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
pass
|
||||
|
||||
check_strapping_pin(value, _ESP32H21_STRAPPING_PINS, _LOGGER)
|
||||
return value
|
||||
34
esphome/components/esp32/gpio_esp32_h4.py
Normal file
34
esphome/components/esp32/gpio_esp32_h4.py
Normal file
@@ -0,0 +1,34 @@
|
||||
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
|
||||
|
||||
# Partial set from the ESP-IDF / esptool boot-mode docs:
|
||||
# https://docs.espressif.com/projects/esptool/en/latest/esp32h4/advanced-topics/boot-mode-selection.html
|
||||
# The full list awaits the ESP32-H4 datasheet's "Strapping Pins" section.
|
||||
_ESP32H4_STRAPPING_PINS: set[int] = {13, 14}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def esp32_h4_validate_gpio_pin(value: int) -> int:
|
||||
if value < 0 or value > 39:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-39)")
|
||||
return value
|
||||
|
||||
|
||||
def esp32_h4_validate_supports(value: dict[str, Any]) -> dict[str, Any]:
|
||||
num = value[CONF_NUMBER]
|
||||
mode = value[CONF_MODE]
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 39:
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-39)")
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
pass
|
||||
|
||||
check_strapping_pin(value, _ESP32H4_STRAPPING_PINS, _LOGGER)
|
||||
return value
|
||||
38
esphome/components/esp32/gpio_esp32_s31.py
Normal file
38
esphome/components/esp32/gpio_esp32_s31.py
Normal file
@@ -0,0 +1,38 @@
|
||||
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
|
||||
|
||||
# Per the ESP32-S31 datasheet (page 96):
|
||||
# https://documentation.espressif.com/esp32-s31_datasheet_en.pdf
|
||||
_ESP32S31_SPI_FLASH_PINS: set[int] = {27, 28, 29, 31, 32, 33}
|
||||
_ESP32S31_STRAPPING_PINS: set[int] = {60, 61}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def esp32_s31_validate_gpio_pin(value: int) -> int:
|
||||
if value < 0 or value > 61:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-61)")
|
||||
if value in _ESP32S31_SPI_FLASH_PINS:
|
||||
raise cv.Invalid(
|
||||
f"GPIO{value} is reserved for the SPI flash interface on ESP32-S31 and cannot be used."
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def esp32_s31_validate_supports(value: dict[str, Any]) -> dict[str, Any]:
|
||||
num = value[CONF_NUMBER]
|
||||
mode = value[CONF_MODE]
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 61:
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-61)")
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
pass
|
||||
|
||||
check_strapping_pin(value, _ESP32S31_STRAPPING_PINS, _LOGGER)
|
||||
return value
|
||||
@@ -11,9 +11,12 @@ from esphome.components.esp32 import (
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32C61,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32H4,
|
||||
VARIANT_ESP32H21,
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32S31,
|
||||
add_idf_sdkconfig_option,
|
||||
get_esp32_variant,
|
||||
require_usb_serial_jtag_secondary,
|
||||
@@ -113,9 +116,12 @@ UART_SELECTION_ESP32 = {
|
||||
VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32C61: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32H4: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32H21: [UART0, UART1, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32P4: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32S2: [UART0, UART1, USB_CDC],
|
||||
VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
VARIANT_ESP32S31: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
|
||||
}
|
||||
|
||||
UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1]
|
||||
@@ -270,9 +276,12 @@ CONFIG_SCHEMA = cv.All(
|
||||
esp32_c6=USB_SERIAL_JTAG,
|
||||
esp32_c61=USB_SERIAL_JTAG,
|
||||
esp32_h2=USB_SERIAL_JTAG,
|
||||
esp32_h4=USB_SERIAL_JTAG,
|
||||
esp32_h21=USB_SERIAL_JTAG,
|
||||
esp32_p4=USB_SERIAL_JTAG,
|
||||
esp32_s2=USB_CDC,
|
||||
esp32_s3=USB_SERIAL_JTAG,
|
||||
esp32_s31=USB_SERIAL_JTAG,
|
||||
rp2040=USB_CDC,
|
||||
bk72xx=DEFAULT,
|
||||
ln882x=DEFAULT,
|
||||
|
||||
@@ -354,9 +354,12 @@
|
||||
#if defined(USE_ESP32_VARIANT_ESP32S2)
|
||||
#define USE_LOGGER_USB_CDC
|
||||
#define USE_LOGGER_UART_SELECTION_USB_CDC
|
||||
#elif defined(USE_ESP32_VARIANT_ESP32H21)
|
||||
#define USE_LOGGER_USB_SERIAL_JTAG
|
||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C5) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
defined(USE_ESP32_VARIANT_ESP32H4) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S3) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S31)
|
||||
#define USE_LOGGER_USB_CDC
|
||||
#define USE_LOGGER_UART_SELECTION_USB_CDC
|
||||
#define USE_LOGGER_USB_SERIAL_JTAG
|
||||
|
||||
@@ -45,11 +45,20 @@ def test_esp32_config(
|
||||
config = CONFIG_SCHEMA(config)
|
||||
assert config["variant"] == VARIANT_ESP32
|
||||
|
||||
# Check that defining a variant sets the board name correctly
|
||||
# Check that defining a variant sets the board name correctly.
|
||||
# Run under the ESP-IDF toolchain so variants without an entry in
|
||||
# STANDARD_BOARDS (S31, H4, H21) still derive a board name from
|
||||
# VARIANT_FRIENDLY rather than failing with cv.Invalid. CORE.toolchain
|
||||
# gets pinned by the first CONFIG_SCHEMA() call above (via
|
||||
# _resolve_toolchain) and that pinned value wins over the dict's
|
||||
# CONF_TOOLCHAIN, so clear it between iterations to mirror a fresh
|
||||
# config run.
|
||||
for variant in VARIANTS:
|
||||
CORE.toolchain = None
|
||||
config = CONFIG_SCHEMA(
|
||||
{
|
||||
"variant": variant,
|
||||
"toolchain": Toolchain.ESP_IDF.value,
|
||||
}
|
||||
)
|
||||
assert VARIANT_FRIENDLY[variant].lower() in config["board"]
|
||||
@@ -73,6 +82,11 @@ def test_esp32_config(
|
||||
r"Option 'variant' does not match selected board. @ data\['variant'\]",
|
||||
id="mismatched_board_variant_config",
|
||||
),
|
||||
pytest.param(
|
||||
{"variant": "esp32s31"},
|
||||
r"No default board is known for ESP32S31\. Please specify the `board:` option explicitly\. @ data\['variant'\]",
|
||||
id="variant_without_default_board_requires_explicit_board_under_platformio",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"variant": "esp32s2",
|
||||
|
||||
Reference in New Issue
Block a user