1
0
mirror of https://github.com/esphome/esphome.git synced 2025-04-13 06:10:30 +01:00

Merge branch 'dev' into dev

This commit is contained in:
Evaldas Auryla 2024-12-20 11:40:36 +01:00 committed by GitHub
commit 93449577ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 1307 additions and 520 deletions

View File

@ -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

View File

@ -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(

View File

@ -26,10 +26,10 @@ template<class T> 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() {

View File

@ -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):

View File

@ -1,5 +1,5 @@
#include <cinttypes>
#include "led_strip.h"
#include <cinttypes>
#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_symbol_word_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_symbol_word_t>::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_item32_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_item32_t>::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:

View File

@ -9,8 +9,14 @@
#include "esphome/core/helpers.h"
#include <driver/gpio.h>
#include <driver/rmt.h>
#include <esp_err.h>
#include <esp_idf_version.h>
#if ESP_IDF_VERSION_MAJOR >= 5
#include <driver/rmt_tx.h>
#else
#include <driver/rmt.h>
#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<uint32_t> max_refresh_rate_{};

View File

@ -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]}")
)
)
)

View File

@ -1,38 +1,20 @@
#include "json_util.h"
#include "esphome/core/log.h"
#ifdef USE_ESP8266
#include <Esp.h>
#endif
#ifdef USE_ESP32
#include <esp_heap_caps.h>
#endif
#ifdef USE_RP2040
#include <Arduino.h>
#endif
namespace esphome {
namespace json {
static const char *const TAG = "json";
static std::vector<char> global_json_build_buffer; // NOLINT
static const auto ALLOCATOR = RAMAllocator<uint8_t>(RAMAllocator<uint8_t>::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;
}

View File

@ -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")

View File

@ -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;
}

View File

@ -1 +1,4 @@
CODEOWNERS = ["@clydebarrow"]
CONF_DRAW_FROM_ORIGIN = "draw_from_origin"
CONF_DRAW_ROUNDING = "draw_rounding"

View File

@ -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]

View File

@ -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")

View File

@ -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));

View File

@ -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<uint8_t> &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<std::vector<uint8_t>> init_sequences_{};

View File

@ -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;

View File

@ -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 <driver/rmt.h>
#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

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 {

View File

@ -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)

View File

@ -5,6 +5,10 @@
#include <vector>
#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5
#include <driver/rmt_tx.h>
#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_symbol_word_t> 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_item32_t> rmt_temp_;
#endif
esp_err_t error_code_{ESP_OK};
std::string error_string_{""};
bool inverted_{false};

View File

@ -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();
}

View File

@ -11,6 +11,14 @@
#include "esphome/core/optional.h"
#ifdef USE_ESP8266
#include <Esp.h>
#endif
#ifdef USE_RP2040
#include <Arduino.h>
#endif
#ifdef USE_ESP32
#include <esp_heap_caps.h>
#endif
@ -684,20 +692,23 @@ template<class T> 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<class U> constexpr RAMAllocator(const RAMAllocator<U> &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<T *>(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<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) {
ptr = static_cast<T *>(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 T> 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<class T> using ExternalRAMAllocator = RAMAllocator<T>;

View File

@ -13,8 +13,8 @@ static const char *const TAG = "ring_buffer";
RingBuffer::~RingBuffer() {
if (this->handle_ != nullptr) {
vStreamBufferDelete(this->handle_);
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
vRingbufferDelete(this->handle_);
RAMAllocator<uint8_t> allocator(RAMAllocator<uint8_t>::ALLOW_FAILURE);
allocator.deallocate(this->storage_, this->size_);
}
}
@ -22,26 +22,49 @@ RingBuffer::~RingBuffer() {
std::unique_ptr<RingBuffer> RingBuffer::create(size_t len) {
std::unique_ptr<RingBuffer> rb = make_unique<RingBuffer>();
rb->size_ = len + 1;
rb->size_ = len;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
RAMAllocator<uint8_t> allocator(RAMAllocator<uint8_t>::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

View File

@ -3,7 +3,7 @@
#ifdef USE_ESP32
#include <freertos/FreeRTOS.h>
#include <freertos/stream_buffer.h>
#include <freertos/ringbuf.h>
#include <cinttypes>
#include <memory>
@ -82,9 +82,14 @@ class RingBuffer {
static std::unique_ptr<RingBuffer> 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};
};

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -3,4 +3,4 @@ substitutions:
rmt_channel: "2"
packages:
common: !include esp32-common.yaml
common: !include esp32-common-ard.yaml

View File

@ -3,4 +3,4 @@ substitutions:
rmt_channel: "2"
packages:
common: !include esp32-common.yaml
common: !include esp32-common-ard.yaml

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,8 @@
remote_transmitter:
- id: xmitr
pin: ${pin}
rmt_channel: ${rmt_channel}
carrier_duty_percent: 50%
packages:
buttons: !include common-buttons.yaml

View File

@ -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

View File

@ -1,8 +0,0 @@
remote_transmitter:
id: rcvr
pin: ${pin}
rmt_channel: ${rmt_channel}
carrier_duty_percent: 50%
packages:
buttons: !include common-buttons.yaml

View File

@ -3,4 +3,4 @@ substitutions:
rmt_channel: "2"
packages:
common: !include esp32-common.yaml
common: !include esp32-common-ard.yaml

View File

@ -3,4 +3,4 @@ substitutions:
rmt_channel: "1"
packages:
common: !include esp32-common.yaml
common: !include esp32-common-ard.yaml

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
remote_transmitter:
id: trns
id: xmitr
pin: GPIO5
carrier_duty_percent: 50%