diff --git a/CODEOWNERS b/CODEOWNERS index 08ce43b94c..9e4202f2d4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -150,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_rmt/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp8266/* @esphome/core +esphome/components/esp_ldo/* @clydebarrow esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/event/* @nohat esphome/components/event_emitter/* @Rapsssito diff --git a/esphome/components/esp_ldo/__init__.py b/esphome/components/esp_ldo/__init__.py new file mode 100644 index 0000000000..ce24028083 --- /dev/null +++ b/esphome/components/esp_ldo/__init__.py @@ -0,0 +1,91 @@ +from esphome.automation import Action, register_action +import esphome.codegen as cg +from esphome.components.esp32 import VARIANT_ESP32P4, only_on_variant +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ID, CONF_VOLTAGE +from esphome.final_validate import full_config + +CODEOWNERS = ["@clydebarrow"] + +DOMAIN = "esp_ldo" + +esp_ldo_ns = cg.esphome_ns.namespace("esp_ldo") +EspLdo = esp_ldo_ns.class_("EspLdo", cg.Component) +AdjustAction = esp_ldo_ns.class_("AdjustAction", Action) + +CHANNELS = (3, 4) +CONF_ADJUSTABLE = "adjustable" + +adjusted_ids = set() + +CONFIG_SCHEMA = cv.All( + cv.ensure_list( + { + cv.GenerateID(): cv.declare_id(EspLdo), + cv.Required(CONF_VOLTAGE): cv.All( + cv.voltage, cv.float_range(min=0.5, max=2.7) + ), + cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), + cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, + } + ), + cv.only_with_esp_idf, + only_on_variant(supported=[VARIANT_ESP32P4]), +) + + +async def to_code(configs): + for config in configs: + var = cg.new_Pvariable(config[CONF_ID], config[CONF_CHANNEL]) + await cg.register_component(var, config) + cg.add(var.set_voltage(config[CONF_VOLTAGE])) + cg.add(var.set_adjustable(config[CONF_ADJUSTABLE])) + + +def final_validate(configs): + for channel in CHANNELS: + used = [config for config in configs if config[CONF_CHANNEL] == channel] + if len(used) > 1: + raise cv.Invalid( + f"Multiple LDOs configured for channel {channel}. Each channel must be unique.", + path=[CONF_CHANNEL, channel], + ) + + global_config = full_config.get() + for w in adjusted_ids: + path = global_config.get_path_for_id(w) + ldo_conf = global_config.get_config_for_path(path[:-1]) + if not ldo_conf[CONF_ADJUSTABLE]: + raise cv.Invalid( + "A non adjustable LDO may not be adjusted.", + path, + ) + + +FINAL_VALIDATE_SCHEMA = final_validate + + +def adjusted_ldo_id(value): + value = cv.use_id(EspLdo)(value) + adjusted_ids.add(value) + return value + + +@register_action( + "esp_ldo.voltage.adjust", + AdjustAction, + cv.Schema( + { + cv.GenerateID(CONF_ID): adjusted_ldo_id, + cv.Required(CONF_VOLTAGE): cv.templatable( + cv.All(cv.voltage, cv.float_range(min=0.5, max=2.7)) + ), + } + ), +) +async def ldo_voltage_adjust_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + template_ = await cg.templatable(config[CONF_VOLTAGE], args, cg.float_) + cg.add(var.set_voltage(template_)) + return var diff --git a/esphome/components/esp_ldo/esp_ldo.cpp b/esphome/components/esp_ldo/esp_ldo.cpp new file mode 100644 index 0000000000..eb04670d7e --- /dev/null +++ b/esphome/components/esp_ldo/esp_ldo.cpp @@ -0,0 +1,43 @@ +#ifdef USE_ESP32_VARIANT_ESP32P4 +#include "esp_ldo.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace esp_ldo { + +static const char *const TAG = "esp_ldo"; +void EspLdo::setup() { + esp_ldo_channel_config_t config{}; + config.chan_id = this->channel_; + config.voltage_mv = (int) (this->voltage_ * 1000.0f); + config.flags.adjustable = this->adjustable_; + auto err = esp_ldo_acquire_channel(&config, &this->handle_); + if (err != ESP_OK) { + auto msg = str_sprintf("Failed to acquire LDO channel %d with voltage %fV", this->channel_, this->voltage_); + this->mark_failed(msg.c_str()); + } else { + ESP_LOGD(TAG, "Acquired LDO channel %d with voltage %fV", this->channel_, this->voltage_); + } +} +void EspLdo::dump_config() { + ESP_LOGCONFIG(TAG, "ESP LDO Channel %d:", this->channel_); + ESP_LOGCONFIG(TAG, " Voltage: %fV", this->voltage_); + ESP_LOGCONFIG(TAG, " Adjustable: %s", YESNO(this->adjustable_)); +} + +void EspLdo::adjust_voltage(float voltage) { + if (!std::isfinite(voltage) || voltage < 0.5f || voltage > 2.7f) { + ESP_LOGE(TAG, "Invalid voltage %fV for LDO channel %d", voltage, this->channel_); + return; + } + auto erro = esp_ldo_channel_adjust_voltage(this->handle_, (int) (voltage * 1000.0f)); + if (erro != ESP_OK) { + ESP_LOGE(TAG, "Failed to adjust LDO channel %d to voltage %fV: %s", this->channel_, voltage, esp_err_to_name(erro)); + } +} + +} // namespace esp_ldo +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32P4 diff --git a/esphome/components/esp_ldo/esp_ldo.h b/esphome/components/esp_ldo/esp_ldo.h new file mode 100644 index 0000000000..fb5abf7a3a --- /dev/null +++ b/esphome/components/esp_ldo/esp_ldo.h @@ -0,0 +1,43 @@ +#pragma once +#ifdef USE_ESP32_VARIANT_ESP32P4 +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esp_ldo_regulator.h" + +namespace esphome { +namespace esp_ldo { + +class EspLdo : public Component { + public: + EspLdo(int channel) : channel_(channel) {} + + void setup() override; + void dump_config() override; + + void set_adjustable(bool adjustable) { this->adjustable_ = adjustable; } + void set_voltage(float voltage) { this->voltage_ = voltage; } + void adjust_voltage(float voltage); + + protected: + int channel_; + float voltage_{2.7}; + bool adjustable_{false}; + esp_ldo_channel_handle_t handle_{}; +}; + +template class AdjustAction : public Action { + public: + explicit AdjustAction(EspLdo *ldo) : ldo_(ldo) {} + + TEMPLATABLE_VALUE(float, voltage) + + void play(Ts... x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); } + + protected: + EspLdo *ldo_; +}; + +} // namespace esp_ldo +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32P4 diff --git a/tests/components/esp_ldo/test.esp32-p4-idf.yaml b/tests/components/esp_ldo/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..0e91aaf082 --- /dev/null +++ b/tests/components/esp_ldo/test.esp32-p4-idf.yaml @@ -0,0 +1,15 @@ +esp_ldo: + - id: ldo_id + channel: 3 + voltage: 2.5V + adjustable: true + - id: ldo_4 + channel: 4 + voltage: 2.0V + +esphome: + on_boot: + then: + - esp_ldo.voltage.adjust: + id: ldo_id + voltage: !lambda return 2.5;