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.io_loop_back = open_drain;
124 channel.flags.io_od_mode = open_drain;
125 channel.flags.invert_out = 0;
126 channel.flags.with_dma = this->
with_dma_;
127 channel.intr_priority = 0;
128 error = rmt_new_tx_channel(&channel, &this->
channel_);
129 if (error != ESP_OK) {
131 if (error == ESP_ERR_NOT_FOUND) {
142 gpio_pullup_dis(gpio_num_t(this->
pin_->
get_pin()));
145#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
146 rmt_simple_encoder_config_t encoder;
147 memset(&encoder, 0,
sizeof(encoder));
148 encoder.callback = encoder_callback;
149 encoder.arg = &this->
store_;
150 encoder.min_chunk_size = 1;
151 error = rmt_new_simple_encoder(&encoder, &this->
encoder_);
152 if (error != ESP_OK) {
159 rmt_copy_encoder_config_t encoder;
160 memset(&encoder, 0,
sizeof(encoder));
161 error = rmt_new_copy_encoder(&encoder, &this->
encoder_);
162 if (error != ESP_OK) {
171 if (error != ESP_OK) {
182 error = rmt_apply_carrier(this->
channel_,
nullptr);
184 rmt_carrier_config_t carrier;
185 memset(&carrier, 0,
sizeof(carrier));
188 carrier.flags.polarity_active_low = this->
inverted_;
189 carrier.flags.always_on = 1;
190 error = rmt_apply_carrier(this->
channel_, &carrier);
192 if (error != ESP_OK) {
201 esp_err_t error = rmt_tx_wait_all_done(this->
channel_, -1);
202 if (error != ESP_OK) {
203 ESP_LOGW(TAG,
"rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
210#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
212 uint64_t total_duration = 0;
233 total_duration += send_wait * (send_times - 1);
235 while (send_wait > 0) {
238 .duration =
static_cast<uint16_t
>(
duration),
239 .level =
static_cast<uint16_t
>(this->
eot_level_),
247 bool level = value >= 0;
251 total_duration += value * send_times;
254 int32_t
duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX));
256 .duration =
static_cast<uint16_t
>(
duration),
257 .level =
static_cast<uint16_t
>(level ^ this->
inverted_),
263 if ((this->
rmt_temp_.data() ==
nullptr) || this->rmt_temp_.size() <= offset) {
264 ESP_LOGE(TAG,
"Empty data");
270 rmt_transmit_config_t config;
271 memset(&config, 0,
sizeof(config));
276 this->rmt_temp_.size() *
sizeof(rmt_symbol_half_t), &config);
277 if (error != ESP_OK) {
278 ESP_LOGW(TAG,
"rmt_transmit failed: %s", esp_err_to_name(error));
303 rmt_symbol_word_t rmt_item;
306 bool level =
val >= 0;
312 int32_t item = std::min(
val, int32_t(RMT_SYMBOL_DURATION_MAX));
315 if (rmt_i % 2 == 0) {
317 rmt_item.duration0 =
static_cast<uint32_t>(item);
320 rmt_item.duration1 =
static_cast<uint32_t>(item);
327 if (rmt_i % 2 == 1) {
329 rmt_item.duration1 = 0;
333 if ((this->
rmt_temp_.data() ==
nullptr) || this->rmt_temp_.empty()) {
334 ESP_LOGE(TAG,
"Empty data");
338 for (uint32_t i = 0; i < send_times; i++) {
339 rmt_transmit_config_t config;
340 memset(&config, 0,
sizeof(config));
343 this->rmt_temp_.size() *
sizeof(rmt_symbol_word_t), &config);
344 if (error != ESP_OK) {
345 ESP_LOGW(TAG,
"rmt_transmit failed: %s", esp_err_to_name(error));
350 error = rmt_tx_wait_all_done(this->
channel_, -1);
351 if (error != ESP_OK) {
352 ESP_LOGW(TAG,
"rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
355 if (i + 1 < send_times)
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message=nullptr)
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)
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_
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)