[remote_receiver] Treat buffer size as bytes on the pulse ring targets (#19101)

This commit is contained in:
J. Nick Koston
2026-09-17 08:15:13 -05:00
committed by GitHub
parent 942322738a
commit a7559547b2
9 changed files with 94 additions and 24 deletions
@@ -112,20 +112,21 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema),
cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers,
cv.Optional(CONF_TOLERANCE, default="25%"): validate_tolerance,
# pulse ring targets hold one 4 byte entry per pulse; 4000b keeps their 1000 pulses
cv.SplitDefault(
CONF_BUFFER_SIZE,
esp32=cv.UNDEFINED,
# the pulse ring needs a size; only RMT targets size themselves in setup()
**{
f"esp32_{variant.removeprefix('ESP32').lower()}": "1000b"
f"esp32_{variant.removeprefix('ESP32').lower()}": "4000b"
for variant in esp32_rmt.VARIANTS_NO_RMT
},
esp8266="1000b",
bk72xx="1000b",
ln882x="1000b",
rtl87xx="1000b",
rp2="1000b",
): cv.All(cv.validate_bytes, cv.int_range(min=64)),
esp8266="4000b",
bk72xx="4000b",
ln882x="4000b",
rtl87xx="4000b",
rp2="4000b",
): cv.All(cv.validate_bytes, cv.int_range(min=64, max=65535)),
cv.Optional(CONF_FILTER, default="50us"): cv.All(
cv.positive_time_period_microseconds,
cv.Range(max=TimePeriod(microseconds=4294967295)),
@@ -14,7 +14,7 @@ static void IRAM_ATTR HOT write_value(RemoteReceiverComponentStore *arg, uint32_
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_size) {
if (buffer_write >= arg->buffer_entries) {
buffer_write = 0;
}
@@ -65,8 +65,9 @@ void RemoteReceiverComponent::setup() {
this->store_.idle_us = this->idle_us_;
this->store_.filter_us = this->filter_us_;
this->store_.pin = this->pin_->to_isr();
this->store_.buffer = new int32_t[this->buffer_size_];
this->store_.buffer_size = this->buffer_size_;
// 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();
@@ -79,11 +80,11 @@ void RemoteReceiverComponent::dump_config() {
ESP_LOGCONFIG(
TAG,
"Remote Receiver:\n"
" Buffer Size: %" PRIu32 "\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->tolerance_,
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_);
@@ -119,7 +120,7 @@ void RemoteReceiverComponent::loop() {
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_size) {
if (temp_read >= s.buffer_entries) {
temp_read = 0;
}
}
@@ -129,7 +130,7 @@ void RemoteReceiverComponent::loop() {
// 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_size) {
if (s.buffer_read >= s.buffer_entries) {
s.buffer_read = 0;
}
}
@@ -30,7 +30,7 @@ struct RemoteReceiverComponentStore {
uint32_t buffer_read{0};
volatile uint32_t commit_micros{0};
volatile uint32_t prev_micros{0};
uint32_t buffer_size{1000};
uint32_t buffer_entries{0};
uint32_t filter_us{10};
uint32_t idle_us{10000};
ISRInternalGPIOPin pin;
@@ -0,0 +1,9 @@
esphome:
name: test
bk72xx:
board: generic-bk7252
remote_receiver:
- id: rcvr
pin: P6
@@ -0,0 +1,12 @@
esphome:
name: test
esp32:
board: esp32-c61-devkitc1
variant: esp32c61
framework:
type: esp-idf
remote_receiver:
- id: rcvr
pin: GPIO4
@@ -0,0 +1,9 @@
esphome:
name: test
ln882x:
board: generic-ln882h
remote_receiver:
- id: rcvr
pin: PA4
@@ -0,0 +1,9 @@
esphome:
name: test
rp2:
board: rpipicow
remote_receiver:
- id: rcvr
pin: GPIO4
@@ -0,0 +1,9 @@
esphome:
name: test
rtl87xx:
board: generic-rtl8710bn-2mb-788k
remote_receiver:
- id: rcvr
pin: PA12
@@ -1,8 +1,16 @@
"""buffer_size reaches the receiver when set, and always on the pulse ring targets."""
"""buffer_size is bytes on the pulse ring targets and only reaches RMT targets when set."""
from collections.abc import Callable
from pathlib import Path
import pytest
from esphome.components import remote_receiver
from esphome.components.esp8266 import gpio as esp8266_gpio # noqa: F401 registers the pin schema
from esphome.config_validation import Invalid
from esphome.const import PlatformFramework
from tests.component_tests.types import SetCoreConfigCallable
def test_explicit_buffer_size_is_passed_through(
generate_main: Callable[[str | Path], str],
@@ -12,17 +20,29 @@ def test_explicit_buffer_size_is_passed_through(
assert "rcvr->set_buffer_size(2000);" in main_cpp
def test_pulse_ring_target_keeps_a_default(
@pytest.mark.parametrize(
"target", ["esp8266", "rp2", "bk72xx", "rtl87xx", "ln882x", "esp32_c2", "esp32_c61"]
)
def test_pulse_ring_default_holds_1000_pulses(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
target: str,
) -> None:
main_cpp = generate_main(component_config_path("receiver_esp8266.yaml"))
assert "rcvr->set_buffer_size(1000);" in main_cpp
main_cpp = generate_main(component_config_path(f"receiver_{target}.yaml"))
assert "rcvr->set_buffer_size(4000);" in main_cpp
def test_esp32_variant_without_rmt_keeps_a_default(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
@pytest.mark.parametrize(
("value", "expected"),
[("32b", None), ("64b", 64), ("65b", 65), ("65535b", 65535), ("65536b", None)],
)
def test_buffer_size_range(
set_core_config: SetCoreConfigCallable, value: str, expected: int | None
) -> None:
main_cpp = generate_main(component_config_path("receiver_esp32_c2.yaml"))
assert "rcvr->set_buffer_size(1000);" in main_cpp
set_core_config(PlatformFramework.ESP8266_ARDUINO)
config = {"pin": "GPIO4", "buffer_size": value}
if expected is None:
with pytest.raises(Invalid):
remote_receiver.CONFIG_SCHEMA(config)
else:
assert remote_receiver.CONFIG_SCHEMA(config)["buffer_size"] == expected