ESPHome  2025.2.0
remote_transmitter_esp32.cpp
Go to the documentation of this file.
1 #include "remote_transmitter.h"
2 #include "esphome/core/log.h"
4 
5 #ifdef USE_ESP32
6 #include <driver/gpio.h>
7 
8 namespace esphome {
9 namespace remote_transmitter {
10 
11 static 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 
189 void 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
RemoteTransmitData temp_
Use same vector for all transmits, avoids many allocations.
Definition: remote_base.h:191
mopeka_std_values val[4]
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition: automation.h:95
virtual gpio::Flags get_flags() const =0
Retrieve GPIO pin flags.
virtual uint8_t get_pin() const =0
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:324
void send_internal(uint32_t send_times, uint32_t send_wait) override
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
const RawTimings & get_data() const
Definition: remote_base.h:36
virtual bool is_inverted() const =0