ESPHome 2025.5.0
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 <driver/gpio.h>
7
8namespace esphome {
9namespace remote_transmitter {
10
11static const char *const TAG = "remote_transmitter";
12
14 ESP_LOGCONFIG(TAG, "Setting up Remote Transmitter...");
15 this->inverted_ = this->pin_->is_inverted();
16 this->configure_rmt_();
17}
18
20 ESP_LOGCONFIG(TAG, "Remote Transmitter:");
21#if ESP_IDF_VERSION_MAJOR >= 5
22 ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_);
23 ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_);
24#else
25 ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_);
26 ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_);
27 ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_);
28#endif
29 LOG_PIN(" Pin: ", this->pin_);
30
31 if (this->current_carrier_frequency_ != 0 && this->carrier_duty_percent_ != 100) {
32 ESP_LOGCONFIG(TAG, " Carrier Duty: %u%%", this->carrier_duty_percent_);
33 }
34
35 if (this->is_failed()) {
36 ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_),
37 this->error_string_.c_str());
38 }
39}
40
41#if ESP_IDF_VERSION_MAJOR >= 5
43 rmt_symbol_word_t symbol = {
44 .duration0 = 1,
45 .level0 = value,
46 .duration1 = 0,
47 .level1 = value,
48 };
49 rmt_transmit_config_t config;
50 memset(&config, 0, sizeof(config));
51 config.loop_count = 0;
52 config.flags.eot_level = value;
53 esp_err_t error = rmt_transmit(this->channel_, this->encoder_, &symbol, sizeof(symbol), &config);
54 if (error != ESP_OK) {
55 ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
56 this->status_set_warning();
57 }
58 error = rmt_tx_wait_all_done(this->channel_, -1);
59 if (error != ESP_OK) {
60 ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
61 this->status_set_warning();
62 }
63}
64#endif
65
67#if ESP_IDF_VERSION_MAJOR >= 5
68 esp_err_t error;
69
70 if (!this->initialized_) {
71 bool open_drain = (this->pin_->get_flags() & gpio::FLAG_OPEN_DRAIN) != 0;
72 rmt_tx_channel_config_t channel;
73 memset(&channel, 0, sizeof(channel));
74 channel.clk_src = RMT_CLK_SRC_DEFAULT;
75 channel.resolution_hz = this->clock_resolution_;
76 channel.gpio_num = gpio_num_t(this->pin_->get_pin());
77 channel.mem_block_symbols = this->rmt_symbols_;
78 channel.trans_queue_depth = 1;
79 channel.flags.io_loop_back = open_drain;
80 channel.flags.io_od_mode = open_drain;
81 channel.flags.invert_out = 0;
82 channel.flags.with_dma = this->with_dma_;
83 channel.intr_priority = 0;
84 error = rmt_new_tx_channel(&channel, &this->channel_);
85 if (error != ESP_OK) {
86 this->error_code_ = error;
87 if (error == ESP_ERR_NOT_FOUND) {
88 this->error_string_ = "out of RMT symbol memory";
89 } else {
90 this->error_string_ = "in rmt_new_tx_channel";
91 }
92 this->mark_failed();
93 return;
94 }
95 if (this->pin_->get_flags() & gpio::FLAG_PULLUP) {
96 gpio_pullup_en(gpio_num_t(this->pin_->get_pin()));
97 } else {
98 gpio_pullup_dis(gpio_num_t(this->pin_->get_pin()));
99 }
100
101 rmt_copy_encoder_config_t encoder;
102 memset(&encoder, 0, sizeof(encoder));
103 error = rmt_new_copy_encoder(&encoder, &this->encoder_);
104 if (error != ESP_OK) {
105 this->error_code_ = error;
106 this->error_string_ = "in rmt_new_copy_encoder";
107 this->mark_failed();
108 return;
109 }
110
111 error = rmt_enable(this->channel_);
112 if (error != ESP_OK) {
113 this->error_code_ = error;
114 this->error_string_ = "in rmt_enable";
115 this->mark_failed();
116 return;
117 }
118 this->digital_write(open_drain || this->inverted_);
119 this->initialized_ = true;
120 }
121
122 if (this->current_carrier_frequency_ == 0 || this->carrier_duty_percent_ == 100) {
123 error = rmt_apply_carrier(this->channel_, nullptr);
124 } else {
125 rmt_carrier_config_t carrier;
126 memset(&carrier, 0, sizeof(carrier));
127 carrier.frequency_hz = this->current_carrier_frequency_;
128 carrier.duty_cycle = (float) this->carrier_duty_percent_ / 100.0f;
129 carrier.flags.polarity_active_low = this->inverted_;
130 carrier.flags.always_on = 1;
131 error = rmt_apply_carrier(this->channel_, &carrier);
132 }
133 if (error != ESP_OK) {
134 this->error_code_ = error;
135 this->error_string_ = "in rmt_apply_carrier";
136 this->mark_failed();
137 return;
138 }
139#else
140 rmt_config_t c{};
141
142 this->config_rmt(c);
143 c.rmt_mode = RMT_MODE_TX;
144 c.gpio_num = gpio_num_t(this->pin_->get_pin());
145 c.tx_config.loop_en = false;
146
147 if (this->current_carrier_frequency_ == 0 || this->carrier_duty_percent_ == 100) {
148 c.tx_config.carrier_en = false;
149 } else {
150 c.tx_config.carrier_en = true;
151 c.tx_config.carrier_freq_hz = this->current_carrier_frequency_;
152 c.tx_config.carrier_duty_percent = this->carrier_duty_percent_;
153 }
154
155 c.tx_config.idle_output_en = true;
156 if (!this->inverted_) {
157 c.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
158 c.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
159 } else {
160 c.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
161 c.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
162 }
163
164 esp_err_t error = rmt_config(&c);
165 if (error != ESP_OK) {
166 this->error_code_ = error;
167 this->error_string_ = "in rmt_config";
168 this->mark_failed();
169 return;
170 }
171
172 if (!this->initialized_) {
173 error = rmt_driver_install(this->channel_, 0, 0);
174 if (error != ESP_OK) {
175 this->error_code_ = error;
176 if (error == ESP_ERR_INVALID_STATE) {
177 this->error_string_ = str_sprintf("RMT channel %i is already in use by another component", this->channel_);
178 } else {
179 this->error_string_ = "in rmt_driver_install";
180 }
181 this->mark_failed();
182 return;
183 }
184 this->initialized_ = true;
185 }
186#endif
187}
188
189void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
190 if (this->is_failed())
191 return;
192
195 this->configure_rmt_();
196 }
197
198 this->rmt_temp_.clear();
199 this->rmt_temp_.reserve((this->temp_.get_data().size() + 1) / 2);
200 uint32_t rmt_i = 0;
201#if ESP_IDF_VERSION_MAJOR >= 5
202 rmt_symbol_word_t rmt_item;
203#else
204 rmt_item32_t rmt_item;
205#endif
206
207 for (int32_t val : this->temp_.get_data()) {
208 bool level = val >= 0;
209 if (!level)
210 val = -val;
211 val = this->from_microseconds_(static_cast<uint32_t>(val));
212
213 do {
214 int32_t item = std::min(val, int32_t(32767));
215 val -= item;
216
217 if (rmt_i % 2 == 0) {
218 rmt_item.level0 = static_cast<uint32_t>(level ^ this->inverted_);
219 rmt_item.duration0 = static_cast<uint32_t>(item);
220 } else {
221 rmt_item.level1 = static_cast<uint32_t>(level ^ this->inverted_);
222 rmt_item.duration1 = static_cast<uint32_t>(item);
223 this->rmt_temp_.push_back(rmt_item);
224 }
225 rmt_i++;
226 } while (val != 0);
227 }
228
229 if (rmt_i % 2 == 1) {
230 rmt_item.level1 = 0;
231 rmt_item.duration1 = 0;
232 this->rmt_temp_.push_back(rmt_item);
233 }
234
235 if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.empty()) {
236 ESP_LOGE(TAG, "Empty data");
237 return;
238 }
239 this->transmit_trigger_->trigger();
240#if ESP_IDF_VERSION_MAJOR >= 5
241 for (uint32_t i = 0; i < send_times; i++) {
242 rmt_transmit_config_t config;
243 memset(&config, 0, sizeof(config));
244 config.loop_count = 0;
245 config.flags.eot_level = this->eot_level_;
246 esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
247 this->rmt_temp_.size() * sizeof(rmt_symbol_word_t), &config);
248 if (error != ESP_OK) {
249 ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
250 this->status_set_warning();
251 } else {
252 this->status_clear_warning();
253 }
254 error = rmt_tx_wait_all_done(this->channel_, -1);
255 if (error != ESP_OK) {
256 ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
257 this->status_set_warning();
258 }
259 if (i + 1 < send_times)
260 delayMicroseconds(send_wait);
261 }
262#else
263 for (uint32_t i = 0; i < send_times; i++) {
264 esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true);
265 if (error != ESP_OK) {
266 ESP_LOGW(TAG, "rmt_write_items failed: %s", esp_err_to_name(error));
267 this->status_set_warning();
268 } else {
269 this->status_clear_warning();
270 }
271 if (i + 1 < send_times)
272 delayMicroseconds(send_wait);
273 }
274#endif
275 this->complete_trigger_->trigger();
276}
277
278} // namespace remote_transmitter
279} // namespace esphome
280
281#endif
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
void status_set_warning(const char *message="unspecified")
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(Ts... x)
Inform the parent automation that the event has triggered.
Definition automation.h:96
uint32_t from_microseconds_(uint32_t us)
const RawTimings & get_data() const
Definition remote_base.h:36
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]
@ FLAG_OPEN_DRAIN
Definition gpio.h:20
@ FLAG_PULLUP
Definition gpio.h:21
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:30
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:323