ESPHome 2026.2.2
Loading...
Searching...
No Matches
remote_transmitter_esp32.cpp
Go to the documentation of this file.
2#include "esphome/core/log.h"
4
5#ifdef USE_ESP32
6#include <soc/soc_caps.h>
7#if SOC_RMT_SUPPORTED
8#include <driver/gpio.h>
9
11
12static const char *const TAG = "remote_transmitter";
13
14// Maximum RMT symbol duration (15-bit field)
15static constexpr uint32_t RMT_SYMBOL_DURATION_MAX = 0x7FFF;
16
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);
23 size_t count = 0;
24
25 // copy symbols
26 for (size_t i = 0; i < free; i++) {
27 uint16_t sym_0 = encoded[store->index++].val;
28 if (store->index >= length) {
29 store->index = 0;
30 store->times--;
31 if (store->times == 0) {
32 *done = true;
33 symbols[count++].val = sym_0;
34 return count;
35 }
36 }
37 uint16_t sym_1 = encoded[store->index++].val;
38 if (store->index >= length) {
39 store->index = 0;
40 store->times--;
41 if (store->times == 0) {
42 *done = true;
43 symbols[count++].val = sym_0 | (sym_1 << 16);
44 return count;
45 }
46 }
47 symbols[count++].val = sym_0 | (sym_1 << 16);
48 }
49 *done = false;
50 return count;
51}
52#endif
53
55 this->inverted_ = this->pin_->is_inverted();
56 this->configure_rmt_();
57}
58
60 ESP_LOGCONFIG(TAG, "Remote Transmitter:");
61 ESP_LOGCONFIG(TAG,
62 " Clock resolution: %" PRIu32 " hz\n"
63 " RMT symbols: %" PRIu32,
64 this->clock_resolution_, this->rmt_symbols_);
65 LOG_PIN(" Pin: ", this->pin_);
66
67 if (this->current_carrier_frequency_ != 0 && this->carrier_duty_percent_ != 100) {
68 ESP_LOGCONFIG(TAG, " Carrier Duty: %u%%", this->carrier_duty_percent_);
69 }
70
71 if (this->is_failed()) {
72 ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_),
73 this->error_string_.c_str());
74 }
75}
76
78#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
79 rmt_symbol_half_t symbol = {
80 .duration = 1,
81 .level = value,
82 };
83 rmt_transmit_config_t config;
84 memset(&config, 0, sizeof(config));
85 config.flags.eot_level = value;
86 this->store_.times = 1;
87 this->store_.index = 0;
88#else
89 rmt_symbol_word_t symbol = {
90 .duration0 = 1,
91 .level0 = value,
92 .duration1 = 0,
93 .level1 = value,
94 };
95 rmt_transmit_config_t config;
96 memset(&config, 0, sizeof(config));
97 config.flags.eot_level = value;
98#endif
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));
102 this->status_set_warning();
103 }
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));
107 this->status_set_warning();
108 }
109}
110
112 esp_err_t error;
113
114 if (!this->initialized_) {
115 bool open_drain = (this->pin_->get_flags() & gpio::FLAG_OPEN_DRAIN) != 0;
116 rmt_tx_channel_config_t channel;
117 memset(&channel, 0, sizeof(channel));
118 channel.clk_src = RMT_CLK_SRC_DEFAULT;
119 channel.resolution_hz = this->clock_resolution_;
120 channel.gpio_num = gpio_num_t(this->pin_->get_pin());
121 channel.mem_block_symbols = this->rmt_symbols_;
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) {
130 this->error_code_ = error;
131 if (error == ESP_ERR_NOT_FOUND) {
132 this->error_string_ = "out of RMT symbol memory";
133 } else {
134 this->error_string_ = "in rmt_new_tx_channel";
135 }
136 this->mark_failed();
137 return;
138 }
139 if (this->pin_->get_flags() & gpio::FLAG_PULLUP) {
140 gpio_pullup_en(gpio_num_t(this->pin_->get_pin()));
141 } else {
142 gpio_pullup_dis(gpio_num_t(this->pin_->get_pin()));
143 }
144
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) {
153 this->error_code_ = error;
154 this->error_string_ = "in rmt_new_simple_encoder";
155 this->mark_failed();
156 return;
157 }
158#else
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) {
163 this->error_code_ = error;
164 this->error_string_ = "in rmt_new_copy_encoder";
165 this->mark_failed();
166 return;
167 }
168#endif
169
170 error = rmt_enable(this->channel_);
171 if (error != ESP_OK) {
172 this->error_code_ = error;
173 this->error_string_ = "in rmt_enable";
174 this->mark_failed();
175 return;
176 }
177 this->digital_write(open_drain || this->inverted_);
178 this->initialized_ = true;
179 }
180
181 if (this->current_carrier_frequency_ == 0 || this->carrier_duty_percent_ == 100) {
182 error = rmt_apply_carrier(this->channel_, nullptr);
183 } else {
184 rmt_carrier_config_t carrier;
185 memset(&carrier, 0, sizeof(carrier));
186 carrier.frequency_hz = this->current_carrier_frequency_;
187 carrier.duty_cycle = (float) this->carrier_duty_percent_ / 100.0f;
188 carrier.flags.polarity_active_low = this->inverted_;
189 carrier.flags.always_on = 1;
190 error = rmt_apply_carrier(this->channel_, &carrier);
191 }
192 if (error != ESP_OK) {
193 this->error_code_ = error;
194 this->error_string_ = "in rmt_apply_carrier";
195 this->mark_failed();
196 return;
197 }
198}
199
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));
204 this->status_set_warning();
205 }
206
208}
209
210#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
211void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
212 uint64_t total_duration = 0;
213
214 if (this->is_failed()) {
215 return;
216 }
217
218 // if the timeout was cancelled, block until the tx is complete
219 if (this->non_blocking_ && this->cancel_timeout("complete")) {
220 this->wait_for_rmt_();
221 }
222
225 this->configure_rmt_();
226 }
227
228 this->rmt_temp_.clear();
229 this->rmt_temp_.reserve(this->temp_.get_data().size() + 1);
230
231 // encode any delay at the start of the buffer to simplify the encoder callback
232 // this will be skipped the first time around
233 total_duration += send_wait * (send_times - 1);
234 send_wait = this->from_microseconds_(static_cast<uint32_t>(send_wait));
235 while (send_wait > 0) {
236 int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX));
237 this->rmt_temp_.push_back({
238 .duration = static_cast<uint16_t>(duration),
239 .level = static_cast<uint16_t>(this->eot_level_),
240 });
241 send_wait -= duration;
242 }
243
244 // encode data
245 size_t offset = this->rmt_temp_.size();
246 for (int32_t value : this->temp_.get_data()) {
247 bool level = value >= 0;
248 if (!level) {
249 value = -value;
250 }
251 total_duration += value * send_times;
252 value = this->from_microseconds_(static_cast<uint32_t>(value));
253 while (value > 0) {
254 int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX));
255 this->rmt_temp_.push_back({
256 .duration = static_cast<uint16_t>(duration),
257 .level = static_cast<uint16_t>(level ^ this->inverted_),
258 });
259 value -= duration;
260 }
261 }
262
263 if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.size() <= offset) {
264 ESP_LOGE(TAG, "Empty data");
265 return;
266 }
267
269
270 rmt_transmit_config_t config;
271 memset(&config, 0, sizeof(config));
272 config.flags.eot_level = this->eot_level_;
273 this->store_.times = send_times;
274 this->store_.index = offset;
275 esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
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));
279 this->status_set_warning();
280 } else {
281 this->status_clear_warning();
282 }
283
284 if (this->non_blocking_) {
285 this->set_timeout("complete", total_duration / 1000, [this]() { this->wait_for_rmt_(); });
286 } else {
287 this->wait_for_rmt_();
288 }
289}
290#else
291void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
292 if (this->is_failed())
293 return;
294
297 this->configure_rmt_();
298 }
299
300 this->rmt_temp_.clear();
301 this->rmt_temp_.reserve((this->temp_.get_data().size() + 1) / 2);
302 uint32_t rmt_i = 0;
303 rmt_symbol_word_t rmt_item;
304
305 for (int32_t val : this->temp_.get_data()) {
306 bool level = val >= 0;
307 if (!level)
308 val = -val;
309 val = this->from_microseconds_(static_cast<uint32_t>(val));
310
311 do {
312 int32_t item = std::min(val, int32_t(RMT_SYMBOL_DURATION_MAX));
313 val -= item;
314
315 if (rmt_i % 2 == 0) {
316 rmt_item.level0 = static_cast<uint32_t>(level ^ this->inverted_);
317 rmt_item.duration0 = static_cast<uint32_t>(item);
318 } else {
319 rmt_item.level1 = static_cast<uint32_t>(level ^ this->inverted_);
320 rmt_item.duration1 = static_cast<uint32_t>(item);
321 this->rmt_temp_.push_back(rmt_item);
322 }
323 rmt_i++;
324 } while (val != 0);
325 }
326
327 if (rmt_i % 2 == 1) {
328 rmt_item.level1 = 0;
329 rmt_item.duration1 = 0;
330 this->rmt_temp_.push_back(rmt_item);
331 }
332
333 if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.empty()) {
334 ESP_LOGE(TAG, "Empty data");
335 return;
336 }
338 for (uint32_t i = 0; i < send_times; i++) {
339 rmt_transmit_config_t config;
340 memset(&config, 0, sizeof(config));
341 config.flags.eot_level = this->eot_level_;
342 esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
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));
346 this->status_set_warning();
347 } else {
348 this->status_clear_warning();
349 }
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));
353 this->status_set_warning();
354 }
355 if (i + 1 < send_times)
356 delayMicroseconds(send_wait);
357 }
359}
360#endif
361
362} // namespace esphome::remote_transmitter
363
364#endif // SOC_RMT_SUPPORTED
365#endif // USE_ESP32
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
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.
Definition component.h:429
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.
Definition component.h:451
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.
Definition automation.h:279
uint32_t from_microseconds_(uint32_t us)
const RawTimings & get_data() const
Definition remote_base.h:32
RemoteTransmitData temp_
Use same vector for all transmits, avoids many allocations.
void send_internal(uint32_t send_times, uint32_t send_wait) override
mopeka_std_values val[4]
uint8_t duration
Definition msa3xx.h:0
@ FLAG_OPEN_DRAIN
Definition gpio.h:29
@ FLAG_PULLUP
Definition gpio.h:30
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:28
size_t size
Definition helpers.h:729
int written
Definition helpers.h:736
uint16_t length
Definition tt21100.cpp:0