6#include <soc/soc_caps.h>
8#include <driver/gpio.h>
12static const char *
const TAG =
"remote_transmitter";
15static constexpr uint32_t RMT_SYMBOL_DURATION_MAX = 0x7FFF;
17#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
18static size_t IRAM_ATTR HOT encoder_callback(
const void *data,
size_t size,
size_t written,
size_t free,
19 rmt_symbol_word_t *symbols,
bool *done,
void *arg) {
20 auto *store =
static_cast<RemoteTransmitterComponentStore *
>(arg);
21 const auto *encoded =
static_cast<const rmt_symbol_half_t *
>(
data);
22 size_t length =
size /
sizeof(rmt_symbol_half_t);
26 for (
size_t i = 0; i < free; i++) {
27 uint16_t sym_0 = encoded[store->index++].val;
28 if (store->index >=
length) {
31 if (store->times == 0) {
33 symbols[count++].val = sym_0;
37 uint16_t sym_1 = encoded[store->index++].val;
38 if (store->index >=
length) {
41 if (store->times == 0) {
43 symbols[count++].val = sym_0 | (sym_1 << 16);
47 symbols[count++].val = sym_0 | (sym_1 << 16);
60 ESP_LOGCONFIG(TAG,
"Remote Transmitter:");
62 " Clock resolution: %" PRIu32
" hz\n"
63 " RMT symbols: %" PRIu32,
65 LOG_PIN(
" Pin: ", this->
pin_);
72 ESP_LOGE(TAG,
"Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->
error_code_),
78#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
79 rmt_symbol_half_t symbol = {
83 rmt_transmit_config_t config;
84 memset(&config, 0,
sizeof(config));
85 config.flags.eot_level = value;
89 rmt_symbol_word_t symbol = {
95 rmt_transmit_config_t config;
96 memset(&config, 0,
sizeof(config));
97 config.flags.eot_level = value;
99 esp_err_t error = rmt_transmit(this->
channel_, this->
encoder_, &symbol,
sizeof(symbol), &config);
100 if (error != ESP_OK) {
101 ESP_LOGW(TAG,
"rmt_transmit failed: %s", esp_err_to_name(error));
104 error = rmt_tx_wait_all_done(this->
channel_, -1);
105 if (error != ESP_OK) {
106 ESP_LOGW(TAG,
"rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
116 rmt_tx_channel_config_t channel;
117 memset(&channel, 0,
sizeof(channel));
118 channel.clk_src = RMT_CLK_SRC_DEFAULT;
120 channel.gpio_num = gpio_num_t(this->
pin_->
get_pin());
122 channel.trans_queue_depth = 1;
123 channel.flags.invert_out = 0;
124 channel.flags.with_dma = this->
with_dma_;
125 channel.intr_priority = 0;
126#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)
127 channel.flags.io_loop_back = open_drain;
128 channel.flags.io_od_mode = open_drain;
130 error = rmt_new_tx_channel(&channel, &this->
channel_);
131 if (error != ESP_OK) {
133 if (error == ESP_ERR_NOT_FOUND) {
141#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
143 gpio_num_t gpio = gpio_num_t(this->
pin_->
get_pin());
144 gpio_od_enable(gpio);
145 gpio_input_enable(gpio);
151 gpio_pullup_dis(gpio_num_t(this->
pin_->
get_pin()));
154#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
155 rmt_simple_encoder_config_t encoder;
156 memset(&encoder, 0,
sizeof(encoder));
157 encoder.callback = encoder_callback;
158 encoder.arg = &this->
store_;
159 encoder.min_chunk_size = 1;
160 error = rmt_new_simple_encoder(&encoder, &this->
encoder_);
161 if (error != ESP_OK) {
168 rmt_copy_encoder_config_t encoder;
169 memset(&encoder, 0,
sizeof(encoder));
170 error = rmt_new_copy_encoder(&encoder, &this->
encoder_);
171 if (error != ESP_OK) {
180 if (error != ESP_OK) {
191 error = rmt_apply_carrier(this->
channel_,
nullptr);
193 rmt_carrier_config_t carrier;
194 memset(&carrier, 0,
sizeof(carrier));
197 carrier.flags.polarity_active_low = this->
inverted_;
198 carrier.flags.always_on = 1;
199 error = rmt_apply_carrier(this->
channel_, &carrier);
201 if (error != ESP_OK) {
210 esp_err_t error = rmt_tx_wait_all_done(this->
channel_, -1);
211 if (error != ESP_OK) {
212 ESP_LOGW(TAG,
"rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
219#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
221 uint64_t total_duration = 0;
242 total_duration += send_wait * (send_times - 1);
244 while (send_wait > 0) {
247 .duration =
static_cast<uint16_t
>(
duration),
248 .level =
static_cast<uint16_t
>(this->
eot_level_),
256 bool level = value >= 0;
260 total_duration += value * send_times;
263 int32_t
duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX));
265 .duration =
static_cast<uint16_t
>(
duration),
266 .level =
static_cast<uint16_t
>(level ^ this->
inverted_),
272 if ((this->
rmt_temp_.data() ==
nullptr) || this->rmt_temp_.size() <= offset) {
273 ESP_LOGE(TAG,
"Empty data");
279 rmt_transmit_config_t config;
280 memset(&config, 0,
sizeof(config));
285 this->rmt_temp_.size() *
sizeof(rmt_symbol_half_t), &config);
286 if (error != ESP_OK) {
287 ESP_LOGW(TAG,
"rmt_transmit failed: %s", esp_err_to_name(error));
312 rmt_symbol_word_t rmt_item;
315 bool level =
val >= 0;
321 int32_t item = std::min(
val, int32_t(RMT_SYMBOL_DURATION_MAX));
324 if (rmt_i % 2 == 0) {
326 rmt_item.duration0 =
static_cast<uint32_t>(item);
329 rmt_item.duration1 =
static_cast<uint32_t>(item);
336 if (rmt_i % 2 == 1) {
338 rmt_item.duration1 = 0;
342 if ((this->
rmt_temp_.data() ==
nullptr) || this->rmt_temp_.empty()) {
343 ESP_LOGE(TAG,
"Empty data");
347 for (
uint32_t i = 0; i < send_times; i++) {
348 rmt_transmit_config_t config;
349 memset(&config, 0,
sizeof(config));
352 this->rmt_temp_.size() *
sizeof(rmt_symbol_word_t), &config);
353 if (error != ESP_OK) {
354 ESP_LOGW(TAG,
"rmt_transmit failed: %s", esp_err_to_name(error));
359 error = rmt_tx_wait_all_done(this->
channel_, -1);
360 if (error != ESP_OK) {
361 ESP_LOGW(TAG,
"rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
364 if (i + 1 < send_times)
void mark_failed()
Mark this component as failed.
void status_set_warning()
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") bool cancel_timeout(const std boo cancel_timeout)(const char *name)
Cancel a timeout function.
void status_clear_warning()
virtual gpio::Flags get_flags() const =0
Retrieve GPIO pin flags.
virtual uint8_t get_pin() const =0
virtual bool is_inverted() const =0
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
uint32_t clock_resolution_
uint32_t from_microseconds_(uint32_t us)
uint32_t get_carrier_frequency() const
const RawTimings & get_data() const
RemoteTransmitData temp_
Use same vector for all transmits, avoids many allocations.
void send_internal(uint32_t send_times, uint32_t send_wait) override
Trigger complete_trigger_
uint32_t current_carrier_frequency_
std::string error_string_
std::vector< rmt_symbol_half_t > rmt_temp_
Trigger transmit_trigger_
rmt_channel_handle_t channel_
rmt_encoder_handle_t encoder_
void digital_write(bool value)
uint8_t carrier_duty_percent_
void dump_config() override
RemoteTransmitterComponentStore store_
const std::vector< uint8_t > & data
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)