mirror of
https://github.com/esphome/esphome.git
synced 2026-02-08 00:31:58 +00:00
[psram] Require mode for S3 (#11470)
Co-authored-by: clydeps <U5yx99dok9>
This commit is contained in:
@@ -4,6 +4,7 @@ from esphome import automation, pins
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
from esphome.components.esp32 import add_idf_component
|
from esphome.components.esp32 import add_idf_component
|
||||||
|
from esphome.components.psram import DOMAIN as psram_domain
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BRIGHTNESS,
|
CONF_BRIGHTNESS,
|
||||||
@@ -26,10 +27,9 @@ import esphome.final_validate as fv
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
AUTO_LOAD = ["camera"]
|
||||||
DEPENDENCIES = ["esp32"]
|
DEPENDENCIES = ["esp32"]
|
||||||
|
|
||||||
AUTO_LOAD = ["camera", "psram"]
|
|
||||||
|
|
||||||
esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera")
|
esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera")
|
||||||
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase)
|
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase)
|
||||||
ESP32CameraImageData = esp32_camera_ns.struct("CameraImageData")
|
ESP32CameraImageData = esp32_camera_ns.struct("CameraImageData")
|
||||||
@@ -163,6 +163,14 @@ CONF_ON_IMAGE = "on_image"
|
|||||||
|
|
||||||
camera_range_param = cv.int_range(min=-2, max=2)
|
camera_range_param = cv.int_range(min=-2, max=2)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_fb_location_(value):
|
||||||
|
validator = cv.enum(ENUM_FB_LOCATION, upper=True)
|
||||||
|
if value.lower() == psram_domain:
|
||||||
|
validator = cv.All(validator, cv.requires_component(psram_domain))
|
||||||
|
return validator(value)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.ENTITY_BASE_SCHEMA.extend(
|
cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
@@ -236,9 +244,9 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.framerate, cv.Range(min=0, max=1)
|
cv.framerate, cv.Range(min=0, max=1)
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
|
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
|
||||||
cv.Optional(CONF_FRAME_BUFFER_LOCATION, default="PSRAM"): cv.enum(
|
cv.Optional(
|
||||||
ENUM_FB_LOCATION, upper=True
|
CONF_FRAME_BUFFER_LOCATION, default="PSRAM"
|
||||||
),
|
): validate_fb_location_,
|
||||||
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
|
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ import esphome.final_validate as fv
|
|||||||
|
|
||||||
from .const import INKPLATE_10_CUSTOM_WAVEFORMS, WAVEFORMS
|
from .const import INKPLATE_10_CUSTOM_WAVEFORMS, WAVEFORMS
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c", "esp32"]
|
DEPENDENCIES = ["i2c", "esp32", "psram"]
|
||||||
AUTO_LOAD = ["psram"]
|
|
||||||
|
|
||||||
CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin"
|
CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin"
|
||||||
CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin"
|
CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components.esp32 import (
|
from esphome.components.esp32 import (
|
||||||
@@ -104,6 +105,17 @@ def get_config_schema(config):
|
|||||||
if not speeds:
|
if not speeds:
|
||||||
raise cv.Invalid("PSRAM is not supported on this chip")
|
raise cv.Invalid("PSRAM is not supported on this chip")
|
||||||
modes = SPIRAM_MODES[variant]
|
modes = SPIRAM_MODES[variant]
|
||||||
|
if CONF_MODE not in config and len(modes) != 1:
|
||||||
|
raise (
|
||||||
|
cv.Invalid(
|
||||||
|
textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
{variant} requires PSRAM mode selection; one of {", ".join(modes)}
|
||||||
|
Selection of the wrong mode for the board will cause a runtime failure to initialise PSRAM
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
return cv.Schema(
|
return cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(PsramComponent),
|
cv.GenerateID(): cv.declare_id(PsramComponent),
|
||||||
|
|||||||
@@ -26,21 +26,12 @@ from esphome.const import (
|
|||||||
from esphome.core import CORE, HexInt
|
from esphome.core import CORE, HexInt
|
||||||
from esphome.core.entity_helpers import inherit_property_from
|
from esphome.core.entity_helpers import inherit_property_from
|
||||||
from esphome.external_files import download_content
|
from esphome.external_files import download_content
|
||||||
from esphome.types import ConfigType
|
from esphome.final_validate import full_config
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def AUTO_LOAD(config: ConfigType) -> list[str]:
|
AUTO_LOAD = ["audio"]
|
||||||
load = ["audio"]
|
|
||||||
if (
|
|
||||||
not config
|
|
||||||
or config.get(CONF_TASK_STACK_IN_PSRAM)
|
|
||||||
or config.get(CONF_CODEC_SUPPORT_ENABLED)
|
|
||||||
):
|
|
||||||
return load + ["psram"]
|
|
||||||
return load
|
|
||||||
|
|
||||||
|
|
||||||
CODEOWNERS = ["@kahrendt", "@synesthesiam"]
|
CODEOWNERS = ["@kahrendt", "@synesthesiam"]
|
||||||
DOMAIN = "media_player"
|
DOMAIN = "media_player"
|
||||||
@@ -226,12 +217,19 @@ def _validate_repeated_speaker(config):
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def _validate_supported_local_file(config):
|
def _final_validate(config):
|
||||||
|
# Default to using codec if psram is enabled
|
||||||
|
if (use_codec := config.get(CONF_CODEC_SUPPORT_ENABLED)) is None:
|
||||||
|
use_codec = psram.DOMAIN in full_config.get()
|
||||||
|
conf_id = config[CONF_ID].id
|
||||||
|
core_data = CORE.data.setdefault(DOMAIN, {conf_id: {}})
|
||||||
|
core_data[conf_id][CONF_CODEC_SUPPORT_ENABLED] = use_codec
|
||||||
|
|
||||||
for file_config in config.get(CONF_FILES, []):
|
for file_config in config.get(CONF_FILES, []):
|
||||||
_, media_file_type = _read_audio_file_and_type(file_config)
|
_, media_file_type = _read_audio_file_and_type(file_config)
|
||||||
if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]):
|
if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]):
|
||||||
raise cv.Invalid("Unsupported local media file")
|
raise cv.Invalid("Unsupported local media file")
|
||||||
if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str(
|
if not use_codec and str(media_file_type) != str(
|
||||||
audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
||||||
):
|
):
|
||||||
# Only wav files are supported
|
# Only wav files are supported
|
||||||
@@ -290,11 +288,11 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
||||||
min=4000, max=4000000
|
min=4000, max=4000000
|
||||||
),
|
),
|
||||||
cv.Optional(
|
cv.Optional(CONF_CODEC_SUPPORT_ENABLED): cv.boolean,
|
||||||
CONF_CODEC_SUPPORT_ENABLED, default=psram.supported()
|
|
||||||
): cv.boolean,
|
|
||||||
cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA),
|
cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA),
|
||||||
cv.Optional(CONF_TASK_STACK_IN_PSRAM, default=False): cv.boolean,
|
cv.Optional(CONF_TASK_STACK_IN_PSRAM): cv.All(
|
||||||
|
cv.boolean, cv.requires_component(psram.DOMAIN)
|
||||||
|
),
|
||||||
cv.Optional(CONF_VOLUME_INCREMENT, default=0.05): cv.percentage,
|
cv.Optional(CONF_VOLUME_INCREMENT, default=0.05): cv.percentage,
|
||||||
cv.Optional(CONF_VOLUME_INITIAL, default=0.5): cv.percentage,
|
cv.Optional(CONF_VOLUME_INITIAL, default=0.5): cv.percentage,
|
||||||
cv.Optional(CONF_VOLUME_MAX, default=1.0): cv.percentage,
|
cv.Optional(CONF_VOLUME_MAX, default=1.0): cv.percentage,
|
||||||
@@ -317,12 +315,12 @@ FINAL_VALIDATE_SCHEMA = cv.All(
|
|||||||
},
|
},
|
||||||
extra=cv.ALLOW_EXTRA,
|
extra=cv.ALLOW_EXTRA,
|
||||||
),
|
),
|
||||||
_validate_supported_local_file,
|
_final_validate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if config[CONF_CODEC_SUPPORT_ENABLED]:
|
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 and optimize the wifi settings
|
||||||
|
|
||||||
cg.add_define("USE_AUDIO_FLAC_SUPPORT", True)
|
cg.add_define("USE_AUDIO_FLAC_SUPPORT", True)
|
||||||
@@ -352,8 +350,8 @@ async def to_code(config):
|
|||||||
|
|
||||||
cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE]))
|
cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE]))
|
||||||
|
|
||||||
cg.add(var.set_task_stack_in_psram(config[CONF_TASK_STACK_IN_PSRAM]))
|
if config.get(CONF_TASK_STACK_IN_PSRAM):
|
||||||
if config[CONF_TASK_STACK_IN_PSRAM]:
|
cg.add(var.set_task_stack_in_psram(True))
|
||||||
esp32.add_idf_sdkconfig_option(
|
esp32.add_idf_sdkconfig_option(
|
||||||
"CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True
|
"CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ SUPPORTED_PSRAM_VARIANTS = [
|
|||||||
VARIANT_ESP32S3,
|
VARIANT_ESP32S3,
|
||||||
VARIANT_ESP32P4,
|
VARIANT_ESP32P4,
|
||||||
]
|
]
|
||||||
|
SUPPORTED_PSRAM_MODES = {
|
||||||
|
VARIANT_ESP32: ["quad"],
|
||||||
|
VARIANT_ESP32S2: ["quad"],
|
||||||
|
VARIANT_ESP32S3: ["quad", "octal"],
|
||||||
|
VARIANT_ESP32P4: ["hex"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -86,7 +92,7 @@ def test_psram_configuration_valid_supported_variants(
|
|||||||
from esphome.components.psram import CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA
|
from esphome.components.psram import CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA
|
||||||
|
|
||||||
# This should not raise an exception
|
# This should not raise an exception
|
||||||
config = CONFIG_SCHEMA({})
|
config = CONFIG_SCHEMA({"mode": SUPPORTED_PSRAM_MODES[variant][0]})
|
||||||
FINAL_VALIDATE_SCHEMA(config)
|
FINAL_VALIDATE_SCHEMA(config)
|
||||||
|
|
||||||
|
|
||||||
@@ -122,7 +128,7 @@ def _setup_psram_final_validation_test(
|
|||||||
("config", "esp32_config", "expect_error", "error_match"),
|
("config", "esp32_config", "expect_error", "error_match"),
|
||||||
[
|
[
|
||||||
pytest.param(
|
pytest.param(
|
||||||
{"speed": "120MHz"},
|
{"mode": "quad", "speed": "120MHz"},
|
||||||
{"cpu_frequency": "160MHz"},
|
{"cpu_frequency": "160MHz"},
|
||||||
True,
|
True,
|
||||||
r"PSRAM 120MHz requires 240MHz CPU frequency",
|
r"PSRAM 120MHz requires 240MHz CPU frequency",
|
||||||
@@ -143,7 +149,7 @@ def _setup_psram_final_validation_test(
|
|||||||
id="ecc_only_in_octal_mode",
|
id="ecc_only_in_octal_mode",
|
||||||
),
|
),
|
||||||
pytest.param(
|
pytest.param(
|
||||||
{"speed": "120MHZ"},
|
{"mode": "quad", "speed": "120MHZ"},
|
||||||
{"cpu_frequency": "240MHZ"},
|
{"cpu_frequency": "240MHZ"},
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
packages:
|
packages:
|
||||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||||
|
|
||||||
|
psram:
|
||||||
|
mode: quad
|
||||||
|
|
||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ display:
|
|||||||
lvgl:
|
lvgl:
|
||||||
|
|
||||||
psram:
|
psram:
|
||||||
|
mode: quad
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# I2C bus for camera sensor
|
# I2C bus for camera sensor
|
||||||
|
psram:
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
- id: i2c_camera_bus
|
- id: i2c_camera_bus
|
||||||
sda: 25
|
sda: 25
|
||||||
|
|||||||
Reference in New Issue
Block a user