mirror of
https://github.com/esphome/esphome.git
synced 2026-10-10 21:39:06 +00:00
[deep_sleep][gpio][core][power_supply] Prepare for gpio hold (2/3) (#18966)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
a613073ee3
commit
a4a09043ec
@@ -27,6 +27,7 @@ CONF_ENABLE_OTA_DOWNGRADE_PROTECTION = "enable_ota_downgrade_protection"
|
||||
CONF_ENABLED = "enabled"
|
||||
CONF_GYROSCOPE_ODR = "gyroscope_odr"
|
||||
CONF_GYROSCOPE_RANGE = "gyroscope_range"
|
||||
CONF_HOLD_STATE = "hold_state"
|
||||
CONF_HOST = "host"
|
||||
CONF_HUMIDITY_SOURCE = "humidity_source"
|
||||
CONF_IAQ = "iaq"
|
||||
|
||||
@@ -111,7 +111,17 @@ void DeepSleepComponent::deep_sleep_() {
|
||||
if (this->sleep_duration_.has_value())
|
||||
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
|
||||
|
||||
// Single pin wakeup (ext0) - ESP32, S2, S3 only
|
||||
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
|
||||
#ifdef USE_GPIO_HOLD
|
||||
// Variants without per-pin deep sleep hold need the global enable
|
||||
gpio_deep_sleep_hold_en();
|
||||
#else
|
||||
if (this->wakeup_pin_ != nullptr)
|
||||
gpio_deep_sleep_hold_en();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Single pin wakeup (ext0) - ESP32, S2, S3 only
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32C2) && !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)
|
||||
@@ -124,11 +134,6 @@ void DeepSleepComponent::deep_sleep_() {
|
||||
}
|
||||
gpio_sleep_set_direction(gpio_pin, GPIO_MODE_INPUT);
|
||||
gpio_hold_en(gpio_pin);
|
||||
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
|
||||
// Some ESP32 variants support holding a single GPIO during deep sleep without this function
|
||||
// For those variants, gpio_hold_en() is sufficient to hold the pin state during deep sleep
|
||||
gpio_deep_sleep_hold_en();
|
||||
#endif
|
||||
bool level = !this->wakeup_pin_->is_inverted();
|
||||
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
||||
level = !level;
|
||||
|
||||
@@ -31,6 +31,14 @@ class ESP32InternalGPIOPin final : public InternalGPIOPin {
|
||||
bool is_inverted() const override { return this->pin_flags_.inverted; }
|
||||
gpio_num_t get_pin_num() const { return static_cast<gpio_num_t>(this->pin_); }
|
||||
gpio_drive_cap_t get_drive_strength() const { return static_cast<gpio_drive_cap_t>(this->pin_flags_.drive_strength); }
|
||||
#ifdef USE_GPIO_HOLD
|
||||
bool is_held() const override {
|
||||
if (!this->get_hold())
|
||||
return false;
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
return reason == ESP_RST_DEEPSLEEP || reason == ESP_RST_SW;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
from esphome.components.const import CONF_HOLD_STATE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PIN
|
||||
from esphome.const import CONF_ID, CONF_PIN, CONF_POWER_SUPPLY
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import gpio_ns
|
||||
@@ -17,6 +19,27 @@ CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def _require_hold(ps_config: ConfigType) -> ConfigType:
|
||||
if not ps_config[CONF_PIN].get(CONF_HOLD_STATE):
|
||||
raise cv.Invalid(
|
||||
f"The power supply pin must also set {CONF_HOLD_STATE} "
|
||||
f"when an output it powers sets it",
|
||||
[CONF_PIN],
|
||||
)
|
||||
return ps_config
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
if not config[CONF_PIN].get(CONF_HOLD_STATE):
|
||||
return
|
||||
if (ps_id := config.get(CONF_POWER_SUPPLY)) is None:
|
||||
return
|
||||
fv.id_declaration_match_schema(_require_hold)(ps_id)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
@@ -11,9 +11,12 @@ class GPIOBinaryOutput final : public output::BinaryOutput, public Component {
|
||||
void set_pin(GPIOPin *pin) { pin_ = pin; }
|
||||
|
||||
void setup() override {
|
||||
this->turn_off();
|
||||
const bool held = this->pin_->is_held();
|
||||
if (!held)
|
||||
this->turn_off();
|
||||
this->pin_->setup();
|
||||
this->turn_off();
|
||||
if (!held)
|
||||
this->turn_off();
|
||||
}
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
@@ -12,8 +12,9 @@ float GPIOSwitch::get_setup_priority() const { return setup_priority::HARDWARE;
|
||||
void GPIOSwitch::setup() {
|
||||
bool initial_state = this->get_initial_state_with_restore_mode().value_or(false);
|
||||
|
||||
// write state before setup
|
||||
this->control(initial_state);
|
||||
// write state before setup unless the pin kept it across the reset
|
||||
if (!this->pin_->is_held())
|
||||
this->control(initial_state);
|
||||
this->pin_->setup();
|
||||
// write after setup again for other IOs
|
||||
this->control(initial_state);
|
||||
|
||||
@@ -9,7 +9,12 @@ static constexpr uint32_t POWER_OFF_TIMEOUT_ID = 0;
|
||||
|
||||
void PowerSupply::setup() {
|
||||
this->pin_->setup();
|
||||
this->pin_->digital_write(false);
|
||||
if (this->pin_->is_held()) {
|
||||
// Rail stayed on across the reset; drop it unless something asks for it
|
||||
this->schedule_off_if_idle_();
|
||||
} else {
|
||||
this->pin_->digital_write(false);
|
||||
}
|
||||
if (this->enable_on_boot_)
|
||||
this->request_high_power();
|
||||
}
|
||||
@@ -47,6 +52,10 @@ void PowerSupply::unrequest_high_power() {
|
||||
return;
|
||||
}
|
||||
this->active_requests_--;
|
||||
this->schedule_off_if_idle_();
|
||||
}
|
||||
|
||||
void PowerSupply::schedule_off_if_idle_() {
|
||||
if (this->active_requests_ == 0) {
|
||||
this->set_timeout(POWER_OFF_TIMEOUT_ID, this->keep_on_time_, [this]() {
|
||||
ESP_LOGV(TAG, "Disabling");
|
||||
@@ -54,7 +63,10 @@ void PowerSupply::unrequest_high_power() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void PowerSupply::on_powerdown() {
|
||||
if (this->pin_->get_hold())
|
||||
return;
|
||||
this->active_requests_ = 0;
|
||||
this->pin_->digital_write(false);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class PowerSupply final : public Component {
|
||||
uint32_t keep_on_time_;
|
||||
int16_t active_requests_{0}; // use signed integer to make catching negative requests easier.
|
||||
bool enable_on_boot_{false};
|
||||
void schedule_off_if_idle_();
|
||||
};
|
||||
|
||||
class PowerSupplyRequester {
|
||||
|
||||
@@ -474,6 +474,7 @@
|
||||
#define USE_USB_UART_CP210X
|
||||
#define USE_USB_UART_FT23XX
|
||||
#define USE_USB_UART_PL2303
|
||||
#define USE_GPIO_HOLD
|
||||
// USB identity on serial proxy ports needs the usb_host stack
|
||||
#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S31) || defined(USE_ESP32_VARIANT_ESP32H4)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -27,6 +28,7 @@ enum Flags : uint8_t {
|
||||
FLAG_OPEN_DRAIN = 0x04,
|
||||
FLAG_PULLUP = 0x08,
|
||||
FLAG_PULLDOWN = 0x10,
|
||||
FLAG_HOLD = 0x20,
|
||||
};
|
||||
|
||||
class FlagsHelper {
|
||||
@@ -79,6 +81,13 @@ class GPIOPin {
|
||||
virtual size_t dump_summary(char *buffer, size_t len) const;
|
||||
|
||||
virtual bool is_internal() { return false; }
|
||||
#ifdef USE_GPIO_HOLD
|
||||
bool get_hold() const { return this->get_flags() & gpio::FLAG_HOLD; }
|
||||
virtual bool is_held() const { return false; }
|
||||
#else
|
||||
bool get_hold() const { return false; }
|
||||
bool is_held() const { return false; }
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
|
||||
|
||||
Reference in New Issue
Block a user