1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-29 22:24:26 +00:00

[esp32_rmt] Updates for IDF 5+ (#7770)

Co-authored-by: Jonathan Swoboda <jonathan.swoboda>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
Jonathan Swoboda
2024-12-18 21:31:22 -05:00
committed by GitHub
parent 61499dbdd8
commit 265b6ec445
33 changed files with 817 additions and 379 deletions

View File

@@ -1,10 +1,11 @@
from esphome import pins
import esphome.codegen as cg
from esphome.components import esp32_rmt, remote_base
from esphome.components import esp32, esp32_rmt, remote_base
import esphome.config_validation as cv
from esphome.const import (
CONF_BUFFER_SIZE,
CONF_CLOCK_DIVIDER,
CONF_CLOCK_RESOLUTION,
CONF_DUMP,
CONF_FILTER,
CONF_ID,
@@ -12,12 +13,17 @@ from esphome.const import (
CONF_MEMORY_BLOCKS,
CONF_PIN,
CONF_RMT_CHANNEL,
CONF_RMT_SYMBOLS,
CONF_TOLERANCE,
CONF_TYPE,
CONF_USE_DMA,
CONF_VALUE,
)
from esphome.core import CORE, TimePeriod
CONF_FILTER_SYMBOLS = "filter_symbols"
CONF_RECEIVE_SYMBOLS = "receive_symbols"
AUTO_LOAD = ["remote_base"]
remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver")
remote_base_ns = cg.esphome_ns.namespace("remote_base")
@@ -97,15 +103,43 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
cv.positive_time_period_microseconds,
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.SplitDefault(CONF_CLOCK_DIVIDER, esp32_arduino=80): cv.All(
cv.only_on_esp32,
cv.only_with_arduino,
cv.int_range(min=1, max=255),
),
cv.Optional(CONF_CLOCK_RESOLUTION): cv.All(
cv.only_on_esp32,
cv.only_with_esp_idf,
esp32_rmt.validate_clock_resolution(),
),
cv.Optional(CONF_IDLE, default="10ms"): cv.All(
cv.positive_time_period_microseconds,
cv.Range(max=TimePeriod(microseconds=4294967295)),
),
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.SplitDefault(CONF_MEMORY_BLOCKS, esp32_arduino=3): cv.All(
cv.only_with_arduino, cv.int_range(min=1, max=8)
),
cv.Optional(CONF_RMT_CHANNEL): cv.All(
cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=False)
),
cv.SplitDefault(
CONF_RMT_SYMBOLS,
esp32_idf=192,
esp32_s2_idf=192,
esp32_s3_idf=192,
esp32_c3_idf=96,
esp32_c6_idf=96,
esp32_h2_idf=96,
): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)),
cv.Optional(CONF_FILTER_SYMBOLS): cv.All(
cv.only_with_esp_idf, cv.int_range(min=0)
),
cv.SplitDefault(
CONF_RECEIVE_SYMBOLS,
esp32_idf=192,
): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)),
cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean),
}
).extend(cv.COMPONENT_SCHEMA)
)
@@ -114,13 +148,27 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN])
if CORE.is_esp32:
if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None:
var = cg.new_Pvariable(
config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS]
)
if esp32_rmt.use_new_rmt_driver():
var = cg.new_Pvariable(config[CONF_ID], pin)
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
cg.add(var.set_receive_symbols(config[CONF_RECEIVE_SYMBOLS]))
if CONF_USE_DMA in config:
cg.add(var.set_with_dma(config[CONF_USE_DMA]))
if CONF_CLOCK_RESOLUTION in config:
cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION]))
if CONF_FILTER_SYMBOLS in config:
cg.add(var.set_filter_symbols(config[CONF_FILTER_SYMBOLS]))
if CORE.using_esp_idf:
esp32.add_idf_sdkconfig_option("CONFIG_RMT_RECV_FUNC_IN_IRAM", True)
esp32.add_idf_sdkconfig_option("CONFIG_RMT_ISR_IRAM_SAFE", True)
else:
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS])
cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER]))
if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None:
var = cg.new_Pvariable(
config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS]
)
else:
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS])
cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER]))
else:
var = cg.new_Pvariable(config[CONF_ID], pin)

View File

@@ -5,6 +5,10 @@
#include <cinttypes>
#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5
#include <driver/rmt_rx.h>
#endif
namespace esphome {
namespace remote_receiver {
@@ -25,6 +29,21 @@ struct RemoteReceiverComponentStore {
uint32_t filter_us{10};
ISRInternalGPIOPin pin;
};
#elif defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5
struct RemoteReceiverComponentStore {
/// Stores RMT symbols and rx done event data
volatile uint8_t *buffer{nullptr};
/// The position last written to
volatile uint32_t buffer_write{0};
/// The position last read from
volatile uint32_t buffer_read{0};
bool overflow{false};
uint32_t buffer_size{1000};
uint32_t receive_size{0};
uint32_t filter_symbols{0};
esp_err_t error{ESP_OK};
rmt_receive_config_t config;
};
#endif
class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
@@ -33,9 +52,10 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
,
public remote_base::RemoteRMTChannel
#endif
{
public:
#ifdef USE_ESP32
#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5
RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1)
: RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {}
@@ -49,19 +69,32 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
void loop() override;
float get_setup_priority() const override { return setup_priority::DATA; }
#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5
void set_filter_symbols(uint32_t filter_symbols) { this->filter_symbols_ = filter_symbols; }
void set_receive_symbols(uint32_t receive_symbols) { this->receive_symbols_ = receive_symbols; }
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
#endif
void set_buffer_size(uint32_t buffer_size) { this->buffer_size_ = buffer_size; }
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; }
protected:
#ifdef USE_ESP32
void decode_rmt_(rmt_item32_t *item, size_t len);
#if ESP_IDF_VERSION_MAJOR >= 5
void decode_rmt_(rmt_symbol_word_t *item, size_t item_count);
rmt_channel_handle_t channel_{NULL};
uint32_t filter_symbols_{0};
uint32_t receive_symbols_{0};
bool with_dma_{false};
#else
void decode_rmt_(rmt_item32_t *item, size_t item_count);
RingbufHandle_t ringbuf_;
#endif
esp_err_t error_code_{ESP_OK};
std::string error_string_{""};
#endif
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || (defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5)
RemoteReceiverComponentStore store_;
HighFrequencyLoopRequester high_freq_;
#endif

View File

@@ -2,15 +2,104 @@
#include "esphome/core/log.h"
#ifdef USE_ESP32
#include <driver/rmt.h>
namespace esphome {
namespace remote_receiver {
static const char *const TAG = "remote_receiver.esp32";
#ifdef USE_ESP32_VARIANT_ESP32H2
static const uint32_t RMT_CLK_FREQ = 32000000;
#else
static const uint32_t RMT_CLK_FREQ = 80000000;
#endif
#if ESP_IDF_VERSION_MAJOR >= 5
static bool IRAM_ATTR HOT rmt_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *event, void *arg) {
RemoteReceiverComponentStore *store = (RemoteReceiverComponentStore *) arg;
rmt_rx_done_event_data_t *event_buffer = (rmt_rx_done_event_data_t *) (store->buffer + store->buffer_write);
uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
uint32_t next_write = store->buffer_write + event_size + event->num_symbols * sizeof(rmt_symbol_word_t);
if (next_write + event_size + store->receive_size > store->buffer_size) {
next_write = 0;
}
if (store->buffer_read - next_write < event_size + store->receive_size) {
next_write = store->buffer_write;
store->overflow = true;
}
if (event->num_symbols <= store->filter_symbols) {
next_write = store->buffer_write;
}
store->error =
rmt_receive(channel, (uint8_t *) store->buffer + next_write + event_size, store->receive_size, &store->config);
event_buffer->num_symbols = event->num_symbols;
event_buffer->received_symbols = event->received_symbols;
store->buffer_write = next_write;
return false;
}
#endif
void RemoteReceiverComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Remote Receiver...");
#if ESP_IDF_VERSION_MAJOR >= 5
rmt_rx_channel_config_t channel;
memset(&channel, 0, sizeof(channel));
channel.clk_src = RMT_CLK_SRC_DEFAULT;
channel.resolution_hz = this->clock_resolution_;
channel.mem_block_symbols = rmt_symbols_;
channel.gpio_num = gpio_num_t(this->pin_->get_pin());
channel.intr_priority = 0;
channel.flags.invert_in = 0;
channel.flags.with_dma = this->with_dma_;
channel.flags.io_loop_back = 0;
esp_err_t error = rmt_new_rx_channel(&channel, &this->channel_);
if (error != ESP_OK) {
this->error_code_ = error;
if (error == ESP_ERR_NOT_FOUND) {
this->error_string_ = "out of RMT symbol memory";
} else {
this->error_string_ = "in rmt_new_rx_channel";
}
this->mark_failed();
return;
}
error = rmt_enable(this->channel_);
if (error != ESP_OK) {
this->error_code_ = error;
this->error_string_ = "in rmt_enable";
this->mark_failed();
return;
}
rmt_rx_event_callbacks_t callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.on_recv_done = rmt_callback;
error = rmt_rx_register_event_callbacks(this->channel_, &callbacks, &this->store_);
if (error != ESP_OK) {
this->error_code_ = error;
this->error_string_ = "in rmt_rx_register_event_callbacks";
this->mark_failed();
return;
}
uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
uint32_t max_filter_ns = 255u * 1000 / (RMT_CLK_FREQ / 1000000);
uint32_t max_idle_ns = 65535u * 1000;
memset(&this->store_.config, 0, sizeof(this->store_.config));
this->store_.config.signal_range_min_ns = std::min(this->filter_us_ * 1000, max_filter_ns);
this->store_.config.signal_range_max_ns = std::min(this->idle_us_ * 1000, max_idle_ns);
this->store_.filter_symbols = this->filter_symbols_;
this->store_.receive_size = this->receive_symbols_ * sizeof(rmt_symbol_word_t);
this->store_.buffer_size = std::max((event_size + this->store_.receive_size) * 2, this->buffer_size_);
this->store_.buffer = new uint8_t[this->buffer_size_];
error = rmt_receive(this->channel_, (uint8_t *) this->store_.buffer + event_size, this->store_.receive_size,
&this->store_.config);
if (error != ESP_OK) {
this->error_code_ = error;
this->error_string_ = "in rmt_receive";
this->mark_failed();
return;
}
#else
this->pin_->setup();
rmt_config_t rmt{};
this->config_rmt(rmt);
@@ -59,7 +148,9 @@ void RemoteReceiverComponent::setup() {
this->mark_failed();
return;
}
#endif
}
void RemoteReceiverComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Remote Receiver:");
LOG_PIN(" Pin: ", this->pin_);
@@ -67,9 +158,16 @@ void RemoteReceiverComponent::dump_config() {
ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to "
"invert the signal using 'inverted: True' in the pin schema!");
}
#if ESP_IDF_VERSION_MAJOR >= 5
ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_);
ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_);
ESP_LOGCONFIG(TAG, " Filter symbols: %" PRIu32, this->filter_symbols_);
ESP_LOGCONFIG(TAG, " Receive symbols: %" PRIu32, this->receive_symbols_);
#else
ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_);
ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_);
ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_);
#endif
ESP_LOGCONFIG(TAG, " Tolerance: %" PRIu32 "%s", this->tolerance_,
(this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%");
ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %" PRIu32 " us", this->filter_us_);
@@ -81,10 +179,38 @@ void RemoteReceiverComponent::dump_config() {
}
void RemoteReceiverComponent::loop() {
#if ESP_IDF_VERSION_MAJOR >= 5
if (this->store_.error != ESP_OK) {
ESP_LOGE(TAG, "Receive error");
this->error_code_ = this->store_.error;
this->error_string_ = "in rmt_callback";
this->mark_failed();
}
if (this->store_.overflow) {
ESP_LOGW(TAG, "Buffer overflow");
this->store_.overflow = false;
}
uint32_t buffer_write = this->store_.buffer_write;
while (this->store_.buffer_read != buffer_write) {
rmt_rx_done_event_data_t *event = (rmt_rx_done_event_data_t *) (this->store_.buffer + this->store_.buffer_read);
uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
uint32_t next_read = this->store_.buffer_read + event_size + event->num_symbols * sizeof(rmt_symbol_word_t);
if (next_read + event_size + this->store_.receive_size > this->store_.buffer_size) {
next_read = 0;
}
this->decode_rmt_(event->received_symbols, event->num_symbols);
this->store_.buffer_read = next_read;
if (!this->temp_.empty()) {
this->temp_.push_back(-this->idle_us_);
this->call_listeners_dumpers_();
}
}
#else
size_t len = 0;
auto *item = (rmt_item32_t *) xRingbufferReceive(this->ringbuf_, &len, 0);
if (item != nullptr) {
this->decode_rmt_(item, len);
this->decode_rmt_(item, len / sizeof(rmt_item32_t));
vRingbufferReturnItem(this->ringbuf_, item);
if (this->temp_.empty())
@@ -93,13 +219,18 @@ void RemoteReceiverComponent::loop() {
this->temp_.push_back(-this->idle_us_);
this->call_listeners_dumpers_();
}
#endif
}
void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
#if ESP_IDF_VERSION_MAJOR >= 5
void RemoteReceiverComponent::decode_rmt_(rmt_symbol_word_t *item, size_t item_count) {
#else
void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t item_count) {
#endif
bool prev_level = false;
uint32_t prev_length = 0;
this->temp_.clear();
int32_t multiplier = this->pin_->is_inverted() ? -1 : 1;
size_t item_count = len / sizeof(rmt_item32_t);
uint32_t filter_ticks = this->from_microseconds_(this->filter_us_);
ESP_LOGVV(TAG, "START:");
@@ -124,7 +255,8 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
this->temp_.reserve(item_count * 2); // each RMT item has 2 pulses
for (size_t i = 0; i < item_count; i++) {
if (item[i].duration0 == 0u) {
// Do nothing
// EOF, sometimes garbage follows, break early
break;
} else if ((bool(item[i].level0) == prev_level) || (item[i].duration0 < filter_ticks)) {
prev_length += item[i].duration0;
} else {
@@ -140,7 +272,8 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
}
if (item[i].duration1 == 0u) {
// Do nothing
// EOF, sometimes garbage follows, break early
break;
} else if ((bool(item[i].level1) == prev_level) || (item[i].duration1 < filter_ticks)) {
prev_length += item[i].duration1;
} else {