diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cd86854b2..0304cd2304 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v4.5.0 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b0bde75451..98db45831a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -602,6 +602,9 @@ async def to_code(config): cg.add_platformio_option( "platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"] ) + add_idf_sdkconfig_option( + f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True + ) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) add_idf_sdkconfig_option( diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index 5b31b97ae2..c98477e121 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -26,10 +26,10 @@ template class Queue { void push(T *element) { if (element == nullptr) return; - if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { - q_.push(element); - xSemaphoreGive(m_); - } + // It is not called from main loop. Thus it won't block main thread. + xSemaphoreTake(m_, portMAX_DELAY); + q_.push(element); + xSemaphoreGive(m_); } T *pop() { diff --git a/esphome/components/esp32_rmt/__init__.py b/esphome/components/esp32_rmt/__init__.py index bda240680b..171c335727 100644 --- a/esphome/components/esp32_rmt/__init__.py +++ b/esphome/components/esp32_rmt/__init__.py @@ -1,7 +1,8 @@ -import esphome.config_validation as cv import esphome.codegen as cg - from esphome.components import esp32 +import esphome.config_validation as cv +from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION +from esphome.core import CORE CODEOWNERS = ["@jesserockz"] @@ -36,8 +37,32 @@ RMT_CHANNEL_ENUMS = { } -def validate_rmt_channel(*, tx: bool): +def use_new_rmt_driver(): + framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0): + return True + return False + +def validate_clock_resolution(): + def _validator(value): + cv.only_on_esp32(value) + value = cv.int_(value) + variant = esp32.get_esp32_variant() + if variant == esp32.const.VARIANT_ESP32H2 and value > 32000000: + raise cv.Invalid( + f"ESP32 variant {variant} has a max clock_resolution of 32000000." + ) + if value > 80000000: + raise cv.Invalid( + f"ESP32 variant {variant} has a max clock_resolution of 80000000." + ) + return value + + return _validator + + +def validate_rmt_channel(*, tx: bool): rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS def _validator(value): diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index c2209f7a6c..8ee890ec10 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -1,5 +1,5 @@ -#include #include "led_strip.h" +#include #ifdef USE_ESP32 @@ -13,9 +13,13 @@ namespace esp32_rmt_led_strip { static const char *const TAG = "esp32_rmt_led_strip"; +#ifdef USE_ESP32_VARIANT_ESP32H2 +static const uint32_t RMT_CLK_FREQ = 32000000; +static const uint8_t RMT_CLK_DIV = 1; +#else static const uint32_t RMT_CLK_FREQ = 80000000; - static const uint8_t RMT_CLK_DIV = 2; +#endif void ESP32RMTLEDStripLightOutput::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP32 LED Strip..."); @@ -37,9 +41,48 @@ void ESP32RMTLEDStripLightOutput::setup() { return; } +#if ESP_IDF_VERSION_MAJOR >= 5 + RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); + + // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset + this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); + + rmt_tx_channel_config_t channel; + memset(&channel, 0, sizeof(channel)); + channel.clk_src = RMT_CLK_SRC_DEFAULT; + channel.resolution_hz = RMT_CLK_FREQ / RMT_CLK_DIV; + channel.gpio_num = gpio_num_t(this->pin_); + channel.mem_block_symbols = this->rmt_symbols_; + channel.trans_queue_depth = 1; + channel.flags.io_loop_back = 0; + channel.flags.io_od_mode = 0; + channel.flags.invert_out = 0; + channel.flags.with_dma = 0; + channel.intr_priority = 0; + if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) { + ESP_LOGE(TAG, "Channel creation failed"); + this->mark_failed(); + return; + } + + rmt_copy_encoder_config_t encoder; + memset(&encoder, 0, sizeof(encoder)); + if (rmt_new_copy_encoder(&encoder, &this->encoder_) != ESP_OK) { + ESP_LOGE(TAG, "Encoder creation failed"); + this->mark_failed(); + return; + } + + if (rmt_enable(this->channel_) != ESP_OK) { + ESP_LOGE(TAG, "Enabling channel failed"); + this->mark_failed(); + return; + } +#else RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); - this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + - 1); // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset + + // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset + this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); rmt_config_t config; memset(&config, 0, sizeof(config)); @@ -64,6 +107,7 @@ void ESP32RMTLEDStripLightOutput::setup() { this->mark_failed(); return; } +#endif } void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, @@ -100,7 +144,12 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { ESP_LOGVV(TAG, "Writing RGB values to bus..."); - if (rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)) != ESP_OK) { +#if ESP_IDF_VERSION_MAJOR >= 5 + esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000); +#else + esp_err_t error = rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)); +#endif + if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX timeout"); this->status_set_warning(); return; @@ -112,7 +161,11 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { size_t size = 0; size_t len = 0; uint8_t *psrc = this->buf_; +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_symbol_word_t *pdest = this->rmt_buf_; +#else rmt_item32_t *pdest = this->rmt_buf_; +#endif while (size < buffer_size) { uint8_t b = *psrc; for (int i = 0; i < 8; i++) { @@ -130,7 +183,16 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { len++; } - if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) { +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.loop_count = 0; + config.flags.eot_level = 0; + error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config); +#else + error = rmt_write_items(this->channel_, this->rmt_buf_, len, false); +#endif + if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX error"); this->status_set_warning(); return; @@ -186,7 +248,11 @@ light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index void ESP32RMTLEDStripLightOutput::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 RMT LED Strip:"); ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); +#if ESP_IDF_VERSION_MAJOR >= 5 + ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_); +#else ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_); +#endif const char *rgb_order; switch (this->rgb_order_) { case ORDER_RGB: diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index d21bd86e75..fe49b9a2f3 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -9,8 +9,14 @@ #include "esphome/core/helpers.h" #include -#include #include +#include + +#if ESP_IDF_VERSION_MAJOR >= 5 +#include +#else +#include +#endif namespace esphome { namespace esp32_rmt_led_strip { @@ -54,7 +60,11 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint32_t reset_time_high, uint32_t reset_time_low); void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; } +#if ESP_IDF_VERSION_MAJOR >= 5 + void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; } +#else void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; } +#endif void clear_effect_data() override { for (int i = 0; i < this->size(); i++) @@ -70,7 +80,17 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_channel_handle_t channel_{nullptr}; + rmt_encoder_handle_t encoder_{nullptr}; + rmt_symbol_word_t *rmt_buf_{nullptr}; + rmt_symbol_word_t bit0_, bit1_, reset_; + uint32_t rmt_symbols_; +#else rmt_item32_t *rmt_buf_{nullptr}; + rmt_item32_t bit0_, bit1_, reset_; + rmt_channel_t channel_{RMT_CHANNEL_0}; +#endif uint8_t pin_; uint16_t num_leds_; @@ -78,9 +98,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { bool is_wrgb_; bool use_psram_; - rmt_item32_t bit0_, bit1_, reset_; RGBOrder rgb_order_; - rmt_channel_t channel_; uint32_t last_refresh_{0}; optional max_refresh_rate_{}; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 976f70e858..67a0e31461 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -13,6 +13,7 @@ from esphome.const import ( CONF_PIN, CONF_RGB_ORDER, CONF_RMT_CHANNEL, + CONF_RMT_SYMBOLS, ) CODEOWNERS = ["@jesserockz"] @@ -23,8 +24,6 @@ ESP32RMTLEDStripLightOutput = esp32_rmt_led_strip_ns.class_( "ESP32RMTLEDStripLightOutput", light.AddressableLight ) -rmt_channel_t = cg.global_ns.enum("rmt_channel_t") - RGBOrder = esp32_rmt_led_strip_ns.enum("RGBOrder") RGB_ORDERS = { @@ -65,6 +64,13 @@ CONF_RESET_HIGH = "reset_high" CONF_RESET_LOW = "reset_low" +def final_validation(config): + if not esp32_rmt.use_new_rmt_driver() and CONF_RMT_CHANNEL not in config: + raise cv.Invalid("rmt_channel is a required option.") + + +FINAL_VALIDATE_SCHEMA = final_validation + CONFIG_SCHEMA = cv.All( light.ADDRESSABLE_LIGHT_SCHEMA.extend( { @@ -72,7 +78,18 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), - cv.Required(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), + cv.Optional(CONF_RMT_CHANNEL): cv.All( + cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=True) + ), + cv.SplitDefault( + CONF_RMT_SYMBOLS, + esp32_idf=64, + esp32_s2_idf=64, + esp32_s3_idf=48, + esp32_c3_idf=48, + esp32_c6_idf=48, + esp32_h2_idf=48, + ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, @@ -148,8 +165,12 @@ async def to_code(config): cg.add(var.set_is_wrgb(config[CONF_IS_WRGB])) cg.add(var.set_use_psram(config[CONF_USE_PSRAM])) - cg.add( - var.set_rmt_channel( - getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}") + if esp32_rmt.use_new_rmt_driver(): + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + else: + rmt_channel_t = cg.global_ns.enum("rmt_channel_t") + cg.add( + var.set_rmt_channel( + getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}") + ) ) - ) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 89ec13fe5b..87b1cc6d2d 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,38 +1,20 @@ #include "json_util.h" #include "esphome/core/log.h" -#ifdef USE_ESP8266 -#include -#endif -#ifdef USE_ESP32 -#include -#endif -#ifdef USE_RP2040 -#include -#endif - namespace esphome { namespace json { static const char *const TAG = "json"; static std::vector global_json_build_buffer; // NOLINT +static const auto ALLOCATOR = RAMAllocator(RAMAllocator::ALLOC_INTERNAL); std::string build_json(const json_build_t &f) { // Here we are allocating up to 5kb of memory, // with the heap size minus 2kb to be safe if less than 5kb // as we can not have a true dynamic sized document. // The excess memory is freed below with `shrinkToFit()` -#ifdef USE_ESP8266 - const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) -#elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); -#elif defined(USE_RP2040) - const size_t free_heap = rp2040.getFreeHeap(); -#elif defined(USE_LIBRETINY) - const size_t free_heap = lt_heap_get_free(); -#endif - + auto free_heap = ALLOCATOR.get_max_free_block_size(); size_t request_size = std::min(free_heap, (size_t) 512); while (true) { ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size); @@ -67,20 +49,12 @@ bool parse_json(const std::string &data, const json_parse_t &f) { // with the heap size minus 2kb to be safe if less than that // as we can not have a true dynamic sized document. // The excess memory is freed below with `shrinkToFit()` -#ifdef USE_ESP8266 - const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) -#elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); -#elif defined(USE_RP2040) - const size_t free_heap = rp2040.getFreeHeap(); -#elif defined(USE_LIBRETINY) - const size_t free_heap = lt_heap_get_free(); -#endif + auto free_heap = ALLOCATOR.get_max_free_block_size(); size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); while (true) { DynamicJsonDocument json_document(request_size); if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size, + ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %zu bytes, free heap: %zu", request_size, free_heap); return false; } diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 8fdd03f647..b858e8df01 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -23,7 +23,7 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, focused_widgets, update_to_code -from .defines import add_define +from .defines import CONF_DRAW_ROUNDING, add_define from .encoders import ( ENCODERS_CONFIG, encoders_to_code, @@ -205,6 +205,10 @@ def final_validation(configs): raise cv.Invalid( "Using auto_clear_enabled: true in display config not compatible with LVGL" ) + if draw_rounding := display.get(CONF_DRAW_ROUNDING): + config[CONF_DRAW_ROUNDING] = max( + draw_rounding, config[CONF_DRAW_ROUNDING] + ) buffer_frac = config[CONF_BUFFER_SIZE] if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config: LOGGER.warning("buffer_size: may need to be reduced without PSRAM") diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 1786809dfa..8c4669cba5 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -80,15 +80,7 @@ bool OnlineImage::resize_(int width_in, int height_in) { this->width_ = width; ESP_LOGD(TAG, "New size: (%d, %d)", width, height); } else { -#if defined(USE_ESP8266) - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - int max_block = ESP.getMaxFreeBlockSize(); -#elif defined(USE_ESP32) - int max_block = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); -#else - int max_block = -1; -#endif - ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %d Bytes", max_block); + ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %zu Bytes", this->allocator_.get_max_free_block_size()); this->end_connection_(); return false; } diff --git a/esphome/components/qspi_dbi/__init__.py b/esphome/components/qspi_dbi/__init__.py index c58ce8a01e..a4b833f6d7 100644 --- a/esphome/components/qspi_dbi/__init__.py +++ b/esphome/components/qspi_dbi/__init__.py @@ -1 +1,4 @@ CODEOWNERS = ["@clydebarrow"] + +CONF_DRAW_FROM_ORIGIN = "draw_from_origin" +CONF_DRAW_ROUNDING = "draw_rounding" diff --git a/esphome/components/qspi_dbi/display.py b/esphome/components/qspi_dbi/display.py index 71ae31f182..ab6dd66cf2 100644 --- a/esphome/components/qspi_dbi/display.py +++ b/esphome/components/qspi_dbi/display.py @@ -24,6 +24,7 @@ from esphome.const import ( ) from esphome.core import TimePeriod +from . import CONF_DRAW_FROM_ORIGIN, CONF_DRAW_ROUNDING from .models import DriverChip DEPENDENCIES = ["spi"] @@ -41,7 +42,6 @@ COLOR_ORDERS = { } DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema -CONF_DRAW_FROM_ORIGIN = "draw_from_origin" DELAY_FLAG = 0xFF @@ -78,56 +78,81 @@ def _validate(config): return config -CONFIG_SCHEMA = cv.All( - display.FULL_DISPLAY_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(QSPI_DBI), - cv.Required(CONF_MODEL): cv.one_of( - *DriverChip.chips.keys(), upper=True - ), - cv.Optional(CONF_INIT_SEQUENCE): cv.ensure_list(map_sequence), - cv.Required(CONF_DIMENSIONS): cv.Any( - cv.dimensions, - cv.Schema( - { - cv.Required(CONF_WIDTH): validate_dimension, - cv.Required(CONF_HEIGHT): validate_dimension, - cv.Optional( - CONF_OFFSET_HEIGHT, default=0 - ): validate_dimension, - cv.Optional( - CONF_OFFSET_WIDTH, default=0 - ): validate_dimension, - } - ), - ), - cv.Optional(CONF_TRANSFORM): cv.Schema( +def power_of_two(value): + value = cv.int_range(1, 128)(value) + if value & (value - 1) != 0: + raise cv.Invalid("value must be a power of two") + return value + + +BASE_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(QSPI_DBI), + cv.Optional(CONF_INIT_SEQUENCE): cv.ensure_list(map_sequence), + cv.Required(CONF_DIMENSIONS): cv.Any( + cv.dimensions, + cv.Schema( { - cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, - cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, - cv.Optional(CONF_SWAP_XY, default=False): cv.boolean, + cv.Required(CONF_WIDTH): validate_dimension, + cv.Required(CONF_HEIGHT): validate_dimension, + cv.Optional(CONF_OFFSET_HEIGHT, default=0): validate_dimension, + cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension, } ), - cv.Optional(CONF_COLOR_ORDER, default="RGB"): cv.enum( - COLOR_ORDERS, upper=True - ), - cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=0xD0): cv.int_range( - 0, 0xFF, min_included=True, max_included=True - ), - cv.Optional(CONF_DRAW_FROM_ORIGIN, default=False): cv.boolean, - } - ).extend( - spi.spi_device_schema( - cs_pin_required=False, - default_mode="MODE0", - default_data_rate=10e6, - quad=True, - ) + ), + cv.Optional(CONF_DRAW_FROM_ORIGIN, default=False): cv.boolean, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=0xD0): cv.int_range( + 0, 0xFF, min_included=True, max_included=True + ), + } + ).extend( + spi.spi_device_schema( + cs_pin_required=False, + default_mode="MODE0", + default_data_rate=10e6, + quad=True, ) + ) +) + + +def model_property(name, defaults, fallback): + return cv.Optional(name, default=defaults.get(name, fallback)) + + +def model_schema(defaults): + transform = cv.Schema( + { + cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, + cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, + } + ) + if defaults.get(CONF_SWAP_XY, True): + transform = transform.extend( + { + cv.Optional(CONF_SWAP_XY, default=False): cv.boolean, + } + ) + return BASE_SCHEMA.extend( + { + model_property(CONF_INVERT_COLORS, defaults, False): cv.boolean, + model_property(CONF_COLOR_ORDER, defaults, "RGB"): cv.enum( + COLOR_ORDERS, upper=True + ), + model_property(CONF_DRAW_ROUNDING, defaults, 2): power_of_two, + cv.Optional(CONF_TRANSFORM): transform, + } + ) + + +CONFIG_SCHEMA = cv.All( + cv.typed_schema( + {k.upper(): model_schema(v.defaults) for k, v in DriverChip.chips.items()}, + upper=True, + key=CONF_MODEL, ), cv.only_with_esp_idf, ) @@ -152,6 +177,7 @@ async def to_code(config): cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) cg.add(var.set_model(config[CONF_MODEL])) cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN])) + cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING])) if enable_pin := config.get(CONF_ENABLE_PIN): enable = await cg.gpio_pin_expression(enable_pin) cg.add(var.set_enable_pin(enable)) @@ -163,7 +189,8 @@ async def to_code(config): if transform := config.get(CONF_TRANSFORM): cg.add(var.set_mirror_x(transform[CONF_MIRROR_X])) cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y])) - cg.add(var.set_swap_xy(transform[CONF_SWAP_XY])) + # swap_xy is not implemented for some chips + cg.add(var.set_swap_xy(transform.get(CONF_SWAP_XY, False))) if CONF_DIMENSIONS in config: dimensions = config[CONF_DIMENSIONS] diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index c1fe434853..7ae1a10ec0 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -1,5 +1,10 @@ # Commands +from esphome.const import CONF_INVERT_COLORS, CONF_SWAP_XY + +from . import CONF_DRAW_ROUNDING + SW_RESET_CMD = 0x01 +SLEEP_IN = 0x10 SLEEP_OUT = 0x11 NORON = 0x13 INVERT_OFF = 0x20 @@ -24,11 +29,12 @@ PAGESEL = 0xFE class DriverChip: chips = {} - def __init__(self, name: str): + def __init__(self, name: str, defaults=None): name = name.upper() self.name = name self.chips[name] = self self.initsequence = [] + self.defaults = defaults or {} def cmd(self, c, *args): """ @@ -59,9 +65,246 @@ chip.cmd(TEON, 0x00) chip.cmd(PIXFMT, 0x55) chip.cmd(NORON) -chip = DriverChip("AXS15231") +chip = DriverChip("AXS15231", {CONF_DRAW_ROUNDING: 8, CONF_SWAP_XY: False}) chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) chip.cmd(0xC1, 0x33) chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) +chip = DriverChip( + "JC4832W535", + { + CONF_DRAW_ROUNDING: 8, + CONF_SWAP_XY: False, + }, +) +chip.cmd(DISPLAY_OFF) +chip.delay(20) +chip.cmd(SLEEP_IN) +chip.delay(80) +chip.cmd(SLEEP_OUT) +chip.cmd(INVERT_OFF) +# A magic sequence to enable the windowed drawing mode +chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) +chip.cmd(0xC1, 0x33) +chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +chip = DriverChip("JC3636W518", {CONF_INVERT_COLORS: True}) +chip.cmd(0xF0, 0x08) +chip.cmd(0xF2, 0x08) +chip.cmd(0x9B, 0x51) +chip.cmd(0x86, 0x53) +chip.cmd(0xF2, 0x80) +chip.cmd(0xF0, 0x00) +chip.cmd(0xF0, 0x01) +chip.cmd(0xF1, 0x01) +chip.cmd(0xB0, 0x54) +chip.cmd(0xB1, 0x3F) +chip.cmd(0xB2, 0x2A) +chip.cmd(0xB4, 0x46) +chip.cmd(0xB5, 0x34) +chip.cmd(0xB6, 0xD5) +chip.cmd(0xB7, 0x30) +chip.cmd(0xBA, 0x00) +chip.cmd(0xBB, 0x08) +chip.cmd(0xBC, 0x08) +chip.cmd(0xBD, 0x00) +chip.cmd(0xC0, 0x80) +chip.cmd(0xC1, 0x10) +chip.cmd(0xC2, 0x37) +chip.cmd(0xC3, 0x80) +chip.cmd(0xC4, 0x10) +chip.cmd(0xC5, 0x37) +chip.cmd(0xC6, 0xA9) +chip.cmd(0xC7, 0x41) +chip.cmd(0xC8, 0x51) +chip.cmd(0xC9, 0xA9) +chip.cmd(0xCA, 0x41) +chip.cmd(0xCB, 0x51) +chip.cmd(0xD0, 0x91) +chip.cmd(0xD1, 0x68) +chip.cmd(0xD2, 0x69) +chip.cmd(0xF5, 0x00, 0xA5) +chip.cmd(0xDD, 0x3F) +chip.cmd(0xDE, 0x3F) +chip.cmd(0xF1, 0x10) +chip.cmd(0xF0, 0x00) +chip.cmd(0xF0, 0x02) +chip.cmd( + 0xE0, + 0x70, + 0x09, + 0x12, + 0x0C, + 0x0B, + 0x27, + 0x38, + 0x54, + 0x4E, + 0x19, + 0x15, + 0x15, + 0x2C, + 0x2F, +) +chip.cmd( + 0xE1, + 0x70, + 0x08, + 0x11, + 0x0C, + 0x0B, + 0x27, + 0x38, + 0x43, + 0x4C, + 0x18, + 0x14, + 0x14, + 0x2B, + 0x2D, +) +chip.cmd(0xF0, 0x10) +chip.cmd(0xF3, 0x10) +chip.cmd(0xE0, 0x08) +chip.cmd(0xE1, 0x00) +chip.cmd(0xE2, 0x00) +chip.cmd(0xE3, 0x00) +chip.cmd(0xE4, 0xE0) +chip.cmd(0xE5, 0x06) +chip.cmd(0xE6, 0x21) +chip.cmd(0xE7, 0x00) +chip.cmd(0xE8, 0x05) +chip.cmd(0xE9, 0x82) +chip.cmd(0xEA, 0xDF) +chip.cmd(0xEB, 0x89) +chip.cmd(0xEC, 0x20) +chip.cmd(0xED, 0x14) +chip.cmd(0xEE, 0xFF) +chip.cmd(0xEF, 0x00) +chip.cmd(0xF8, 0xFF) +chip.cmd(0xF9, 0x00) +chip.cmd(0xFA, 0x00) +chip.cmd(0xFB, 0x30) +chip.cmd(0xFC, 0x00) +chip.cmd(0xFD, 0x00) +chip.cmd(0xFE, 0x00) +chip.cmd(0xFF, 0x00) +chip.cmd(0x60, 0x42) +chip.cmd(0x61, 0xE0) +chip.cmd(0x62, 0x40) +chip.cmd(0x63, 0x40) +chip.cmd(0x64, 0x02) +chip.cmd(0x65, 0x00) +chip.cmd(0x66, 0x40) +chip.cmd(0x67, 0x03) +chip.cmd(0x68, 0x00) +chip.cmd(0x69, 0x00) +chip.cmd(0x6A, 0x00) +chip.cmd(0x6B, 0x00) +chip.cmd(0x70, 0x42) +chip.cmd(0x71, 0xE0) +chip.cmd(0x72, 0x40) +chip.cmd(0x73, 0x40) +chip.cmd(0x74, 0x02) +chip.cmd(0x75, 0x00) +chip.cmd(0x76, 0x40) +chip.cmd(0x77, 0x03) +chip.cmd(0x78, 0x00) +chip.cmd(0x79, 0x00) +chip.cmd(0x7A, 0x00) +chip.cmd(0x7B, 0x00) +chip.cmd(0x80, 0x48) +chip.cmd(0x81, 0x00) +chip.cmd(0x82, 0x05) +chip.cmd(0x83, 0x02) +chip.cmd(0x84, 0xDD) +chip.cmd(0x85, 0x00) +chip.cmd(0x86, 0x00) +chip.cmd(0x87, 0x00) +chip.cmd(0x88, 0x48) +chip.cmd(0x89, 0x00) +chip.cmd(0x8A, 0x07) +chip.cmd(0x8B, 0x02) +chip.cmd(0x8C, 0xDF) +chip.cmd(0x8D, 0x00) +chip.cmd(0x8E, 0x00) +chip.cmd(0x8F, 0x00) +chip.cmd(0x90, 0x48) +chip.cmd(0x91, 0x00) +chip.cmd(0x92, 0x09) +chip.cmd(0x93, 0x02) +chip.cmd(0x94, 0xE1) +chip.cmd(0x95, 0x00) +chip.cmd(0x96, 0x00) +chip.cmd(0x97, 0x00) +chip.cmd(0x98, 0x48) +chip.cmd(0x99, 0x00) +chip.cmd(0x9A, 0x0B) +chip.cmd(0x9B, 0x02) +chip.cmd(0x9C, 0xE3) +chip.cmd(0x9D, 0x00) +chip.cmd(0x9E, 0x00) +chip.cmd(0x9F, 0x00) +chip.cmd(0xA0, 0x48) +chip.cmd(0xA1, 0x00) +chip.cmd(0xA2, 0x04) +chip.cmd(0xA3, 0x02) +chip.cmd(0xA4, 0xDC) +chip.cmd(0xA5, 0x00) +chip.cmd(0xA6, 0x00) +chip.cmd(0xA7, 0x00) +chip.cmd(0xA8, 0x48) +chip.cmd(0xA9, 0x00) +chip.cmd(0xAA, 0x06) +chip.cmd(0xAB, 0x02) +chip.cmd(0xAC, 0xDE) +chip.cmd(0xAD, 0x00) +chip.cmd(0xAE, 0x00) +chip.cmd(0xAF, 0x00) +chip.cmd(0xB0, 0x48) +chip.cmd(0xB1, 0x00) +chip.cmd(0xB2, 0x08) +chip.cmd(0xB3, 0x02) +chip.cmd(0xB4, 0xE0) +chip.cmd(0xB5, 0x00) +chip.cmd(0xB6, 0x00) +chip.cmd(0xB7, 0x00) +chip.cmd(0xB8, 0x48) +chip.cmd(0xB9, 0x00) +chip.cmd(0xBA, 0x0A) +chip.cmd(0xBB, 0x02) +chip.cmd(0xBC, 0xE2) +chip.cmd(0xBD, 0x00) +chip.cmd(0xBE, 0x00) +chip.cmd(0xBF, 0x00) +chip.cmd(0xC0, 0x12) +chip.cmd(0xC1, 0xAA) +chip.cmd(0xC2, 0x65) +chip.cmd(0xC3, 0x74) +chip.cmd(0xC4, 0x47) +chip.cmd(0xC5, 0x56) +chip.cmd(0xC6, 0x00) +chip.cmd(0xC7, 0x88) +chip.cmd(0xC8, 0x99) +chip.cmd(0xC9, 0x33) +chip.cmd(0xD0, 0x21) +chip.cmd(0xD1, 0xAA) +chip.cmd(0xD2, 0x65) +chip.cmd(0xD3, 0x74) +chip.cmd(0xD4, 0x47) +chip.cmd(0xD5, 0x56) +chip.cmd(0xD6, 0x00) +chip.cmd(0xD7, 0x88) +chip.cmd(0xD8, 0x99) +chip.cmd(0xD9, 0x33) +chip.cmd(0xF3, 0x01) +chip.cmd(0xF0, 0x00) +chip.cmd(0xF0, 0x01) +chip.cmd(0xF1, 0x01) +chip.cmd(0xA0, 0x0B) +chip.cmd(0xA3, 0x2A) +chip.cmd(0xA5, 0xC3) +chip.cmd(PIXFMT, 0x55) + + DriverChip("Custom") diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index f8fd5dd374..380c93c400 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -33,19 +33,12 @@ void QspiDbi::update() { this->do_update_(); if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) return; - // Start addresses and widths/heights must be divisible by 2 (CASET/RASET restriction in datasheet) - if (this->x_low_ % 2 == 1) { - this->x_low_--; - } - if (this->x_high_ % 2 == 0) { - this->x_high_++; - } - if (this->y_low_ % 2 == 1) { - this->y_low_--; - } - if (this->y_high_ % 2 == 0) { - this->y_high_++; - } + // Some chips require that the drawing window be aligned on certain boundaries + auto dr = this->draw_rounding_; + this->x_low_ = this->x_low_ / dr * dr; + this->y_low_ = this->y_low_ / dr * dr; + this->x_high_ = (this->x_high_ + dr) / dr * dr - 1; + this->y_high_ = (this->y_high_ + dr) / dr * dr - 1; if (this->draw_from_origin_) { this->x_low_ = 0; this->y_low_ = 0; @@ -175,10 +168,9 @@ void QspiDbi::write_to_display_(int x_start, int y_start, int w, int h, const ui this->write_cmd_addr_data(8, 0x32, 24, 0x2C00, ptr, w * h * 2, 4); } else { auto stride = x_offset + w + x_pad; - uint16_t cmd = 0x2C00; + this->write_cmd_addr_data(8, 0x32, 24, 0x2C00, nullptr, 0, 4); for (int y = 0; y != h; y++) { - this->write_cmd_addr_data(8, 0x32, 24, cmd, ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2, 4); - cmd = 0x3C00; + this->write_cmd_addr_data(0, 0, 0, 0, ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2, 4); } } this->disable(); @@ -220,6 +212,7 @@ void QspiDbi::dump_config() { ESP_LOGCONFIG("", "Model: %s", this->model_); ESP_LOGCONFIG(TAG, " Height: %u", this->height_); ESP_LOGCONFIG(TAG, " Width: %u", this->width_); + ESP_LOGCONFIG(TAG, " Draw rounding: %u", this->draw_rounding_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); diff --git a/esphome/components/qspi_dbi/qspi_dbi.h b/esphome/components/qspi_dbi/qspi_dbi.h index ebb65a8a05..2c555f115e 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.h +++ b/esphome/components/qspi_dbi/qspi_dbi.h @@ -4,12 +4,10 @@ #pragma once #ifdef USE_ESP_IDF -#include "esphome/core/component.h" #include "esphome/components/spi/spi.h" #include "esphome/components/display/display.h" #include "esphome/components/display/display_buffer.h" #include "esphome/components/display/display_color_utils.h" -#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_rgb.h" @@ -105,6 +103,7 @@ class QspiDbi : public display::DisplayBuffer, int get_height_internal() override { return this->height_; } bool can_proceed() override { return this->setup_complete_; } void add_init_sequence(const std::vector &sequence) { this->init_sequences_.push_back(sequence); } + void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; } protected: void check_buffer_() { @@ -161,6 +160,7 @@ class QspiDbi : public display::DisplayBuffer, bool mirror_x_{}; bool mirror_y_{}; bool draw_from_origin_{false}; + unsigned draw_rounding_{2}; uint8_t brightness_{0xD0}; const char *model_{"Unknown"}; std::vector> init_sequences_{}; diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index fdfd0b43cc..5dff2c6a38 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -8,7 +8,7 @@ namespace remote_base { static const char *const TAG = "remote_base"; -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_block_num) { static rmt_channel_t next_rmt_channel = RMT_CHANNEL_0; this->channel_ = next_rmt_channel; diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index c31127735a..70691177ef 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -8,7 +8,7 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 #include #endif @@ -112,25 +112,43 @@ class RemoteComponentBase { #ifdef USE_ESP32 class RemoteRMTChannel { public: +#if ESP_IDF_VERSION_MAJOR >= 5 + void set_clock_resolution(uint32_t clock_resolution) { this->clock_resolution_ = clock_resolution; } + void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; } +#else explicit RemoteRMTChannel(uint8_t mem_block_num = 1); explicit RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num = 1); void config_rmt(rmt_config_t &rmt); void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; } +#endif protected: uint32_t from_microseconds_(uint32_t us) { +#if ESP_IDF_VERSION_MAJOR >= 5 + const uint32_t ticks_per_ten_us = this->clock_resolution_ / 100000u; +#else const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; +#endif return us * ticks_per_ten_us / 10; } uint32_t to_microseconds_(uint32_t ticks) { +#if ESP_IDF_VERSION_MAJOR >= 5 + const uint32_t ticks_per_ten_us = this->clock_resolution_ / 100000u; +#else const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; +#endif return (ticks * 10) / ticks_per_ten_us; } RemoteComponentBase *remote_base_; +#if ESP_IDF_VERSION_MAJOR >= 5 + uint32_t clock_resolution_{1000000}; + uint32_t rmt_symbols_; +#else rmt_channel_t channel_{RMT_CHANNEL_0}; uint8_t mem_block_num_; uint8_t clock_divider_{80}; +#endif }; #endif diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index d3f61977c6..b01443a974 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -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) diff --git a/esphome/components/remote_receiver/remote_receiver.h b/esphome/components/remote_receiver/remote_receiver.h index 773f8cf636..8d19d5490f 100644 --- a/esphome/components/remote_receiver/remote_receiver.h +++ b/esphome/components/remote_receiver/remote_receiver.h @@ -5,6 +5,10 @@ #include +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#include +#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 diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index 91295871e2..48b84d61d4 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -2,15 +2,104 @@ #include "esphome/core/log.h" #ifdef USE_ESP32 -#include 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 { diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index f979939739..ea29751671 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -2,12 +2,23 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import esp32_rmt, remote_base import esphome.config_validation as cv -from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL +from esphome.const import ( + CONF_CARRIER_DUTY_PERCENT, + CONF_CLOCK_DIVIDER, + CONF_CLOCK_RESOLUTION, + CONF_ID, + CONF_PIN, + CONF_RMT_CHANNEL, + CONF_RMT_SYMBOLS, + CONF_USE_DMA, +) +from esphome.core import CORE AUTO_LOAD = ["remote_base"] CONF_ON_TRANSMIT = "on_transmit" CONF_ON_COMPLETE = "on_complete" +CONF_ONE_WIRE = "one_wire" remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") RemoteTransmitterComponent = remote_transmitter_ns.class_( @@ -22,7 +33,28 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All( cv.percentage_int, cv.Range(min=1, max=100) ), - cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), + cv.Optional(CONF_CLOCK_RESOLUTION): cv.All( + cv.only_on_esp32, + cv.only_with_esp_idf, + esp32_rmt.validate_clock_resolution(), + ), + cv.Optional(CONF_CLOCK_DIVIDER): cv.All( + cv.only_on_esp32, cv.only_with_arduino, cv.int_range(min=1, max=255) + ), + cv.Optional(CONF_ONE_WIRE): cv.All(cv.only_with_esp_idf, cv.boolean), + cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), + cv.SplitDefault( + CONF_RMT_SYMBOLS, + esp32_idf=64, + esp32_s2_idf=64, + esp32_s3_idf=48, + esp32_c3_idf=48, + esp32_c6_idf=48, + esp32_h2_idf=48, + ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), + cv.Optional(CONF_RMT_CHANNEL): cv.All( + cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=True) + ), cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True), cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True), } @@ -31,8 +63,24 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) - if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: - var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) + if CORE.is_esp32: + 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])) + if CONF_CLOCK_RESOLUTION in config: + cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) + if CONF_USE_DMA in config: + cg.add(var.set_with_dma(config[CONF_USE_DMA])) + if CONF_ONE_WIRE in config: + cg.add(var.set_one_wire(config[CONF_ONE_WIRE])) + else: + if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: + var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) + else: + var = cg.new_Pvariable(config[CONF_ID], pin) + if CONF_CLOCK_DIVIDER in config: + cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER])) + else: var = cg.new_Pvariable(config[CONF_ID], pin) await cg.register_component(var, config) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 4abe687d23..20b98d5488 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -5,6 +5,10 @@ #include +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#include +#endif + namespace esphome { namespace remote_transmitter { @@ -16,7 +20,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #endif { public: -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 RemoteTransmitterComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) : remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} @@ -29,10 +33,16 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void dump_config() override; - float get_setup_priority() const override { return setup_priority::DATA; } + // transmitter setup must run after receiver setup to allow the same GPIO to be used by both + float get_setup_priority() const override { return setup_priority::DATA - 1; } void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; } +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 + void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } + void set_one_wire(bool one_wire) { this->one_wire_ = one_wire; } +#endif + Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; }; Trigger<> *get_complete_trigger() const { return this->complete_trigger_; }; @@ -54,7 +64,15 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, uint32_t current_carrier_frequency_{38000}; bool initialized_{false}; +#if ESP_IDF_VERSION_MAJOR >= 5 + std::vector rmt_temp_; + bool with_dma_{false}; + bool one_wire_{false}; + rmt_channel_handle_t channel_{NULL}; + rmt_encoder_handle_t encoder_{NULL}; +#else std::vector rmt_temp_; +#endif esp_err_t error_code_{ESP_OK}; std::string error_string_{""}; bool inverted_{false}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index bce2408723..efb1735bfd 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -9,13 +9,22 @@ namespace remote_transmitter { static const char *const TAG = "remote_transmitter"; -void RemoteTransmitterComponent::setup() { this->configure_rmt_(); } +void RemoteTransmitterComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up Remote Transmitter..."); + this->configure_rmt_(); +} void RemoteTransmitterComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Transmitter..."); + ESP_LOGCONFIG(TAG, "Remote Transmitter:"); +#if ESP_IDF_VERSION_MAJOR >= 5 + ESP_LOGCONFIG(TAG, " One wire: %s", this->one_wire_ ? "true" : "false"); + ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); + ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_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 LOG_PIN(" Pin: ", this->pin_); if (this->current_carrier_frequency_ != 0 && this->carrier_duty_percent_ != 100) { @@ -29,6 +38,72 @@ void RemoteTransmitterComponent::dump_config() { } void RemoteTransmitterComponent::configure_rmt_() { +#if ESP_IDF_VERSION_MAJOR >= 5 + esp_err_t error; + + if (!this->initialized_) { + rmt_tx_channel_config_t channel; + memset(&channel, 0, sizeof(channel)); + channel.clk_src = RMT_CLK_SRC_DEFAULT; + channel.resolution_hz = this->clock_resolution_; + channel.gpio_num = gpio_num_t(this->pin_->get_pin()); + channel.mem_block_symbols = this->rmt_symbols_; + channel.trans_queue_depth = 1; + channel.flags.io_loop_back = this->one_wire_; + channel.flags.io_od_mode = this->one_wire_; + channel.flags.invert_out = 0; + channel.flags.with_dma = this->with_dma_; + channel.intr_priority = 0; + error = rmt_new_tx_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_tx_channel"; + } + this->mark_failed(); + return; + } + + rmt_copy_encoder_config_t encoder; + memset(&encoder, 0, sizeof(encoder)); + error = rmt_new_copy_encoder(&encoder, &this->encoder_); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_new_copy_encoder"; + 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; + } + this->initialized_ = true; + } + + if (this->current_carrier_frequency_ == 0 || this->carrier_duty_percent_ == 100) { + error = rmt_apply_carrier(this->channel_, nullptr); + } else { + rmt_carrier_config_t carrier; + memset(&carrier, 0, sizeof(carrier)); + carrier.frequency_hz = this->current_carrier_frequency_; + carrier.duty_cycle = (float) this->carrier_duty_percent_ / 100.0f; + carrier.flags.polarity_active_low = this->inverted_; + carrier.flags.always_on = 1; + error = rmt_apply_carrier(this->channel_, &carrier); + } + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_apply_carrier"; + this->mark_failed(); + return; + } +#else rmt_config_t c{}; this->config_rmt(c); @@ -76,6 +151,7 @@ void RemoteTransmitterComponent::configure_rmt_() { } this->initialized_ = true; } +#endif } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { @@ -90,7 +166,11 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen this->rmt_temp_.clear(); this->rmt_temp_.reserve((this->temp_.get_data().size() + 1) / 2); uint32_t rmt_i = 0; +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_symbol_word_t rmt_item; +#else rmt_item32_t rmt_item; +#endif for (int32_t val : this->temp_.get_data()) { bool level = val >= 0; @@ -125,6 +205,31 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen return; } this->transmit_trigger_->trigger(); +#if ESP_IDF_VERSION_MAJOR >= 5 + for (uint32_t i = 0; i < send_times; i++) { + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.loop_count = 0; + config.flags.eot_level = this->inverted_; + esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(), + this->rmt_temp_.size() * sizeof(rmt_symbol_word_t), &config); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } else { + this->status_clear_warning(); + } + error = rmt_tx_wait_all_done(this->channel_, -1); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } else { + this->status_clear_warning(); + } + if (i + 1 < send_times) + delayMicroseconds(send_wait); + } +#else for (uint32_t i = 0; i < send_times; i++) { esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true); if (error != ESP_OK) { @@ -136,6 +241,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (i + 1 < send_times) delayMicroseconds(send_wait); } +#endif this->complete_trigger_->trigger(); } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index fcbd8d8683..c823439fb3 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -11,6 +11,14 @@ #include "esphome/core/optional.h" +#ifdef USE_ESP8266 +#include +#endif + +#ifdef USE_RP2040 +#include +#endif + #ifdef USE_ESP32 #include #endif @@ -684,20 +692,23 @@ template class RAMAllocator { }; RAMAllocator() = default; - RAMAllocator(uint8_t flags) : flags_{flags} {} + RAMAllocator(uint8_t flags) { + // default is both external and internal + flags &= ALLOC_INTERNAL | ALLOC_EXTERNAL; + if (flags != 0) + this->flags_ = flags; + } template constexpr RAMAllocator(const RAMAllocator &other) : flags_{other.flags_} {} T *allocate(size_t n) { size_t size = n * sizeof(T); T *ptr = nullptr; #ifdef USE_ESP32 - // External allocation by default or if explicitely requested - if ((this->flags_ & Flags::ALLOC_EXTERNAL) || ((this->flags_ & Flags::ALLOC_INTERNAL) == 0)) { + if (this->flags_ & Flags::ALLOC_EXTERNAL) { ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); } - // Fallback to internal allocation if explicitely requested or no flag is specified - if (ptr == nullptr && ((this->flags_ & Flags::ALLOC_INTERNAL) || (this->flags_ & Flags::ALLOC_EXTERNAL) == 0)) { - ptr = static_cast(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) + if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) { + ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); } #else // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported @@ -710,8 +721,46 @@ template class RAMAllocator { free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) } + /** + * Return the total heap space available via this allocator + */ + size_t get_free_heap_size() const { +#ifdef USE_ESP8266 + return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP32) + auto max_internal = + this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0; + auto max_external = + this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0; + return max_internal + max_external; +#elif defined(USE_RP2040) + return ::rp2040.getFreeHeap(); +#elif defined(USE_LIBRETINY) + return lt_heap_get_free(); +#else + return 100000; +#endif + } + + /** + * Return the maximum size block this allocator could allocate. This may be an approximation on some platforms + */ + size_t get_max_free_block_size() const { +#ifdef USE_ESP8266 + return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP32) + auto max_internal = + this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0; + auto max_external = + this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0; + return std::max(max_internal, max_external); +#else + return this->get_free_heap_size(); +#endif + } + private: - uint8_t flags_{Flags::ALLOW_FAILURE}; + uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL}; }; template using ExternalRAMAllocator = RAMAllocator; diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index 6152ada314..f779531263 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -13,8 +13,8 @@ static const char *const TAG = "ring_buffer"; RingBuffer::~RingBuffer() { if (this->handle_ != nullptr) { - vStreamBufferDelete(this->handle_); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + vRingbufferDelete(this->handle_); + RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); allocator.deallocate(this->storage_, this->size_); } } @@ -22,26 +22,49 @@ RingBuffer::~RingBuffer() { std::unique_ptr RingBuffer::create(size_t len) { std::unique_ptr rb = make_unique(); - rb->size_ = len + 1; + rb->size_ = len; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); rb->storage_ = allocator.allocate(rb->size_); if (rb->storage_ == nullptr) { return nullptr; } - rb->handle_ = xStreamBufferCreateStatic(rb->size_, 1, rb->storage_, &rb->structure_); + rb->handle_ = xRingbufferCreateStatic(rb->size_, RINGBUF_TYPE_BYTEBUF, rb->storage_, &rb->structure_); ESP_LOGD(TAG, "Created ring buffer with size %u", len); + return rb; } size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { - if (ticks_to_wait > 0) - xStreamBufferSetTriggerLevel(this->handle_, len); + size_t bytes_read = 0; - size_t bytes_read = xStreamBufferReceive(this->handle_, data, len, ticks_to_wait); + void *buffer_data = xRingbufferReceiveUpTo(this->handle_, &bytes_read, ticks_to_wait, len); - xStreamBufferSetTriggerLevel(this->handle_, 1); + if (buffer_data == nullptr) { + return 0; + } + + std::memcpy(data, buffer_data, bytes_read); + + vRingbufferReturnItem(this->handle_, buffer_data); + + if (bytes_read < len) { + // Data may have wrapped around, so read a second time to receive the remainder + size_t follow_up_bytes_read = 0; + size_t bytes_remaining = len - bytes_read; + + buffer_data = xRingbufferReceiveUpTo(this->handle_, &follow_up_bytes_read, 0, bytes_remaining); + + if (buffer_data == nullptr) { + return bytes_read; + } + + std::memcpy((void *) ((uint8_t *) (data) + bytes_read), buffer_data, follow_up_bytes_read); + + vRingbufferReturnItem(this->handle_, buffer_data); + bytes_read += follow_up_bytes_read; + } return bytes_read; } @@ -49,22 +72,55 @@ size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { size_t RingBuffer::write(const void *data, size_t len) { size_t free = this->free(); if (free < len) { - size_t needed = len - free; - uint8_t discard[needed]; - xStreamBufferReceive(this->handle_, discard, needed, 0); + // Free enough space in the ring buffer to fit the new data + this->discard_bytes_(len - free); } - return xStreamBufferSend(this->handle_, data, len, 0); + return this->write_without_replacement(data, len, 0); } size_t RingBuffer::write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait) { - return xStreamBufferSend(this->handle_, data, len, ticks_to_wait); + if (!xRingbufferSend(this->handle_, data, len, ticks_to_wait)) { + // Couldn't fit all the data, so only write what will fit + size_t free = std::min(this->free(), len); + if (xRingbufferSend(this->handle_, data, free, 0)) { + return free; + } + return 0; + } + return len; } -size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); } +size_t RingBuffer::available() const { + UBaseType_t ux_items_waiting = 0; + vRingbufferGetInfo(this->handle_, nullptr, nullptr, nullptr, nullptr, &ux_items_waiting); + return ux_items_waiting; +} -size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); } +size_t RingBuffer::free() const { return xRingbufferGetCurFreeSize(this->handle_); } -BaseType_t RingBuffer::reset() { return xStreamBufferReset(this->handle_); } +BaseType_t RingBuffer::reset() { + // Discards all the available data + return this->discard_bytes_(this->available()); +} + +bool RingBuffer::discard_bytes_(size_t discard_bytes) { + size_t bytes_read = 0; + + void *buffer_data = xRingbufferReceiveUpTo(this->handle_, &bytes_read, 0, discard_bytes); + if (buffer_data != nullptr) + vRingbufferReturnItem(this->handle_, buffer_data); + + if (bytes_read < discard_bytes) { + size_t wrapped_bytes_read = 0; + buffer_data = xRingbufferReceiveUpTo(this->handle_, &wrapped_bytes_read, 0, discard_bytes - bytes_read); + if (buffer_data != nullptr) { + vRingbufferReturnItem(this->handle_, buffer_data); + bytes_read += wrapped_bytes_read; + } + } + + return (bytes_read == discard_bytes); +} } // namespace esphome diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index aade1b5f49..bad96d3181 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -3,7 +3,7 @@ #ifdef USE_ESP32 #include -#include +#include #include #include @@ -82,9 +82,14 @@ class RingBuffer { static std::unique_ptr create(size_t len); protected: - StreamBufferHandle_t handle_; - StaticStreamBuffer_t structure_; - uint8_t *storage_; + /// @brief Discards data from the ring buffer. + /// @param discard_bytes amount of bytes to discard + /// @return True if all bytes were successfully discarded, false otherwise + bool discard_bytes_(size_t discard_bytes); + + RingbufHandle_t handle_{nullptr}; + StaticRingbuffer_t structure_; + uint8_t *storage_{nullptr}; size_t size_{0}; }; diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml index 8d04d3370b..93318d0581 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml @@ -3,14 +3,12 @@ light: id: led_strip pin: 4 num_leds: 60 - rmt_channel: 0 rgb_order: GRB chipset: ws2812 - platform: esp32_rmt_led_strip id: led_strip2 pin: 5 num_leds: 60 - rmt_channel: 1 rgb_order: RGB bit0_high: 100µs bit0_low: 100µs diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml index 6e1763b339..228af17189 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml @@ -3,14 +3,12 @@ light: id: led_strip pin: 13 num_leds: 60 - rmt_channel: 6 rgb_order: GRB chipset: ws2812 - platform: esp32_rmt_led_strip id: led_strip2 pin: 14 num_leds: 60 - rmt_channel: 2 rgb_order: RGB bit0_high: 100µs bit0_low: 100µs diff --git a/tests/components/remote_receiver/common-actions.yaml b/tests/components/remote_receiver/common-actions.yaml new file mode 100644 index 0000000000..23589aed22 --- /dev/null +++ b/tests/components/remote_receiver/common-actions.yaml @@ -0,0 +1,144 @@ +on_abbwelcome: + then: + - logger.log: + format: "on_abbwelcome: %u" + args: ["x.data()[0]"] +on_aeha: + then: + - logger.log: + format: "on_aeha: %u %u" + args: ["x.address", "x.data.front()"] +on_byronsx: + then: + - logger.log: + format: "on_byronsx: %u %u" + args: ["x.address", "x.command"] +on_canalsat: + then: + - logger.log: + format: "on_canalsat: %u %u" + args: ["x.address", "x.command"] +# on_canalsatld: +# then: +# - logger.log: +# format: "on_canalsatld: %u %u" +# args: ["x.address", "x.command"] +on_coolix: + then: + - logger.log: + format: "on_coolix: %lu %lu" + args: ["long(x.first)", "long(x.second)"] +on_dish: + then: + - logger.log: + format: "on_dish: %u %u" + args: ["x.address", "x.command"] +on_dooya: + then: + - logger.log: + format: "on_dooya: %u %u %u" + args: ["x.channel", "x.button", "x.check"] +on_drayton: + then: + - logger.log: + format: "on_drayton: %u %u %u" + args: ["x.address", "x.channel", "x.command"] +on_jvc: + then: + - logger.log: + format: "on_jvc: %lu" + args: ["long(x.data)"] +on_keeloq: + then: + - logger.log: + format: "on_keeloq: %lu %lu %u" + args: ["long(x.encrypted)", "long(x.address)", "x.command"] +on_haier: + then: + - logger.log: + format: "on_haier: %u" + args: ["x.data.front()"] +on_lg: + then: + - logger.log: + format: "on_lg: %lu %u" + args: ["long(x.data)", "x.nbits"] +on_magiquest: + then: + - logger.log: + format: "on_magiquest: %u %lu" + args: ["x.magnitude", "long(x.wand_id)"] +on_midea: + then: + - logger.log: + format: "on_midea: %u %u" + args: ["x.size()", "x.data()[0]"] +on_nec: + then: + - logger.log: + format: "on_nec: %u %u" + args: ["x.address", "x.command"] +on_nexa: + then: + - logger.log: + format: "on_nexa: %lu %u %u %u %u" + args: ["long(x.device)", "x.group", "x.state", "x.channel", "x.level"] +on_panasonic: + then: + - logger.log: + format: "on_panasonic: %u %lu" + args: ["x.address", "long(x.command)"] +on_pioneer: + then: + - logger.log: + format: "on_pioneer: %u %u" + args: ["x.rc_code_1", "x.rc_code_2"] +on_pronto: + then: + - logger.log: + format: "on_pronto: %s" + args: ["x.data.c_str()"] +on_raw: + then: + - logger.log: + format: "on_raw: %lu" + args: ["long(x.front())"] +on_rc5: + then: + - logger.log: + format: "on_rc5: %u %u" + args: ["x.address", "x.command"] +on_rc6: + then: + - logger.log: + format: "on_rc6: %u %u" + args: ["x.address", "x.command"] +on_rc_switch: + then: + - logger.log: + format: "on_rc_switch: %llu %u" + args: ["x.code", "x.protocol"] +on_samsung: + then: + - logger.log: + format: "on_samsung: %llu %u" + args: ["x.data", "x.nbits"] +on_samsung36: + then: + - logger.log: + format: "on_samsung36: %u %lu" + args: ["x.address", "long(x.command)"] +on_sony: + then: + - logger.log: + format: "on_sony: %lu %u" + args: ["long(x.data)", "x.nbits"] +on_toshiba_ac: + then: + - logger.log: + format: "on_toshiba_ac: %llu %llu" + args: ["x.rc_code_1", "x.rc_code_2"] +on_mirage: + then: + - lambda: |- + ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); diff --git a/tests/components/remote_receiver/esp32-common-ard.yaml b/tests/components/remote_receiver/esp32-common-ard.yaml new file mode 100644 index 0000000000..e331a35307 --- /dev/null +++ b/tests/components/remote_receiver/esp32-common-ard.yaml @@ -0,0 +1,14 @@ +remote_receiver: + - id: rcvr + pin: ${pin} + rmt_channel: ${rmt_channel} + dump: all + tolerance: 25% + <<: !include common-actions.yaml + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_receiver/esp32-common-idf.yaml b/tests/components/remote_receiver/esp32-common-idf.yaml new file mode 100644 index 0000000000..b314880f8a --- /dev/null +++ b/tests/components/remote_receiver/esp32-common-idf.yaml @@ -0,0 +1,18 @@ +remote_receiver: + - id: rcvr + pin: ${pin} + dump: all + tolerance: 25% + clock_resolution: ${clock_resolution} + filter_symbols: ${filter_symbols} + receive_symbols: ${receive_symbols} + rmt_symbols: ${rmt_symbols} + use_dma: ${use_dma} + <<: !include common-actions.yaml + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_receiver/esp32-common.yaml b/tests/components/remote_receiver/esp32-common.yaml deleted file mode 100644 index 7e5d2cce32..0000000000 --- a/tests/components/remote_receiver/esp32-common.yaml +++ /dev/null @@ -1,157 +0,0 @@ -remote_receiver: - id: rcvr - pin: ${pin} - rmt_channel: ${rmt_channel} - dump: all - tolerance: 25% - on_abbwelcome: - then: - - logger.log: - format: "on_abbwelcome: %u" - args: ["x.data()[0]"] - on_aeha: - then: - - logger.log: - format: "on_aeha: %u %u" - args: ["x.address", "x.data.front()"] - on_byronsx: - then: - - logger.log: - format: "on_byronsx: %u %u" - args: ["x.address", "x.command"] - on_canalsat: - then: - - logger.log: - format: "on_canalsat: %u %u" - args: ["x.address", "x.command"] - # on_canalsatld: - # then: - # - logger.log: - # format: "on_canalsatld: %u %u" - # args: ["x.address", "x.command"] - on_coolix: - then: - - logger.log: - format: "on_coolix: %lu %lu" - args: ["long(x.first)", "long(x.second)"] - on_dish: - then: - - logger.log: - format: "on_dish: %u %u" - args: ["x.address", "x.command"] - on_dooya: - then: - - logger.log: - format: "on_dooya: %u %u %u" - args: ["x.channel", "x.button", "x.check"] - on_drayton: - then: - - logger.log: - format: "on_drayton: %u %u %u" - args: ["x.address", "x.channel", "x.command"] - on_jvc: - then: - - logger.log: - format: "on_jvc: %lu" - args: ["long(x.data)"] - on_keeloq: - then: - - logger.log: - format: "on_keeloq: %lu %lu %u" - args: ["long(x.encrypted)", "long(x.address)", "x.command"] - on_haier: - then: - - logger.log: - format: "on_haier: %u" - args: ["x.data.front()"] - on_lg: - then: - - logger.log: - format: "on_lg: %lu %u" - args: ["long(x.data)", "x.nbits"] - on_magiquest: - then: - - logger.log: - format: "on_magiquest: %u %lu" - args: ["x.magnitude", "long(x.wand_id)"] - on_midea: - then: - - logger.log: - format: "on_midea: %u %u" - args: ["x.size()", "x.data()[0]"] - on_nec: - then: - - logger.log: - format: "on_nec: %u %u" - args: ["x.address", "x.command"] - on_nexa: - then: - - logger.log: - format: "on_nexa: %lu %u %u %u %u" - args: ["long(x.device)", "x.group", "x.state", "x.channel", "x.level"] - on_panasonic: - then: - - logger.log: - format: "on_panasonic: %u %lu" - args: ["x.address", "long(x.command)"] - on_pioneer: - then: - - logger.log: - format: "on_pioneer: %u %u" - args: ["x.rc_code_1", "x.rc_code_2"] - on_pronto: - then: - - logger.log: - format: "on_pronto: %s" - args: ["x.data.c_str()"] - on_raw: - then: - - logger.log: - format: "on_raw: %lu" - args: ["long(x.front())"] - on_rc5: - then: - - logger.log: - format: "on_rc5: %u %u" - args: ["x.address", "x.command"] - on_rc6: - then: - - logger.log: - format: "on_rc6: %u %u" - args: ["x.address", "x.command"] - on_rc_switch: - then: - - logger.log: - format: "on_rc_switch: %llu %u" - args: ["x.code", "x.protocol"] - on_samsung: - then: - - logger.log: - format: "on_samsung: %llu %u" - args: ["x.data", "x.nbits"] - on_samsung36: - then: - - logger.log: - format: "on_samsung36: %u %lu" - args: ["x.address", "long(x.command)"] - on_sony: - then: - - logger.log: - format: "on_sony: %lu %u" - args: ["long(x.data)", "x.nbits"] - on_toshiba_ac: - then: - - logger.log: - format: "on_toshiba_ac: %llu %llu" - args: ["x.rc_code_1", "x.rc_code_2"] - on_mirage: - then: - - lambda: |- - ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); - -binary_sensor: - - platform: remote_receiver - name: Panasonic Remote Input - panasonic: - address: 0x4004 - command: 0x100BCBD diff --git a/tests/components/remote_receiver/test.esp32-ard.yaml b/tests/components/remote_receiver/test.esp32-ard.yaml index 16d276958a..5d29187206 100644 --- a/tests/components/remote_receiver/test.esp32-ard.yaml +++ b/tests/components/remote_receiver/test.esp32-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "2" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-ard.yaml b/tests/components/remote_receiver/test.esp32-c3-ard.yaml index 16d276958a..5d29187206 100644 --- a/tests/components/remote_receiver/test.esp32-c3-ard.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "2" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-idf.yaml b/tests/components/remote_receiver/test.esp32-c3-idf.yaml index 16d276958a..495bb293c3 100644 --- a/tests/components/remote_receiver/test.esp32-c3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-idf.yaml @@ -1,6 +1,10 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_receiver/test.esp32-idf.yaml b/tests/components/remote_receiver/test.esp32-idf.yaml index 16d276958a..495bb293c3 100644 --- a/tests/components/remote_receiver/test.esp32-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-idf.yaml @@ -1,6 +1,10 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_receiver/test.esp32-s3-idf.yaml b/tests/components/remote_receiver/test.esp32-s3-idf.yaml index 265ecda771..e678ba456d 100644 --- a/tests/components/remote_receiver/test.esp32-s3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-s3-idf.yaml @@ -1,6 +1,10 @@ substitutions: pin: GPIO38 - rmt_channel: "5" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_receiver/test.esp8266-ard.yaml b/tests/components/remote_receiver/test.esp8266-ard.yaml index 27d36d4a16..c9784ae003 100644 --- a/tests/components/remote_receiver/test.esp8266-ard.yaml +++ b/tests/components/remote_receiver/test.esp8266-ard.yaml @@ -2,150 +2,7 @@ remote_receiver: id: rcvr pin: GPIO5 dump: all - on_abbwelcome: - then: - - logger.log: - format: "on_abbwelcome: %u" - args: ["x.data()[0]"] - on_aeha: - then: - - logger.log: - format: "on_aeha: %u %u" - args: ["x.address", "x.data.front()"] - on_byronsx: - then: - - logger.log: - format: "on_byronsx: %u %u" - args: ["x.address", "x.command"] - on_canalsat: - then: - - logger.log: - format: "on_canalsat: %u %u" - args: ["x.address", "x.command"] - # on_canalsatld: - # then: - # - logger.log: - # format: "on_canalsatld: %u %u" - # args: ["x.address", "x.command"] - on_coolix: - then: - - logger.log: - format: "on_coolix: %u %u" - args: ["x.first", "x.second"] - on_dish: - then: - - logger.log: - format: "on_dish: %u %u" - args: ["x.address", "x.command"] - on_dooya: - then: - - logger.log: - format: "on_dooya: %u %u %u" - args: ["x.channel", "x.button", "x.check"] - on_drayton: - then: - - logger.log: - format: "on_drayton: %u %u %u" - args: ["x.address", "x.channel", "x.command"] - on_jvc: - then: - - logger.log: - format: "on_jvc: %u" - args: ["x.data"] - on_keeloq: - then: - - logger.log: - format: "on_keeloq: %u %u %u" - args: ["x.encrypted", "x.address", "x.command"] - on_haier: - then: - - logger.log: - format: "on_haier: %u" - args: ["x.data.front()"] - on_lg: - then: - - logger.log: - format: "on_lg: %u %u" - args: ["x.data", "x.nbits"] - on_magiquest: - then: - - logger.log: - format: "on_magiquest: %u %u" - args: ["x.magnitude", "x.wand_id"] - on_midea: - then: - - logger.log: - format: "on_midea: %u %u" - args: ["x.size()", "x.data()[0]"] - on_nec: - then: - - logger.log: - format: "on_nec: %u %u" - args: ["x.address", "x.command"] - on_nexa: - then: - - logger.log: - format: "on_nexa: %u %u %u %u %u" - args: ["x.device", "x.group", "x.state", "x.channel", "x.level"] - on_panasonic: - then: - - logger.log: - format: "on_panasonic: %u %u" - args: ["x.address", "x.command"] - on_pioneer: - then: - - logger.log: - format: "on_pioneer: %u %u" - args: ["x.rc_code_1", "x.rc_code_2"] - on_pronto: - then: - - logger.log: - format: "on_pronto: %s" - args: ["x.data.c_str()"] - on_raw: - then: - - logger.log: - format: "on_raw: %u" - args: ["x.front()"] - on_rc5: - then: - - logger.log: - format: "on_rc5: %u %u" - args: ["x.address", "x.command"] - on_rc6: - then: - - logger.log: - format: "on_rc6: %u %u" - args: ["x.address", "x.command"] - on_rc_switch: - then: - - logger.log: - format: "on_rc_switch: %llu %u" - args: ["x.code", "x.protocol"] - on_samsung: - then: - - logger.log: - format: "on_samsung: %llu %u" - args: ["x.data", "x.nbits"] - on_samsung36: - then: - - logger.log: - format: "on_samsung36: %u %u" - args: ["x.address", "x.command"] - on_sony: - then: - - logger.log: - format: "on_sony: %u %u" - args: ["x.data", "x.nbits"] - on_toshiba_ac: - then: - - logger.log: - format: "on_toshiba_ac: %llu %llu" - args: ["x.rc_code_1", "x.rc_code_2"] - on_mirage: - then: - - lambda: |- - ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); + <<: !include common-actions.yaml binary_sensor: - platform: remote_receiver diff --git a/tests/components/remote_transmitter/esp32-common-ard.yaml b/tests/components/remote_transmitter/esp32-common-ard.yaml new file mode 100644 index 0000000000..420cea326d --- /dev/null +++ b/tests/components/remote_transmitter/esp32-common-ard.yaml @@ -0,0 +1,8 @@ +remote_transmitter: + - id: xmitr + pin: ${pin} + rmt_channel: ${rmt_channel} + carrier_duty_percent: 50% + +packages: + buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/esp32-common-idf.yaml b/tests/components/remote_transmitter/esp32-common-idf.yaml new file mode 100644 index 0000000000..3b8b5e2aef --- /dev/null +++ b/tests/components/remote_transmitter/esp32-common-idf.yaml @@ -0,0 +1,11 @@ +remote_transmitter: + - id: xmitr + pin: ${pin} + carrier_duty_percent: 50% + clock_resolution: ${clock_resolution} + one_wire: ${one_wire} + rmt_symbols: ${rmt_symbols} + use_dma: ${use_dma} + +packages: + buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/esp32-common.yaml b/tests/components/remote_transmitter/esp32-common.yaml deleted file mode 100644 index 3f3cd3f8c7..0000000000 --- a/tests/components/remote_transmitter/esp32-common.yaml +++ /dev/null @@ -1,8 +0,0 @@ -remote_transmitter: - id: rcvr - pin: ${pin} - rmt_channel: ${rmt_channel} - carrier_duty_percent: 50% - -packages: - buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/test.esp32-ard.yaml b/tests/components/remote_transmitter/test.esp32-ard.yaml index 16d276958a..5d29187206 100644 --- a/tests/components/remote_transmitter/test.esp32-ard.yaml +++ b/tests/components/remote_transmitter/test.esp32-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "2" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml index 3e2dc88e5a..c755b11563 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "1" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml index 3e2dc88e5a..1a27f29dac 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO2 - rmt_channel: "1" + clock_resolution: "2000000" + one_wire: "true" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_transmitter/test.esp32-idf.yaml b/tests/components/remote_transmitter/test.esp32-idf.yaml index 16d276958a..1a27f29dac 100644 --- a/tests/components/remote_transmitter/test.esp32-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-idf.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + one_wire: "true" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml index 31851dc54c..25bdbd4772 100644 --- a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO38 - rmt_channel: "3" + clock_resolution: "2000000" + one_wire: "true" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_transmitter/test.esp8266-ard.yaml b/tests/components/remote_transmitter/test.esp8266-ard.yaml index de494485f4..19759360f4 100644 --- a/tests/components/remote_transmitter/test.esp8266-ard.yaml +++ b/tests/components/remote_transmitter/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ remote_transmitter: - id: trns + id: xmitr pin: GPIO5 carrier_duty_percent: 50%