mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
145 lines
5.0 KiB
C++
145 lines
5.0 KiB
C++
#include "remote_receiver.h"
|
|
#include "esphome/core/hal.h"
|
|
#include "esphome/core/helpers.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
|
|
|
|
namespace esphome::remote_receiver {
|
|
|
|
static const char *const TAG = "remote_receiver";
|
|
|
|
static void IRAM_ATTR HOT write_value(RemoteReceiverComponentStore *arg, uint32_t delta, bool level) {
|
|
// convert level to -1 or +1 and write the delta to the buffer
|
|
int32_t multiplier = ((int32_t) level << 1) - 1;
|
|
uint32_t buffer_write = arg->buffer_write;
|
|
arg->buffer[buffer_write++] = (int32_t) delta * multiplier;
|
|
if (buffer_write >= arg->buffer_entries) {
|
|
buffer_write = 0;
|
|
}
|
|
|
|
// detect overflow and reset the write pointer
|
|
if (buffer_write == arg->buffer_read) {
|
|
buffer_write = arg->buffer_start;
|
|
arg->overflow = true;
|
|
}
|
|
|
|
// detect idle and start a new sequence unless there is only idle in
|
|
// which case reset the write pointer instead
|
|
if (delta >= arg->idle_us) {
|
|
if (arg->buffer_write == arg->buffer_start) {
|
|
buffer_write = arg->buffer_start;
|
|
} else {
|
|
arg->buffer_start = buffer_write;
|
|
}
|
|
}
|
|
arg->buffer_write = buffer_write;
|
|
}
|
|
|
|
static void IRAM_ATTR HOT commit_value(RemoteReceiverComponentStore *arg, uint32_t micros, bool level) {
|
|
// commit value if the level is different from the last commit level
|
|
if (level != arg->commit_level) {
|
|
write_value(arg, micros - arg->commit_micros, level);
|
|
arg->commit_micros = micros;
|
|
arg->commit_level = level;
|
|
}
|
|
}
|
|
|
|
void IRAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverComponentStore *arg) {
|
|
// invert the level so it matches the level of the signal before the edge
|
|
const bool curr_level = !arg->pin.digital_read();
|
|
const uint32_t curr_micros = micros();
|
|
const bool prev_level = arg->prev_level;
|
|
const uint32_t prev_micros = arg->prev_micros;
|
|
|
|
// commit the previous value if the pulse is not filtered and the level is different
|
|
if (curr_micros - prev_micros >= arg->filter_us && prev_level != curr_level) {
|
|
commit_value(arg, prev_micros, prev_level);
|
|
}
|
|
arg->prev_micros = curr_micros;
|
|
arg->prev_level = curr_level;
|
|
}
|
|
|
|
void RemoteReceiverComponent::setup() {
|
|
this->pin_->setup();
|
|
this->store_.idle_us = this->idle_us_;
|
|
this->store_.filter_us = this->filter_us_;
|
|
this->store_.pin = this->pin_->to_isr();
|
|
// rounded up so a size that is not a multiple of four never holds less than requested
|
|
this->store_.buffer_entries = (this->buffer_size_ + sizeof(int32_t) - 1) / sizeof(int32_t);
|
|
this->store_.buffer = new int32_t[this->store_.buffer_entries];
|
|
this->store_.prev_micros = micros();
|
|
this->store_.commit_micros = this->store_.prev_micros;
|
|
this->store_.prev_level = this->pin_->digital_read();
|
|
this->store_.commit_level = this->store_.prev_level;
|
|
this->pin_->attach_interrupt(RemoteReceiverComponentStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE);
|
|
this->high_freq_.start();
|
|
}
|
|
|
|
void RemoteReceiverComponent::dump_config() {
|
|
ESP_LOGCONFIG(
|
|
TAG,
|
|
"Remote Receiver:\n"
|
|
" Buffer Size: %" PRIu32 " bytes (%" PRIu32 " pulses)\n"
|
|
" Tolerance: %" PRIu32 "%s\n"
|
|
" Filter out pulses shorter than: %" PRIu32 " us\n"
|
|
" Signal is done after %" PRIu32 " us of no changes",
|
|
this->buffer_size_, this->store_.buffer_entries, this->tolerance_,
|
|
(this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? LOG_STR_LITERAL(" us") : LOG_STR_LITERAL("%"),
|
|
this->filter_us_, this->idle_us_);
|
|
LOG_PIN(" Pin: ", this->pin_);
|
|
}
|
|
|
|
void RemoteReceiverComponent::loop() {
|
|
// check for overflow
|
|
auto &s = this->store_;
|
|
if (s.overflow) {
|
|
ESP_LOGW(TAG, "Buffer overflow");
|
|
s.overflow = false;
|
|
}
|
|
|
|
// if no data is available check for uncommitted data stuck in the buffer and commit
|
|
// the previous value if needed
|
|
uint32_t last_index = s.buffer_start;
|
|
if (last_index == s.buffer_read) {
|
|
InterruptLock lock;
|
|
if (s.buffer_read == s.buffer_start && s.buffer_write != s.buffer_start &&
|
|
micros() - s.prev_micros >= this->idle_us_) {
|
|
commit_value(&s, s.prev_micros, s.prev_level);
|
|
write_value(&s, s.idle_us, !s.commit_level);
|
|
last_index = s.buffer_start;
|
|
}
|
|
}
|
|
if (last_index == s.buffer_read) {
|
|
return;
|
|
}
|
|
|
|
// find the size of the packet and reserve the memory
|
|
uint32_t temp_read = s.buffer_read;
|
|
uint32_t reserve_size = 0;
|
|
while (temp_read != last_index && (uint32_t) std::abs(s.buffer[temp_read]) < this->idle_us_) {
|
|
reserve_size++;
|
|
temp_read++;
|
|
if (temp_read >= s.buffer_entries) {
|
|
temp_read = 0;
|
|
}
|
|
}
|
|
this->temp_.clear();
|
|
this->temp_.reserve(reserve_size + 1);
|
|
|
|
// read the buffer
|
|
for (uint32_t i = 0; i < reserve_size + 1; i++) {
|
|
this->temp_.push_back((int32_t) s.buffer[s.buffer_read++]);
|
|
if (s.buffer_read >= s.buffer_entries) {
|
|
s.buffer_read = 0;
|
|
}
|
|
}
|
|
|
|
// call the listeners and dumpers
|
|
this->call_listeners_dumpers_();
|
|
}
|
|
|
|
} // namespace esphome::remote_receiver
|
|
|
|
#endif
|