ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
remote_receiver.cpp
Go to the documentation of this file.
1#include "remote_receiver.h"
2#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5
6#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
7
9
10ESPHOME_LOG_TAG(TAG, "remote_receiver");
11
12static void IRAM_ATTR HOT write_value(RemoteReceiverComponentStore *arg, uint32_t delta, bool level) {
13 // convert level to -1 or +1 and write the delta to the buffer
14 int32_t multiplier = ((int32_t) level << 1) - 1;
15 uint32_t buffer_write = arg->buffer_write;
16 arg->buffer[buffer_write++] = (int32_t) delta * multiplier;
17 if (buffer_write >= arg->buffer_entries) {
18 buffer_write = 0;
19 }
20
21 // detect overflow and reset the write pointer
22 if (buffer_write == arg->buffer_read) {
23 buffer_write = arg->buffer_start;
24 arg->overflow = true;
25 }
26
27 // detect idle and start a new sequence unless there is only idle in
28 // which case reset the write pointer instead
29 if (delta >= arg->idle_us) {
30 if (arg->buffer_write == arg->buffer_start) {
31 buffer_write = arg->buffer_start;
32 } else {
33 arg->buffer_start = buffer_write;
34 }
35 }
36 arg->buffer_write = buffer_write;
37}
38
39static void IRAM_ATTR HOT commit_value(RemoteReceiverComponentStore *arg, uint32_t micros, bool level) {
40 // commit value if the level is different from the last commit level
41 if (level != arg->commit_level) {
42 write_value(arg, micros - arg->commit_micros, level);
43 arg->commit_micros = micros;
44 arg->commit_level = level;
45 }
46}
47
49 // invert the level so it matches the level of the signal before the edge
50 const bool curr_level = !arg->pin.digital_read();
51 const uint32_t curr_micros = micros();
52 const bool prev_level = arg->prev_level;
53 const uint32_t prev_micros = arg->prev_micros;
54
55 // commit the previous value if the pulse is not filtered and the level is different
56 if (curr_micros - prev_micros >= arg->filter_us && prev_level != curr_level) {
57 commit_value(arg, prev_micros, prev_level);
58 }
59 arg->prev_micros = curr_micros;
60 arg->prev_level = curr_level;
61}
62
64 this->pin_->setup();
65 this->store_.idle_us = this->idle_us_;
66 this->store_.filter_us = this->filter_us_;
67 this->store_.pin = this->pin_->to_isr();
68 // rounded up so a size that is not a multiple of four never holds less than requested
69 this->store_.buffer_entries = (this->buffer_size_ + sizeof(int32_t) - 1) / sizeof(int32_t);
70 this->store_.buffer = new int32_t[this->store_.buffer_entries];
71 this->store_.prev_micros = micros();
73 this->store_.prev_level = this->pin_->digital_read();
76 this->high_freq_.start();
77}
78
80 ESP_LOGCONFIG(
81 TAG,
82 "Remote Receiver:\n"
83 " Buffer Size: %" PRIu32 " bytes (%" PRIu32 " pulses)\n"
84 " Tolerance: %" PRIu32 "%s\n"
85 " Filter out pulses shorter than: %" PRIu32 " us\n"
86 " Signal is done after %" PRIu32 " us of no changes",
87 this->buffer_size_, this->store_.buffer_entries, this->tolerance_,
88 (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? LOG_STR_LITERAL(" us") : LOG_STR_LITERAL("%"),
89 this->filter_us_, this->idle_us_);
90 LOG_PIN(" Pin: ", this->pin_);
91}
92
94 // check for overflow
95 auto &s = this->store_;
96 if (s.overflow) {
97 ESP_LOGW(TAG, "Buffer overflow");
98 s.overflow = false;
99 }
100
101 // if no data is available check for uncommitted data stuck in the buffer and commit
102 // the previous value if needed
103 uint32_t last_index = s.buffer_start;
104 if (last_index == s.buffer_read) {
106 if (s.buffer_read == s.buffer_start && s.buffer_write != s.buffer_start &&
107 micros() - s.prev_micros >= this->idle_us_) {
108 commit_value(&s, s.prev_micros, s.prev_level);
109 write_value(&s, s.idle_us, !s.commit_level);
110 last_index = s.buffer_start;
111 }
112 }
113 if (last_index == s.buffer_read) {
114 return;
115 }
116
117 // find the size of the packet and reserve the memory
118 uint32_t temp_read = s.buffer_read;
119 uint32_t reserve_size = 0;
120 while (temp_read != last_index && (uint32_t) std::abs(s.buffer[temp_read]) < this->idle_us_) {
121 reserve_size++;
122 temp_read++;
123 if (temp_read >= s.buffer_entries) {
124 temp_read = 0;
125 }
126 }
127 this->temp_.clear();
128 this->temp_.reserve(reserve_size + 1);
129
130 // read the buffer
131 for (uint32_t i = 0; i < reserve_size + 1; i++) {
132 this->temp_.push_back((int32_t) s.buffer[s.buffer_read++]);
133 if (s.buffer_read >= s.buffer_entries) {
134 s.buffer_read = 0;
135 }
136 }
137
138 // call the listeners and dumpers
140}
141
142} // namespace esphome::remote_receiver
143
144#endif
void start()
Start running the loop continuously.
Definition helpers.cpp:835
Helper class to disable interrupts.
Definition helpers.h:2164
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:29
ISRInternalGPIOPin to_isr() const
Definition gpio.cpp:91
@ INTERRUPT_ANY_EDGE
Definition gpio_pin.h:54
ESPHOME_LOG_TAG(TAG, "remote_receiver")
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
static void uint32_t
static void gpio_intr(RemoteReceiverComponentStore *arg)
uint32_t buffer_read
The position last read from.
volatile uint32_t buffer_start
The start position of the last sequence.
volatile int32_t * buffer
Stores pulse durations in microseconds as signed integers.
volatile uint32_t buffer_write
The position last written to.
SemaphoreHandle_t lock