ESPHome  2025.2.0
remote_receiver_esp32.cpp
Go to the documentation of this file.
1 #include "remote_receiver.h"
2 #include "esphome/core/log.h"
3 
4 #ifdef USE_ESP32
5 #include <driver/gpio.h>
6 
7 namespace esphome {
8 namespace remote_receiver {
9 
10 static const char *const TAG = "remote_receiver.esp32";
11 #ifdef USE_ESP32_VARIANT_ESP32H2
12 static const uint32_t RMT_CLK_FREQ = 32000000;
13 #else
14 static const uint32_t RMT_CLK_FREQ = 80000000;
15 #endif
16 
17 #if ESP_IDF_VERSION_MAJOR >= 5
18 static bool IRAM_ATTR HOT rmt_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *event, void *arg) {
19  RemoteReceiverComponentStore *store = (RemoteReceiverComponentStore *) arg;
20  rmt_rx_done_event_data_t *event_buffer = (rmt_rx_done_event_data_t *) (store->buffer + store->buffer_write);
21  uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
22  uint32_t next_write = store->buffer_write + event_size + event->num_symbols * sizeof(rmt_symbol_word_t);
23  if (next_write + event_size + store->receive_size > store->buffer_size) {
24  next_write = 0;
25  }
26  if (store->buffer_read - next_write < event_size + store->receive_size) {
27  next_write = store->buffer_write;
28  store->overflow = true;
29  }
30  if (event->num_symbols <= store->filter_symbols) {
31  next_write = store->buffer_write;
32  }
33  store->error =
34  rmt_receive(channel, (uint8_t *) store->buffer + next_write + event_size, store->receive_size, &store->config);
35  event_buffer->num_symbols = event->num_symbols;
36  event_buffer->received_symbols = event->received_symbols;
37  store->buffer_write = next_write;
38  return false;
39 }
40 #endif
41 
43  ESP_LOGCONFIG(TAG, "Setting up Remote Receiver...");
44 #if ESP_IDF_VERSION_MAJOR >= 5
45  rmt_rx_channel_config_t channel;
46  memset(&channel, 0, sizeof(channel));
47  channel.clk_src = RMT_CLK_SRC_DEFAULT;
48  channel.resolution_hz = this->clock_resolution_;
49  channel.mem_block_symbols = rmt_symbols_;
50  channel.gpio_num = gpio_num_t(this->pin_->get_pin());
51  channel.intr_priority = 0;
52  channel.flags.invert_in = 0;
53  channel.flags.with_dma = this->with_dma_;
54  channel.flags.io_loop_back = 0;
55  esp_err_t error = rmt_new_rx_channel(&channel, &this->channel_);
56  if (error != ESP_OK) {
57  this->error_code_ = error;
58  if (error == ESP_ERR_NOT_FOUND) {
59  this->error_string_ = "out of RMT symbol memory";
60  } else {
61  this->error_string_ = "in rmt_new_rx_channel";
62  }
63  this->mark_failed();
64  return;
65  }
66  if (this->pin_->get_flags() & gpio::FLAG_PULLUP) {
67  gpio_pullup_en(gpio_num_t(this->pin_->get_pin()));
68  } else {
69  gpio_pullup_dis(gpio_num_t(this->pin_->get_pin()));
70  }
71  error = rmt_enable(this->channel_);
72  if (error != ESP_OK) {
73  this->error_code_ = error;
74  this->error_string_ = "in rmt_enable";
75  this->mark_failed();
76  return;
77  }
78 
79  rmt_rx_event_callbacks_t callbacks;
80  memset(&callbacks, 0, sizeof(callbacks));
81  callbacks.on_recv_done = rmt_callback;
82  error = rmt_rx_register_event_callbacks(this->channel_, &callbacks, &this->store_);
83  if (error != ESP_OK) {
84  this->error_code_ = error;
85  this->error_string_ = "in rmt_rx_register_event_callbacks";
86  this->mark_failed();
87  return;
88  }
89 
90  uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
91  uint32_t max_filter_ns = 255u * 1000 / (RMT_CLK_FREQ / 1000000);
92  uint32_t max_idle_ns = 65535u * 1000;
93  memset(&this->store_.config, 0, sizeof(this->store_.config));
94  this->store_.config.signal_range_min_ns = std::min(this->filter_us_ * 1000, max_filter_ns);
95  this->store_.config.signal_range_max_ns = std::min(this->idle_us_ * 1000, max_idle_ns);
97  this->store_.receive_size = this->receive_symbols_ * sizeof(rmt_symbol_word_t);
98  this->store_.buffer_size = std::max((event_size + this->store_.receive_size) * 2, this->buffer_size_);
99  this->store_.buffer = new uint8_t[this->buffer_size_];
100  error = rmt_receive(this->channel_, (uint8_t *) this->store_.buffer + event_size, this->store_.receive_size,
101  &this->store_.config);
102  if (error != ESP_OK) {
103  this->error_code_ = error;
104  this->error_string_ = "in rmt_receive";
105  this->mark_failed();
106  return;
107  }
108 #else
109  this->pin_->setup();
110  rmt_config_t rmt{};
111  this->config_rmt(rmt);
112  rmt.gpio_num = gpio_num_t(this->pin_->get_pin());
113  rmt.rmt_mode = RMT_MODE_RX;
114  if (this->filter_us_ == 0) {
115  rmt.rx_config.filter_en = false;
116  } else {
117  rmt.rx_config.filter_en = true;
118  rmt.rx_config.filter_ticks_thresh = static_cast<uint8_t>(
119  std::min(this->from_microseconds_(this->filter_us_) * this->clock_divider_, (uint32_t) 255));
120  }
121  rmt.rx_config.idle_threshold =
122  static_cast<uint16_t>(std::min(this->from_microseconds_(this->idle_us_), (uint32_t) 65535));
123 
124  esp_err_t error = rmt_config(&rmt);
125  if (error != ESP_OK) {
126  this->error_code_ = error;
127  this->error_string_ = "in rmt_config";
128  this->mark_failed();
129  return;
130  }
131 
132  error = rmt_driver_install(this->channel_, this->buffer_size_, 0);
133  if (error != ESP_OK) {
134  this->error_code_ = error;
135  if (error == ESP_ERR_INVALID_STATE) {
136  this->error_string_ = str_sprintf("RMT channel %i is already in use by another component", this->channel_);
137  } else {
138  this->error_string_ = "in rmt_driver_install";
139  }
140  this->mark_failed();
141  return;
142  }
143  error = rmt_get_ringbuf_handle(this->channel_, &this->ringbuf_);
144  if (error != ESP_OK) {
145  this->error_code_ = error;
146  this->error_string_ = "in rmt_get_ringbuf_handle";
147  this->mark_failed();
148  return;
149  }
150  error = rmt_rx_start(this->channel_, true);
151  if (error != ESP_OK) {
152  this->error_code_ = error;
153  this->error_string_ = "in rmt_rx_start";
154  this->mark_failed();
155  return;
156  }
157 #endif
158 }
159 
161  ESP_LOGCONFIG(TAG, "Remote Receiver:");
162  LOG_PIN(" Pin: ", this->pin_);
163 #if ESP_IDF_VERSION_MAJOR >= 5
164  ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_);
165  ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_);
166  ESP_LOGCONFIG(TAG, " Filter symbols: %" PRIu32, this->filter_symbols_);
167  ESP_LOGCONFIG(TAG, " Receive symbols: %" PRIu32, this->receive_symbols_);
168 #else
169  if (this->pin_->digital_read()) {
170  ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to "
171  "invert the signal using 'inverted: True' in the pin schema!");
172  }
173  ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_);
174  ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_);
175  ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_);
176 #endif
177  ESP_LOGCONFIG(TAG, " Tolerance: %" PRIu32 "%s", this->tolerance_,
178  (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%");
179  ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %" PRIu32 " us", this->filter_us_);
180  ESP_LOGCONFIG(TAG, " Signal is done after %" PRIu32 " us of no changes", this->idle_us_);
181  if (this->is_failed()) {
182  ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_),
183  this->error_string_.c_str());
184  }
185 }
186 
188 #if ESP_IDF_VERSION_MAJOR >= 5
189  if (this->store_.error != ESP_OK) {
190  ESP_LOGE(TAG, "Receive error");
191  this->error_code_ = this->store_.error;
192  this->error_string_ = "in rmt_callback";
193  this->mark_failed();
194  }
195  if (this->store_.overflow) {
196  ESP_LOGW(TAG, "Buffer overflow");
197  this->store_.overflow = false;
198  }
199  uint32_t buffer_write = this->store_.buffer_write;
200  while (this->store_.buffer_read != buffer_write) {
201  rmt_rx_done_event_data_t *event = (rmt_rx_done_event_data_t *) (this->store_.buffer + this->store_.buffer_read);
202  uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
203  uint32_t next_read = this->store_.buffer_read + event_size + event->num_symbols * sizeof(rmt_symbol_word_t);
204  if (next_read + event_size + this->store_.receive_size > this->store_.buffer_size) {
205  next_read = 0;
206  }
207  this->decode_rmt_(event->received_symbols, event->num_symbols);
208  this->store_.buffer_read = next_read;
209 
210  if (!this->temp_.empty()) {
211  this->temp_.push_back(-this->idle_us_);
212  this->call_listeners_dumpers_();
213  }
214  }
215 #else
216  size_t len = 0;
217  auto *item = (rmt_item32_t *) xRingbufferReceive(this->ringbuf_, &len, 0);
218  if (item != nullptr) {
219  this->decode_rmt_(item, len / sizeof(rmt_item32_t));
220  vRingbufferReturnItem(this->ringbuf_, item);
221 
222  if (this->temp_.empty())
223  return;
224 
225  this->temp_.push_back(-this->idle_us_);
226  this->call_listeners_dumpers_();
227  }
228 #endif
229 }
230 
231 #if ESP_IDF_VERSION_MAJOR >= 5
232 void RemoteReceiverComponent::decode_rmt_(rmt_symbol_word_t *item, size_t item_count) {
233 #else
234 void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t item_count) {
235 #endif
236  bool prev_level = false;
237  uint32_t prev_length = 0;
238  this->temp_.clear();
239  int32_t multiplier = this->pin_->is_inverted() ? -1 : 1;
240  uint32_t filter_ticks = this->from_microseconds_(this->filter_us_);
241 
242  ESP_LOGVV(TAG, "START:");
243  for (size_t i = 0; i < item_count; i++) {
244  if (item[i].level0) {
245  ESP_LOGVV(TAG, "%zu A: ON %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration0),
246  item[i].duration0);
247  } else {
248  ESP_LOGVV(TAG, "%zu A: OFF %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration0),
249  item[i].duration0);
250  }
251  if (item[i].level1) {
252  ESP_LOGVV(TAG, "%zu B: ON %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration1),
253  item[i].duration1);
254  } else {
255  ESP_LOGVV(TAG, "%zu B: OFF %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration1),
256  item[i].duration1);
257  }
258  }
259  ESP_LOGVV(TAG, "\n");
260 
261  this->temp_.reserve(item_count * 2); // each RMT item has 2 pulses
262  for (size_t i = 0; i < item_count; i++) {
263  if (item[i].duration0 == 0u) {
264  // EOF, sometimes garbage follows, break early
265  break;
266  } else if ((bool(item[i].level0) == prev_level) || (item[i].duration0 < filter_ticks)) {
267  prev_length += item[i].duration0;
268  } else {
269  if (prev_length > 0) {
270  if (prev_level) {
271  this->temp_.push_back(this->to_microseconds_(prev_length) * multiplier);
272  } else {
273  this->temp_.push_back(-int32_t(this->to_microseconds_(prev_length)) * multiplier);
274  }
275  }
276  prev_level = bool(item[i].level0);
277  prev_length = item[i].duration0;
278  }
279 
280  if (item[i].duration1 == 0u) {
281  // EOF, sometimes garbage follows, break early
282  break;
283  } else if ((bool(item[i].level1) == prev_level) || (item[i].duration1 < filter_ticks)) {
284  prev_length += item[i].duration1;
285  } else {
286  if (prev_length > 0) {
287  if (prev_level) {
288  this->temp_.push_back(this->to_microseconds_(prev_length) * multiplier);
289  } else {
290  this->temp_.push_back(-int32_t(this->to_microseconds_(prev_length)) * multiplier);
291  }
292  }
293  prev_level = bool(item[i].level1);
294  prev_length = item[i].duration1;
295  }
296  }
297  if (prev_length > 0) {
298  if (prev_level) {
299  this->temp_.push_back(this->to_microseconds_(prev_length) * multiplier);
300  } else {
301  this->temp_.push_back(-int32_t(this->to_microseconds_(prev_length)) * multiplier);
302  }
303  }
304 }
305 
306 } // namespace remote_receiver
307 } // namespace esphome
308 
309 #endif
volatile uint32_t buffer_read
The position last read from.
virtual void setup()=0
virtual gpio::Flags get_flags() const =0
Retrieve GPIO pin flags.
virtual uint8_t get_pin() const =0
const char *const TAG
Definition: spi.cpp:8
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:324
virtual bool digital_read()=0
volatile uint32_t * buffer
Stores the time (in micros) that the leading/falling edge happened at.
volatile uint32_t buffer_write
The position last written to.
std::string size_t len
Definition: helpers.h:301
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void decode_rmt_(rmt_symbol_word_t *item, size_t item_count)
virtual bool is_inverted() const =0