Remote receiver improvements (#4642)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Mat931 2024-05-02 01:21:57 +00:00 committed by GitHub
parent 539c369eea
commit 1b9a30e921
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 125 additions and 29 deletions

View file

@ -108,18 +108,18 @@ void RemoteReceiverBase::register_dumper(RemoteReceiverDumperBase *dumper) {
void RemoteReceiverBase::call_listeners_() { void RemoteReceiverBase::call_listeners_() {
for (auto *listener : this->listeners_) for (auto *listener : this->listeners_)
listener->on_receive(RemoteReceiveData(this->temp_, this->tolerance_)); listener->on_receive(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_));
} }
void RemoteReceiverBase::call_dumpers_() { void RemoteReceiverBase::call_dumpers_() {
bool success = false; bool success = false;
for (auto *dumper : this->dumpers_) { for (auto *dumper : this->dumpers_) {
if (dumper->dump(RemoteReceiveData(this->temp_, this->tolerance_))) if (dumper->dump(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_)))
success = true; success = true;
} }
if (!success) { if (!success) {
for (auto *dumper : this->secondary_dumpers_) for (auto *dumper : this->secondary_dumpers_)
dumper->dump(RemoteReceiveData(this->temp_, this->tolerance_)); dumper->dump(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_));
} }
} }

View file

@ -15,6 +15,11 @@
namespace esphome { namespace esphome {
namespace remote_base { namespace remote_base {
enum ToleranceMode : uint8_t {
TOLERANCE_MODE_PERCENTAGE = 0,
TOLERANCE_MODE_TIME = 1,
};
using RawTimings = std::vector<int32_t>; using RawTimings = std::vector<int32_t>;
class RemoteTransmitData { class RemoteTransmitData {
@ -42,8 +47,8 @@ class RemoteTransmitData {
class RemoteReceiveData { class RemoteReceiveData {
public: public:
explicit RemoteReceiveData(const RawTimings &data, uint8_t tolerance) explicit RemoteReceiveData(const RawTimings &data, uint32_t tolerance, ToleranceMode tolerance_mode)
: data_(data), index_(0), tolerance_(tolerance) {} : data_(data), index_(0), tolerance_(tolerance), tolerance_mode_(tolerance_mode) {}
const RawTimings &get_raw_data() const { return this->data_; } const RawTimings &get_raw_data() const { return this->data_; }
uint32_t get_index() const { return index_; } uint32_t get_index() const { return index_; }
@ -65,13 +70,35 @@ class RemoteReceiveData {
void advance(uint32_t amount = 1) { this->index_ += amount; } void advance(uint32_t amount = 1) { this->index_ += amount; }
void reset() { this->index_ = 0; } void reset() { this->index_ = 0; }
void set_tolerance(uint32_t tolerance, ToleranceMode tolerance_mode) {
this->tolerance_ = tolerance;
this->tolerance_mode_ = tolerance_mode;
}
uint32_t get_tolerance() { return tolerance_; }
ToleranceMode get_tolerance_mode() { return this->tolerance_mode_; }
protected: protected:
int32_t lower_bound_(uint32_t length) const { return int32_t(100 - this->tolerance_) * length / 100U; } int32_t lower_bound_(uint32_t length) const {
int32_t upper_bound_(uint32_t length) const { return int32_t(100 + this->tolerance_) * length / 100U; } if (this->tolerance_mode_ == TOLERANCE_MODE_TIME) {
return int32_t(length - this->tolerance_);
} else if (this->tolerance_mode_ == TOLERANCE_MODE_PERCENTAGE) {
return int32_t(100 - this->tolerance_) * length / 100U;
}
return 0;
}
int32_t upper_bound_(uint32_t length) const {
if (this->tolerance_mode_ == TOLERANCE_MODE_TIME) {
return int32_t(length + this->tolerance_);
} else if (this->tolerance_mode_ == TOLERANCE_MODE_PERCENTAGE) {
return int32_t(100 + this->tolerance_) * length / 100U;
}
return 0;
}
const RawTimings &data_; const RawTimings &data_;
uint32_t index_; uint32_t index_;
uint8_t tolerance_; uint32_t tolerance_;
ToleranceMode tolerance_mode_;
}; };
class RemoteComponentBase { class RemoteComponentBase {
@ -162,7 +189,10 @@ class RemoteReceiverBase : public RemoteComponentBase {
RemoteReceiverBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {} RemoteReceiverBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {}
void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); } void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); }
void register_dumper(RemoteReceiverDumperBase *dumper); void register_dumper(RemoteReceiverDumperBase *dumper);
void set_tolerance(uint8_t tolerance) { tolerance_ = tolerance; } void set_tolerance(uint32_t tolerance, ToleranceMode tolerance_mode) {
this->tolerance_ = tolerance;
this->tolerance_mode_ = tolerance_mode;
}
protected: protected:
void call_listeners_(); void call_listeners_();
@ -176,7 +206,8 @@ class RemoteReceiverBase : public RemoteComponentBase {
std::vector<RemoteReceiverDumperBase *> dumpers_; std::vector<RemoteReceiverDumperBase *> dumpers_;
std::vector<RemoteReceiverDumperBase *> secondary_dumpers_; std::vector<RemoteReceiverDumperBase *> secondary_dumpers_;
RawTimings temp_; RawTimings temp_;
uint8_t tolerance_; uint32_t tolerance_{25};
ToleranceMode tolerance_mode_{TOLERANCE_MODE_PERCENTAGE};
}; };
class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensorInitiallyOff, class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensorInitiallyOff,

View file

@ -10,17 +10,69 @@ from esphome.const import (
CONF_IDLE, CONF_IDLE,
CONF_PIN, CONF_PIN,
CONF_TOLERANCE, CONF_TOLERANCE,
CONF_TYPE,
CONF_MEMORY_BLOCKS, CONF_MEMORY_BLOCKS,
CONF_RMT_CHANNEL, CONF_RMT_CHANNEL,
CONF_VALUE,
) )
from esphome.core import CORE, TimePeriod from esphome.core import CORE, TimePeriod
CONF_CLOCK_DIVIDER = "clock_divider"
AUTO_LOAD = ["remote_base"] AUTO_LOAD = ["remote_base"]
remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver") remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver")
remote_base_ns = cg.esphome_ns.namespace("remote_base")
ToleranceMode = remote_base_ns.enum("ToleranceMode")
TYPE_PERCENTAGE = "percentage"
TYPE_TIME = "time"
TOLERANCE_MODE = {
TYPE_PERCENTAGE: ToleranceMode.TOLERANCE_MODE_PERCENTAGE,
TYPE_TIME: ToleranceMode.TOLERANCE_MODE_TIME,
}
TOLERANCE_SCHEMA = cv.typed_schema(
{
TYPE_PERCENTAGE: cv.Schema(
{cv.Required(CONF_VALUE): cv.All(cv.percentage_int, cv.uint32_t)}
),
TYPE_TIME: cv.Schema(
{
cv.Required(CONF_VALUE): cv.All(
cv.positive_time_period_microseconds,
cv.Range(max=TimePeriod(microseconds=4294967295)),
)
}
),
},
lower=True,
enum=TOLERANCE_MODE,
)
RemoteReceiverComponent = remote_receiver_ns.class_( RemoteReceiverComponent = remote_receiver_ns.class_(
"RemoteReceiverComponent", remote_base.RemoteReceiverBase, cg.Component "RemoteReceiverComponent", remote_base.RemoteReceiverBase, cg.Component
) )
def validate_tolerance(value):
if isinstance(value, dict):
return TOLERANCE_SCHEMA(value)
if "%" in str(value):
type_ = TYPE_PERCENTAGE
else:
type_ = TYPE_TIME
return TOLERANCE_SCHEMA(
{
CONF_VALUE: value,
CONF_TYPE: type_,
}
)
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = remote_base.validate_triggers( CONFIG_SCHEMA = remote_base.validate_triggers(
cv.Schema( cv.Schema(
@ -28,9 +80,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
cv.GenerateID(): cv.declare_id(RemoteReceiverComponent), cv.GenerateID(): cv.declare_id(RemoteReceiverComponent),
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema),
cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers, cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers,
cv.Optional(CONF_TOLERANCE, default=25): cv.All( cv.Optional(CONF_TOLERANCE, default="25%"): validate_tolerance,
cv.percentage_int, cv.Range(min=0)
),
cv.SplitDefault( cv.SplitDefault(
CONF_BUFFER_SIZE, CONF_BUFFER_SIZE,
esp32="10000b", esp32="10000b",
@ -40,11 +90,15 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
): cv.validate_bytes, ): cv.validate_bytes,
cv.Optional(CONF_FILTER, default="50us"): cv.All( cv.Optional(CONF_FILTER, default="50us"): cv.All(
cv.positive_time_period_microseconds, cv.positive_time_period_microseconds,
cv.Range(max=TimePeriod(microseconds=255)), cv.Range(max=TimePeriod(microseconds=4294967295)),
),
cv.SplitDefault(CONF_CLOCK_DIVIDER, esp32=80): cv.All(
cv.only_on_esp32, cv.Range(min=1, max=255)
),
cv.Optional(CONF_IDLE, default="10ms"): cv.All(
cv.positive_time_period_microseconds,
cv.Range(max=TimePeriod(microseconds=4294967295)),
), ),
cv.Optional(
CONF_IDLE, default="10ms"
): cv.positive_time_period_microseconds,
cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8), cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8),
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=False), cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=False),
} }
@ -61,6 +115,7 @@ async def to_code(config):
) )
else: else:
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS])
cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER]))
else: else:
var = cg.new_Pvariable(config[CONF_ID], pin) var = cg.new_Pvariable(config[CONF_ID], pin)
@ -73,7 +128,11 @@ async def to_code(config):
cg.add(var.register_listener(trigger)) cg.add(var.register_listener(trigger))
await cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_tolerance(config[CONF_TOLERANCE])) cg.add(
var.set_tolerance(
config[CONF_TOLERANCE][CONF_VALUE], config[CONF_TOLERANCE][CONF_TYPE]
)
)
cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE])) cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE]))
cg.add(var.set_filter_us(config[CONF_FILTER])) cg.add(var.set_filter_us(config[CONF_FILTER]))
cg.add(var.set_idle_us(config[CONF_IDLE])) cg.add(var.set_idle_us(config[CONF_IDLE]))

View file

@ -22,7 +22,7 @@ struct RemoteReceiverComponentStore {
uint32_t buffer_read_at{0}; uint32_t buffer_read_at{0};
bool overflow{false}; bool overflow{false};
uint32_t buffer_size{1000}; uint32_t buffer_size{1000};
uint8_t filter_us{10}; uint32_t filter_us{10};
ISRInternalGPIOPin pin; ISRInternalGPIOPin pin;
}; };
#endif #endif
@ -50,7 +50,7 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }
void set_buffer_size(uint32_t buffer_size) { this->buffer_size_ = buffer_size; } void set_buffer_size(uint32_t buffer_size) { this->buffer_size_ = buffer_size; }
void set_filter_us(uint8_t filter_us) { this->filter_us_ = filter_us; } void set_filter_us(uint32_t filter_us) { this->filter_us_ = filter_us; }
void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; } void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; }
protected: protected:
@ -66,7 +66,7 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
#endif #endif
uint32_t buffer_size_{}; uint32_t buffer_size_{};
uint8_t filter_us_{10}; uint32_t filter_us_{10};
uint32_t idle_us_{10000}; uint32_t idle_us_{10000};
}; };

View file

@ -20,9 +20,11 @@ void RemoteReceiverComponent::setup() {
rmt.rx_config.filter_en = false; rmt.rx_config.filter_en = false;
} else { } else {
rmt.rx_config.filter_en = true; rmt.rx_config.filter_en = true;
rmt.rx_config.filter_ticks_thresh = this->from_microseconds_(this->filter_us_); rmt.rx_config.filter_ticks_thresh = static_cast<uint8_t>(
std::min(this->from_microseconds_(this->filter_us_) * this->clock_divider_, (uint32_t) 255));
} }
rmt.rx_config.idle_threshold = this->from_microseconds_(this->idle_us_); rmt.rx_config.idle_threshold =
static_cast<uint16_t>(std::min(this->from_microseconds_(this->idle_us_), (uint32_t) 65535));
esp_err_t error = rmt_config(&rmt); esp_err_t error = rmt_config(&rmt);
if (error != ESP_OK) { if (error != ESP_OK) {
@ -60,8 +62,9 @@ void RemoteReceiverComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_); ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_);
ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_); ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_);
ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_); ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_);
ESP_LOGCONFIG(TAG, " Tolerance: %u%%", this->tolerance_); ESP_LOGCONFIG(TAG, " Tolerance: %" PRIu32 "%s", this->tolerance_,
ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%");
ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %" PRIu32 " us", this->filter_us_);
ESP_LOGCONFIG(TAG, " Signal is done after %" PRIu32 " us of no changes", this->idle_us_); ESP_LOGCONFIG(TAG, " Signal is done after %" PRIu32 " us of no changes", this->idle_us_);
if (this->is_failed()) { if (this->is_failed()) {
ESP_LOGE(TAG, "Configuring RMT driver failed: %s", esp_err_to_name(this->error_code_)); ESP_LOGE(TAG, "Configuring RMT driver failed: %s", esp_err_to_name(this->error_code_));
@ -88,6 +91,7 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
this->temp_.clear(); this->temp_.clear();
int32_t multiplier = this->pin_->is_inverted() ? -1 : 1; int32_t multiplier = this->pin_->is_inverted() ? -1 : 1;
size_t item_count = len / sizeof(rmt_item32_t); size_t item_count = len / sizeof(rmt_item32_t);
uint32_t filter_ticks = this->from_microseconds_(this->filter_us_);
ESP_LOGVV(TAG, "START:"); ESP_LOGVV(TAG, "START:");
for (size_t i = 0; i < item_count; i++) { for (size_t i = 0; i < item_count; i++) {
@ -112,7 +116,7 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
for (size_t i = 0; i < item_count; i++) { for (size_t i = 0; i < item_count; i++) {
if (item[i].duration0 == 0u) { if (item[i].duration0 == 0u) {
// Do nothing // Do nothing
} else if (bool(item[i].level0) == prev_level) { } else if ((bool(item[i].level0) == prev_level) || (item[i].duration0 < filter_ticks)) {
prev_length += item[i].duration0; prev_length += item[i].duration0;
} else { } else {
if (prev_length > 0) { if (prev_length > 0) {
@ -128,7 +132,7 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
if (item[i].duration1 == 0u) { if (item[i].duration1 == 0u) {
// Do nothing // Do nothing
} else if (bool(item[i].level1) == prev_level) { } else if ((bool(item[i].level1) == prev_level) || (item[i].duration1 < filter_ticks)) {
prev_length += item[i].duration1; prev_length += item[i].duration1;
} else { } else {
if (prev_length > 0) { if (prev_length > 0) {

View file

@ -64,7 +64,8 @@ void RemoteReceiverComponent::dump_config() {
"invert the signal using 'inverted: True' in the pin schema!"); "invert the signal using 'inverted: True' in the pin schema!");
} }
ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_); ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_);
ESP_LOGCONFIG(TAG, " Tolerance: %u%%", this->tolerance_); ESP_LOGCONFIG(TAG, " Tolerance: %u%s", this->tolerance_,
(this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%");
ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_);
ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_); ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_);
} }

View file

@ -64,7 +64,8 @@ void RemoteReceiverComponent::dump_config() {
"invert the signal using 'inverted: True' in the pin schema!"); "invert the signal using 'inverted: True' in the pin schema!");
} }
ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_); ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_);
ESP_LOGCONFIG(TAG, " Tolerance: %u%%", this->tolerance_); ESP_LOGCONFIG(TAG, " Tolerance: %u%s", this->tolerance_,
(this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%");
ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_);
ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_); ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_);
} }