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