1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 15:12:06 +00:00

[remote_transmitter] Add non-blocking mode (#11524)

This commit is contained in:
Jonathan Swoboda
2025-10-29 12:40:22 -04:00
committed by GitHub
parent 66cf7c3a3b
commit 6fb490f49b
4 changed files with 48 additions and 6 deletions

View File

@@ -1,3 +1,5 @@
import logging
from esphome import automation, pins from esphome import automation, pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import esp32, esp32_rmt, remote_base from esphome.components import esp32, esp32_rmt, remote_base
@@ -18,9 +20,12 @@ from esphome.const import (
) )
from esphome.core import CORE from esphome.core import CORE
_LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["remote_base"] AUTO_LOAD = ["remote_base"]
CONF_EOT_LEVEL = "eot_level" CONF_EOT_LEVEL = "eot_level"
CONF_NON_BLOCKING = "non_blocking"
CONF_ON_TRANSMIT = "on_transmit" CONF_ON_TRANSMIT = "on_transmit"
CONF_ON_COMPLETE = "on_complete" CONF_ON_COMPLETE = "on_complete"
CONF_TRANSMITTER_ID = remote_base.CONF_TRANSMITTER_ID CONF_TRANSMITTER_ID = remote_base.CONF_TRANSMITTER_ID
@@ -65,11 +70,25 @@ CONFIG_SCHEMA = cv.Schema(
esp32_c6=48, esp32_c6=48,
esp32_h2=48, esp32_h2=48,
): cv.All(cv.only_on_esp32, cv.int_range(min=2)), ): cv.All(cv.only_on_esp32, cv.int_range(min=2)),
cv.Optional(CONF_NON_BLOCKING): cv.All(cv.only_on_esp32, cv.boolean),
cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True), cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True),
cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True), cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def _validate_non_blocking(config):
if CORE.is_esp32 and CONF_NON_BLOCKING not in config:
_LOGGER.warning(
"'non_blocking' is not set for 'remote_transmitter' and will default to 'true'.\n"
"The default behavior changed in 2025.11.0; previously blocking mode was used.\n"
"To silence this warning, explicitly set 'non_blocking: true' (or 'false')."
)
config[CONF_NON_BLOCKING] = True
FINAL_VALIDATE_SCHEMA = _validate_non_blocking
DIGITAL_WRITE_ACTION_SCHEMA = cv.maybe_simple_value( DIGITAL_WRITE_ACTION_SCHEMA = cv.maybe_simple_value(
{ {
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterComponent), cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterComponent),
@@ -95,6 +114,7 @@ async def to_code(config):
if CORE.is_esp32: if CORE.is_esp32:
var = cg.new_Pvariable(config[CONF_ID], pin) var = cg.new_Pvariable(config[CONF_ID], pin)
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
cg.add(var.set_non_blocking(config[CONF_NON_BLOCKING]))
if CONF_CLOCK_RESOLUTION in config: if CONF_CLOCK_RESOLUTION in config:
cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION]))
if CONF_USE_DMA in config: if CONF_USE_DMA in config:

View File

@@ -54,6 +54,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
#if defined(USE_ESP32) #if defined(USE_ESP32)
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; } void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; }
void set_non_blocking(bool non_blocking) { this->non_blocking_ = non_blocking; }
#endif #endif
Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; }; Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; };
@@ -74,6 +75,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
#ifdef USE_ESP32 #ifdef USE_ESP32
void configure_rmt_(); void configure_rmt_();
void wait_for_rmt_();
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
RemoteTransmitterComponentStore store_{}; RemoteTransmitterComponentStore store_{};
@@ -90,6 +92,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
esp_err_t error_code_{ESP_OK}; esp_err_t error_code_{ESP_OK};
std::string error_string_{""}; std::string error_string_{""};
bool inverted_{false}; bool inverted_{false};
bool non_blocking_{false};
#endif #endif
uint8_t carrier_duty_percent_; uint8_t carrier_duty_percent_;

View File

@@ -196,12 +196,29 @@ void RemoteTransmitterComponent::configure_rmt_() {
} }
} }
void RemoteTransmitterComponent::wait_for_rmt_() {
esp_err_t error = rmt_tx_wait_all_done(this->channel_, -1);
if (error != ESP_OK) {
ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
this->status_set_warning();
}
this->complete_trigger_->trigger();
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
uint64_t total_duration = 0;
if (this->is_failed()) { if (this->is_failed()) {
return; return;
} }
// if the timeout was cancelled, block until the tx is complete
if (this->non_blocking_ && this->cancel_timeout("complete")) {
this->wait_for_rmt_();
}
if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) { if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) {
this->current_carrier_frequency_ = this->temp_.get_carrier_frequency(); this->current_carrier_frequency_ = this->temp_.get_carrier_frequency();
this->configure_rmt_(); this->configure_rmt_();
@@ -212,6 +229,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
// encode any delay at the start of the buffer to simplify the encoder callback // encode any delay at the start of the buffer to simplify the encoder callback
// this will be skipped the first time around // this will be skipped the first time around
total_duration += send_wait * (send_times - 1);
send_wait = this->from_microseconds_(static_cast<uint32_t>(send_wait)); send_wait = this->from_microseconds_(static_cast<uint32_t>(send_wait));
while (send_wait > 0) { while (send_wait > 0) {
int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX)); int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX));
@@ -229,6 +247,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
if (!level) { if (!level) {
value = -value; value = -value;
} }
total_duration += value * send_times;
value = this->from_microseconds_(static_cast<uint32_t>(value)); value = this->from_microseconds_(static_cast<uint32_t>(value));
while (value > 0) { while (value > 0) {
int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX)); int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX));
@@ -260,13 +279,12 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
} else { } else {
this->status_clear_warning(); this->status_clear_warning();
} }
error = rmt_tx_wait_all_done(this->channel_, -1);
if (error != ESP_OK) {
ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
this->status_set_warning();
}
this->complete_trigger_->trigger(); if (this->non_blocking_) {
this->set_timeout("complete", total_duration / 1000, [this]() { this->wait_for_rmt_(); });
} else {
this->wait_for_rmt_();
}
} }
#else #else
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {

View File

@@ -2,6 +2,7 @@ remote_transmitter:
- id: xmitr - id: xmitr
pin: ${pin} pin: ${pin}
carrier_duty_percent: 50% carrier_duty_percent: 50%
non_blocking: true
clock_resolution: ${clock_resolution} clock_resolution: ${clock_resolution}
rmt_symbols: ${rmt_symbols} rmt_symbols: ${rmt_symbols}