mirror of
https://github.com/esphome/esphome.git
synced 2026-02-15 22:09:36 -07:00
Compare commits
8 Commits
runtime-im
...
json-remov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac6636127 | ||
|
|
f2cb5db9e0 | ||
|
|
066419019f | ||
|
|
15da6d0a0b | ||
|
|
6303bc3e35 | ||
|
|
0f4dc6702d | ||
|
|
f48c8a6444 | ||
|
|
062f223876 |
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -75,12 +77,32 @@ bool AudioTransferBuffer::has_buffered_data() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
|
bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
|
||||||
if (this->buffer_length_ > 0) {
|
if (this->buffer_ == nullptr) {
|
||||||
// Buffer currently has data, so reallocation is impossible
|
return this->allocate_buffer_(new_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_buffer_size < this->buffer_length_) {
|
||||||
|
// New size is too small to hold existing data
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this->deallocate_buffer_();
|
|
||||||
return this->allocate_buffer_(new_buffer_size);
|
// Shift existing data to the start of the buffer so realloc preserves it
|
||||||
|
if ((this->buffer_length_ > 0) && (this->data_start_ != this->buffer_)) {
|
||||||
|
std::memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||||
|
this->data_start_ = this->buffer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
RAMAllocator<uint8_t> allocator;
|
||||||
|
uint8_t *new_buffer = allocator.reallocate(this->buffer_, new_buffer_size);
|
||||||
|
if (new_buffer == nullptr) {
|
||||||
|
// Reallocation failed, but the original buffer is still valid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->buffer_ = new_buffer;
|
||||||
|
this->data_start_ = this->buffer_;
|
||||||
|
this->buffer_size_ = new_buffer_size;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
|
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
|
||||||
@@ -115,7 +137,7 @@ size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_
|
|||||||
if (pre_shift) {
|
if (pre_shift) {
|
||||||
// Shift data in buffer to start
|
// Shift data in buffer to start
|
||||||
if (this->buffer_length_ > 0) {
|
if (this->buffer_length_ > 0) {
|
||||||
memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
std::memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||||
}
|
}
|
||||||
this->data_start_ = this->buffer_;
|
this->data_start_ = this->buffer_;
|
||||||
}
|
}
|
||||||
@@ -150,7 +172,7 @@ size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait,
|
|||||||
|
|
||||||
if (post_shift) {
|
if (post_shift) {
|
||||||
// Shift unwritten data to the start of the buffer
|
// Shift unwritten data to the start of the buffer
|
||||||
memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
std::memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||||
this->data_start_ = this->buffer_;
|
this->data_start_ = this->buffer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ class AudioTransferBuffer {
|
|||||||
/// @return True if there is data, false otherwise.
|
/// @return True if there is data, false otherwise.
|
||||||
virtual bool has_buffered_data() const;
|
virtual bool has_buffered_data() const;
|
||||||
|
|
||||||
|
/// @brief Reallocates the transfer buffer, preserving any existing data.
|
||||||
|
/// @param new_buffer_size The new size in bytes. Must be at least as large as available().
|
||||||
|
/// @return True if successful, false otherwise. On failure, the original buffer remains valid.
|
||||||
bool reallocate(size_t new_buffer_size);
|
bool reallocate(size_t new_buffer_size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ void LinearCombinationComponent::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LinearCombinationComponent::handle_new_value(float value) {
|
void LinearCombinationComponent::handle_new_value(float value) {
|
||||||
// Multiplies each sensor state by a configured coeffecient and then sums
|
// Multiplies each sensor state by a configured coefficient and then sums
|
||||||
|
|
||||||
if (!std::isfinite(value))
|
if (!std::isfinite(value))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
@@ -15,6 +17,8 @@ from esphome.const import (
|
|||||||
)
|
)
|
||||||
from esphome.core.entity_helpers import inherit_property_from
|
from esphome.core.entity_helpers import inherit_property_from
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CODEOWNERS = ["@Cat-Ion", "@kahrendt"]
|
CODEOWNERS = ["@Cat-Ion", "@kahrendt"]
|
||||||
|
|
||||||
combination_ns = cg.esphome_ns.namespace("combination")
|
combination_ns = cg.esphome_ns.namespace("combination")
|
||||||
@@ -47,7 +51,8 @@ SumCombinationComponent = combination_ns.class_(
|
|||||||
"SumCombinationComponent", cg.Component, sensor.Sensor
|
"SumCombinationComponent", cg.Component, sensor.Sensor
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_COEFFECIENT = "coeffecient"
|
CONF_COEFFICIENT = "coefficient"
|
||||||
|
CONF_COEFFECIENT = "coeffecient" # Deprecated, remove before 2026.12.0
|
||||||
CONF_ERROR = "error"
|
CONF_ERROR = "error"
|
||||||
CONF_KALMAN = "kalman"
|
CONF_KALMAN = "kalman"
|
||||||
CONF_LINEAR = "linear"
|
CONF_LINEAR = "linear"
|
||||||
@@ -68,11 +73,34 @@ KALMAN_SOURCE_SCHEMA = cv.Schema(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
LINEAR_SOURCE_SCHEMA = cv.Schema(
|
|
||||||
{
|
def _migrate_coeffecient(config):
|
||||||
cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor),
|
"""Migrate deprecated 'coeffecient' spelling to 'coefficient'."""
|
||||||
cv.Required(CONF_COEFFECIENT): cv.templatable(cv.float_),
|
if CONF_COEFFECIENT in config:
|
||||||
}
|
if CONF_COEFFICIENT in config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Cannot specify both '{CONF_COEFFICIENT}' and '{CONF_COEFFECIENT}'"
|
||||||
|
)
|
||||||
|
_LOGGER.warning(
|
||||||
|
"'%s' is deprecated, use '%s' instead. Will be removed in 2026.12.0",
|
||||||
|
CONF_COEFFECIENT,
|
||||||
|
CONF_COEFFICIENT,
|
||||||
|
)
|
||||||
|
config[CONF_COEFFICIENT] = config.pop(CONF_COEFFECIENT)
|
||||||
|
elif CONF_COEFFICIENT not in config:
|
||||||
|
raise cv.Invalid(f"'{CONF_COEFFICIENT}' is a required option")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
LINEAR_SOURCE_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor),
|
||||||
|
cv.Optional(CONF_COEFFICIENT): cv.templatable(cv.float_),
|
||||||
|
cv.Optional(CONF_COEFFECIENT): cv.templatable(cv.float_),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
_migrate_coeffecient,
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSOR_ONLY_SOURCE_SCHEMA = cv.Schema(
|
SENSOR_ONLY_SOURCE_SCHEMA = cv.Schema(
|
||||||
@@ -162,12 +190,12 @@ async def to_code(config):
|
|||||||
)
|
)
|
||||||
cg.add(var.add_source(source, error))
|
cg.add(var.add_source(source, error))
|
||||||
elif config[CONF_TYPE] == CONF_LINEAR:
|
elif config[CONF_TYPE] == CONF_LINEAR:
|
||||||
coeffecient = await cg.templatable(
|
coefficient = await cg.templatable(
|
||||||
source_conf[CONF_COEFFECIENT],
|
source_conf[CONF_COEFFICIENT],
|
||||||
[(float, "x")],
|
[(float, "x")],
|
||||||
cg.float_,
|
cg.float_,
|
||||||
)
|
)
|
||||||
cg.add(var.add_source(source, coeffecient))
|
cg.add(var.add_source(source, coefficient))
|
||||||
else:
|
else:
|
||||||
cg.add(var.add_source(source))
|
cg.add(var.add_source(source))
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ EPaperBase = epaper_spi_ns.class_(
|
|||||||
)
|
)
|
||||||
Transform = epaper_spi_ns.enum("Transform")
|
Transform = epaper_spi_ns.enum("Transform")
|
||||||
|
|
||||||
EPaperSpectraE6 = epaper_spi_ns.class_("EPaperSpectraE6", EPaperBase)
|
|
||||||
EPaper7p3InSpectraE6 = epaper_spi_ns.class_("EPaper7p3InSpectraE6", EPaperSpectraE6)
|
|
||||||
|
|
||||||
|
|
||||||
# Import all models dynamically from the models package
|
# Import all models dynamically from the models package
|
||||||
for module_info in pkgutil.iter_modules(models.__path__):
|
for module_info in pkgutil.iter_modules(models.__path__):
|
||||||
importlib.import_module(f".models.{module_info.name}", package=__package__)
|
importlib.import_module(f".models.{module_info.name}", package=__package__)
|
||||||
|
|||||||
231
esphome/components/epaper_spi/epaper_weact_3c.cpp
Normal file
231
esphome/components/epaper_spi/epaper_weact_3c.cpp
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
#include "epaper_weact_3c.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome::epaper_spi {
|
||||||
|
|
||||||
|
static constexpr const char *const TAG = "epaper_weact_3c";
|
||||||
|
|
||||||
|
// SSD1680 3-color display notes:
|
||||||
|
// - Buffer uses 1 bit per pixel, 8 pixels per byte
|
||||||
|
// - Buffer first half (black_offset): Black/White plane (1=black, 0=white)
|
||||||
|
// - Buffer second half (red_offset): Red plane (1=red, 0=no red)
|
||||||
|
// - Total buffer: width * height / 4 bytes = 2 * (width * height / 8)
|
||||||
|
// - For 128x296: 128*296/4 = 9472 bytes total (4736 per color)
|
||||||
|
|
||||||
|
void EPaperWeAct3C::draw_pixel_at(int x, int y, Color color) {
|
||||||
|
if (!this->rotate_coordinates_(x, y))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Calculate position in the 1-bit buffer
|
||||||
|
const uint32_t pos = (x / 8) + (y * this->row_width_);
|
||||||
|
const uint8_t bit = 0x80 >> (x & 0x07);
|
||||||
|
const uint32_t red_offset = this->buffer_length_ / 2u;
|
||||||
|
|
||||||
|
// Use luminance threshold for B/W mapping
|
||||||
|
// Split at halfway point (382 = (255*3)/2)
|
||||||
|
bool is_white = (static_cast<int>(color.r) + color.g + color.b) > 382;
|
||||||
|
|
||||||
|
// Update black/white plane (first half of buffer)
|
||||||
|
if (is_white) {
|
||||||
|
// White pixel - clear bit in black plane
|
||||||
|
this->buffer_[pos] &= ~bit;
|
||||||
|
} else {
|
||||||
|
// Black pixel - set bit in black plane
|
||||||
|
this->buffer_[pos] |= bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update red plane (second half of buffer)
|
||||||
|
// Red if red component is dominant (r > g+b)
|
||||||
|
if (color.r > color.g + color.b) {
|
||||||
|
// Red pixel - set bit in red plane
|
||||||
|
this->buffer_[red_offset + pos] |= bit;
|
||||||
|
} else {
|
||||||
|
// Not red - clear bit in red plane
|
||||||
|
this->buffer_[red_offset + pos] &= ~bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::fill(Color color) {
|
||||||
|
// For 3-color e-paper with 1-bit buffer format:
|
||||||
|
// - Black buffer: 1=black, 0=white
|
||||||
|
// - Red buffer: 1=red, 0=no red
|
||||||
|
// The buffer is stored as two halves: [black plane][red plane]
|
||||||
|
const size_t half_buffer = this->buffer_length_ / 2u;
|
||||||
|
|
||||||
|
// Use luminance threshold for B/W mapping
|
||||||
|
bool is_white = (static_cast<int>(color.r) + color.g + color.b) > 382;
|
||||||
|
bool is_red = color.r > color.g + color.b;
|
||||||
|
|
||||||
|
// Fill both planes
|
||||||
|
if (is_white) {
|
||||||
|
// White - both planes = 0x00
|
||||||
|
this->buffer_.fill(0x00);
|
||||||
|
} else if (is_red) {
|
||||||
|
// Red - black plane = 0x00, red plane = 0xFF
|
||||||
|
for (size_t i = 0; i < half_buffer; i++)
|
||||||
|
this->buffer_[i] = 0x00;
|
||||||
|
for (size_t i = 0; i < half_buffer; i++)
|
||||||
|
this->buffer_[half_buffer + i] = 0xFF;
|
||||||
|
} else {
|
||||||
|
// Black - black plane = 0xFF, red plane = 0x00
|
||||||
|
for (size_t i = 0; i < half_buffer; i++)
|
||||||
|
this->buffer_[i] = 0xFF;
|
||||||
|
for (size_t i = 0; i < half_buffer; i++)
|
||||||
|
this->buffer_[half_buffer + i] = 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::clear() {
|
||||||
|
// Clear buffer to white, just like real paper.
|
||||||
|
this->fill(COLOR_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::set_window_() {
|
||||||
|
// For full screen refresh, we always start from (0,0)
|
||||||
|
// The y_low_/y_high_ values track the dirty region for optimization,
|
||||||
|
// but for display refresh we need to write from the beginning
|
||||||
|
uint16_t x_start = 0;
|
||||||
|
uint16_t x_end = this->width_ - 1;
|
||||||
|
uint16_t y_start = 0;
|
||||||
|
uint16_t y_end = this->height_ - 1; // height = 296 for 2.9" display
|
||||||
|
|
||||||
|
// Set RAM X address boundaries (0x44)
|
||||||
|
// X coordinates are byte-aligned (divided by 8)
|
||||||
|
this->cmd_data(0x44, {(uint8_t) (x_start / 8), (uint8_t) (x_end / 8)});
|
||||||
|
|
||||||
|
// Set RAM Y address boundaries (0x45)
|
||||||
|
// Format: Y start (LSB, MSB), Y end (LSB, MSB)
|
||||||
|
this->cmd_data(0x45, {(uint8_t) y_start, (uint8_t) (y_start >> 8), (uint8_t) (y_end & 0xFF), (uint8_t) (y_end >> 8)});
|
||||||
|
|
||||||
|
// Reset RAM X counter to start (0x4E) - 1 byte
|
||||||
|
this->cmd_data(0x4E, {(uint8_t) (x_start / 8)});
|
||||||
|
|
||||||
|
// Reset RAM Y counter to start (0x4F) - 2 bytes (LSB, MSB)
|
||||||
|
this->cmd_data(0x4F, {(uint8_t) y_start, (uint8_t) (y_start >> 8)});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HOT EPaperWeAct3C::transfer_data() {
|
||||||
|
const uint32_t start_time = millis();
|
||||||
|
const size_t buffer_length = this->buffer_length_;
|
||||||
|
const size_t half_buffer = buffer_length / 2u;
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "transfer_data: buffer_length=%u, half_buffer=%u", buffer_length, half_buffer);
|
||||||
|
|
||||||
|
// Use a local buffer for SPI transfers
|
||||||
|
static constexpr size_t MAX_TRANSFER_SIZE = 128;
|
||||||
|
uint8_t bytes_to_send[MAX_TRANSFER_SIZE];
|
||||||
|
|
||||||
|
// First, send the RED buffer (0x26 = WRITE_COLOR)
|
||||||
|
// The red plane is in the second half of our buffer
|
||||||
|
// NOTE: Must set RAM window first to reset address counters!
|
||||||
|
if (this->current_data_index_ < half_buffer) {
|
||||||
|
if (this->current_data_index_ == 0) {
|
||||||
|
ESP_LOGV(TAG, "transfer_data: sending RED buffer (0x26)");
|
||||||
|
this->set_window_(); // Reset RAM X/Y counters to start position
|
||||||
|
this->command(0x26);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->start_data_();
|
||||||
|
size_t red_offset = half_buffer;
|
||||||
|
while (this->current_data_index_ < half_buffer) {
|
||||||
|
size_t bytes_to_copy = std::min(MAX_TRANSFER_SIZE, half_buffer - this->current_data_index_);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < bytes_to_copy; i++) {
|
||||||
|
bytes_to_send[i] = this->buffer_[red_offset + this->current_data_index_ + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
this->write_array(bytes_to_send, bytes_to_copy);
|
||||||
|
|
||||||
|
this->current_data_index_ += bytes_to_copy;
|
||||||
|
|
||||||
|
if (millis() - start_time > MAX_TRANSFER_TIME) {
|
||||||
|
// Let the main loop run and come back next loop
|
||||||
|
this->disable();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished the red buffer, now send the BLACK buffer (0x24 = WRITE_BLACK)
|
||||||
|
// The black plane is in the first half of our buffer
|
||||||
|
if (this->current_data_index_ < buffer_length) {
|
||||||
|
if (this->current_data_index_ == half_buffer) {
|
||||||
|
ESP_LOGV(TAG, "transfer_data: finished red buffer, sending BLACK buffer (0x24)");
|
||||||
|
|
||||||
|
// Do NOT reset RAM counters here for WeAct displays (Reference implementation behavior)
|
||||||
|
// this->set_window();
|
||||||
|
this->command(0x24);
|
||||||
|
// Continue using current_data_index_, but we need to map it to the start of the buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
this->start_data_();
|
||||||
|
while (this->current_data_index_ < buffer_length) {
|
||||||
|
size_t remaining = buffer_length - this->current_data_index_;
|
||||||
|
size_t bytes_to_copy = std::min(MAX_TRANSFER_SIZE, remaining);
|
||||||
|
|
||||||
|
// Calculate offset into the BLACK buffer (which is at the start of this->buffer_)
|
||||||
|
// current_data_index_ goes from half_buffer to buffer_length
|
||||||
|
size_t buffer_offset = this->current_data_index_ - half_buffer;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < bytes_to_copy; i++) {
|
||||||
|
bytes_to_send[i] = this->buffer_[buffer_offset + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
this->write_array(bytes_to_send, bytes_to_copy);
|
||||||
|
|
||||||
|
this->current_data_index_ += bytes_to_copy;
|
||||||
|
|
||||||
|
if (millis() - start_time > MAX_TRANSFER_TIME) {
|
||||||
|
// Let the main loop run and come back next loop
|
||||||
|
this->disable();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->current_data_index_ = 0;
|
||||||
|
ESP_LOGV(TAG, "transfer_data: completed (red=%u, black=%u bytes)", half_buffer, half_buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::refresh_screen(bool partial) {
|
||||||
|
// SSD1680 refresh sequence:
|
||||||
|
// Reset RAM X/Y address counters to 0,0 so display reads from start
|
||||||
|
// 0x4E: RAM X counter - 1 byte (X / 8)
|
||||||
|
// 0x4F: RAM Y counter - 2 bytes (Y LSB, Y MSB)
|
||||||
|
this->cmd_data(0x4E, {0x00}); // RAM X counter = 0 (1 byte)
|
||||||
|
this->cmd_data(0x4F, {0x00, 0x00}); // RAM Y counter = 0 (2 bytes)
|
||||||
|
|
||||||
|
// Send UPDATE_FULL command (0x22) with display update control parameter
|
||||||
|
// Both WeAct and waveshare reference use 0xF7: {0x22, 0xF7}
|
||||||
|
// 0xF7 = Display update: Load temperature, Load LUT, Enable RAM content
|
||||||
|
this->cmd_data(0x22, {0xF7}); // Command 0x22 with parameter 0xF7
|
||||||
|
this->command(0x20); // Activate display update
|
||||||
|
|
||||||
|
// COMMAND TERMINATE FRAME READ WRITE (required by SSD1680)
|
||||||
|
// Removed 0xFF based on working reference implementation
|
||||||
|
// this->command(0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::power_on() {
|
||||||
|
// Power on sequence - send command to turn on power
|
||||||
|
// According to SSD1680 spec: 0x22, 0xF8 powers on the display
|
||||||
|
this->cmd_data(0x22, {0xF8}); // Power on
|
||||||
|
this->command(0x20); // Activate
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::power_off() {
|
||||||
|
// Power off sequence - send command to turn off power
|
||||||
|
// According to SSD1680 spec: 0x22, 0x83 powers off the display
|
||||||
|
this->cmd_data(0x22, {0x83}); // Power off
|
||||||
|
this->command(0x20); // Activate
|
||||||
|
}
|
||||||
|
|
||||||
|
void EPaperWeAct3C::deep_sleep() {
|
||||||
|
// Deep sleep sequence
|
||||||
|
this->cmd_data(0x10, {0x01}); // Deep sleep mode
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esphome::epaper_spi
|
||||||
39
esphome/components/epaper_spi/epaper_weact_3c.h
Normal file
39
esphome/components/epaper_spi/epaper_weact_3c.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "epaper_spi.h"
|
||||||
|
|
||||||
|
namespace esphome::epaper_spi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WeAct 3-color e-paper displays (SSD1683 controller).
|
||||||
|
* Supports multiple sizes: 2.9" (128x296), 4.2" (400x300), etc.
|
||||||
|
*
|
||||||
|
* Color scheme: Black, White, Red (BWR)
|
||||||
|
* Buffer layout: 1 bit per pixel, separate planes
|
||||||
|
* - Buffer first half: Black/White plane (1=black, 0=white)
|
||||||
|
* - Buffer second half: Red plane (1=red, 0=no red)
|
||||||
|
* - Total buffer: width * height / 4 bytes (2 * width * height / 8)
|
||||||
|
*/
|
||||||
|
class EPaperWeAct3C : public EPaperBase {
|
||||||
|
public:
|
||||||
|
EPaperWeAct3C(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence,
|
||||||
|
size_t init_sequence_length)
|
||||||
|
: EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY) {
|
||||||
|
this->buffer_length_ = this->row_width_ * height * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(Color color) override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void set_window_();
|
||||||
|
void refresh_screen(bool partial) override;
|
||||||
|
void power_on() override;
|
||||||
|
void power_off() override;
|
||||||
|
void deep_sleep() override;
|
||||||
|
void draw_pixel_at(int x, int y, Color color) override;
|
||||||
|
|
||||||
|
bool transfer_data() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::epaper_spi
|
||||||
@@ -84,3 +84,35 @@ jd79660.extend(
|
|||||||
(0xA5, 0x00,),
|
(0xA5, 0x00,),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Waveshare 7.5-H
|
||||||
|
#
|
||||||
|
# Vendor init derived from vendor sample code
|
||||||
|
# <https://github.com/waveshareteam/e-Paper/blob/master/E-paper_Separate_Program/7in5_e-Paper_H/ESP32/EPD_7in5h.cpp>
|
||||||
|
# Compatible MIT license, see esphome/LICENSE file.
|
||||||
|
#
|
||||||
|
# Note: busy pin uses LOW=busy, HIGH=idle. Configure with inverted: true in YAML.
|
||||||
|
#
|
||||||
|
# fmt: off
|
||||||
|
jd79660.extend(
|
||||||
|
"Waveshare-7.5in-H",
|
||||||
|
width=800,
|
||||||
|
height=480,
|
||||||
|
|
||||||
|
initsequence=(
|
||||||
|
(0x00, 0x0F, 0x29,),
|
||||||
|
(0x06, 0x0F, 0x8B, 0x93, 0xA1,),
|
||||||
|
(0x41, 0x00,),
|
||||||
|
(0x50, 0x37,),
|
||||||
|
(0x60, 0x02, 0x02,),
|
||||||
|
(0x61, 800 // 256, 800 % 256, 480 // 256, 480 % 256,), # RES: 800x480
|
||||||
|
(0x62, 0x98, 0x98, 0x98, 0x75, 0xCA, 0xB2, 0x98, 0x7E,),
|
||||||
|
(0x65, 0x00, 0x00, 0x00, 0x00,),
|
||||||
|
(0xE7, 0x1C,),
|
||||||
|
(0xE3, 0x00,),
|
||||||
|
(0xE9, 0x01,),
|
||||||
|
(0x30, 0x08,),
|
||||||
|
# Power On (0x04): Must be early part of init seq = Disabled later!
|
||||||
|
(0x04,),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|||||||
75
esphome/components/epaper_spi/models/weact_bwr.py
Normal file
75
esphome/components/epaper_spi/models/weact_bwr.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
"""WeAct Black/White/Red e-paper displays using SSD1683 controller.
|
||||||
|
|
||||||
|
Supported models:
|
||||||
|
- weact-2.13in-3c: 122x250 pixels (2.13" display)
|
||||||
|
- weact-2.9in-3c: 128x296 pixels (2.9" display)
|
||||||
|
- weact-4.2in-3c: 400x300 pixels (4.2" display)
|
||||||
|
|
||||||
|
These displays use SSD1680 or SSD1683 controller and require a specific initialization
|
||||||
|
sequence. The DRV_OUT_CTL command is calculated from the display height.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import EpaperModel
|
||||||
|
|
||||||
|
|
||||||
|
class WeActBWR(EpaperModel):
|
||||||
|
"""Base EpaperModel class for WeAct Black/White/Red displays using SSD1683 controller."""
|
||||||
|
|
||||||
|
def __init__(self, name, **defaults):
|
||||||
|
super().__init__(name, "EPaperWeAct3C", **defaults)
|
||||||
|
|
||||||
|
def get_init_sequence(self, config):
|
||||||
|
"""Generate initialization sequence for WeAct BWR displays.
|
||||||
|
|
||||||
|
The initialization sequence is based on SSD1680 and SSD1683 controller datasheet
|
||||||
|
and the WeAct display specifications.
|
||||||
|
"""
|
||||||
|
_, height = self.get_dimensions(config)
|
||||||
|
# DRV_OUT_CTL: MSB of (height-1), LSB of (height-1), gate setting (0x00)
|
||||||
|
height_minus_1 = height - 1
|
||||||
|
msb = height_minus_1 >> 8
|
||||||
|
lsb = height_minus_1 & 0xFF
|
||||||
|
return (
|
||||||
|
# Step 1: Software Reset (0x12) - REQUIRED per SSD1680, but works without it as well, so it's commented out for now
|
||||||
|
# (0x12,),
|
||||||
|
# Step 2: Wait 10ms after SWRESET (?) not sure how to implement wht waiting for 10ms after SWRESET, so it's commented out for now
|
||||||
|
# Step 3: DRV_OUT_CTL - driver output control (height-dependent)
|
||||||
|
# Format: (command, LSB, MSB, gate setting)
|
||||||
|
(0x01, lsb, msb, 0x00),
|
||||||
|
# Step 4: DATA_ENTRY - data entry mode (0x03 = decrement Y, increment X)
|
||||||
|
(0x11, 0x03),
|
||||||
|
# Step 5: BORDER_FULL - border waveform control
|
||||||
|
(0x3C, 0x05),
|
||||||
|
# Step 6: TEMP_SENS - internal temperature sensor
|
||||||
|
(0x18, 0x80),
|
||||||
|
# Step 7: DISPLAY_UPDATE - display update control
|
||||||
|
(0x21, 0x00, 0x80),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Model: WeAct 2.9" 3C - 128x296 pixels, SSD1680 controller
|
||||||
|
weact_2p9in3c = WeActBWR(
|
||||||
|
"weact-2.9in-3c",
|
||||||
|
width=128,
|
||||||
|
height=296,
|
||||||
|
data_rate="10MHz",
|
||||||
|
minimum_update_interval="1s",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Model: WeAct 2.13" 3C - 122x250 pixels, SSD1680 controller
|
||||||
|
weact_2p13in3c = WeActBWR(
|
||||||
|
"weact-2.13in-3c",
|
||||||
|
width=122,
|
||||||
|
height=250,
|
||||||
|
data_rate="10MHz",
|
||||||
|
minimum_update_interval="1s",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Model: WeAct 4.2" 3C - 400x300 pixels, SSD1683 controller
|
||||||
|
weact_4p2in3c = WeActBWR(
|
||||||
|
"weact-4.2in-3c",
|
||||||
|
width=400,
|
||||||
|
height=300,
|
||||||
|
data_rate="10MHz",
|
||||||
|
minimum_update_interval="10s",
|
||||||
|
)
|
||||||
@@ -1,8 +1,30 @@
|
|||||||
from esphome.components import esp32
|
from esphome.components import esp32
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome.core import CORE
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
|
|
||||||
|
VARIANTS_NO_RMT = {esp32.VARIANT_ESP32C2, esp32.VARIANT_ESP32C61}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_rmt_not_supported(rmt_only_keys):
|
||||||
|
"""Validate that RMT-only config keys are not used on variants without RMT hardware."""
|
||||||
|
rmt_only_keys = set(rmt_only_keys)
|
||||||
|
|
||||||
|
def _validator(config):
|
||||||
|
if CORE.is_esp32:
|
||||||
|
variant = esp32.get_esp32_variant()
|
||||||
|
if variant in VARIANTS_NO_RMT:
|
||||||
|
unsupported = rmt_only_keys.intersection(config)
|
||||||
|
if unsupported:
|
||||||
|
keys = ", ".join(sorted(f"'{k}'" for k in unsupported))
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{keys} not available on {variant} (no RMT hardware)"
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
return _validator
|
||||||
|
|
||||||
|
|
||||||
def validate_clock_resolution():
|
def validate_clock_resolution():
|
||||||
def _validator(value):
|
def _validator(value):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import logging
|
|||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import esp32, light
|
from esphome.components import esp32, esp32_rmt, light
|
||||||
from esphome.components.const import CONF_USE_PSRAM
|
from esphome.components.const import CONF_USE_PSRAM
|
||||||
from esphome.components.esp32 import include_builtin_idf_component
|
from esphome.components.esp32 import include_builtin_idf_component
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
@@ -71,6 +71,10 @@ CONF_RESET_LOW = "reset_low"
|
|||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
esp32.only_on_variant(
|
||||||
|
unsupported=list(esp32_rmt.VARIANTS_NO_RMT),
|
||||||
|
msg_prefix="ESP32 RMT LED strip",
|
||||||
|
),
|
||||||
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
|
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
|
||||||
|
|||||||
@@ -221,12 +221,17 @@ void Fan::publish_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Random 32-bit value, change this every time the layout of the FanRestoreState struct changes.
|
// Random 32-bit value, change this every time the layout of the FanRestoreState struct changes.
|
||||||
constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
|
constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABB;
|
||||||
optional<FanRestoreState> Fan::restore_state_() {
|
optional<FanRestoreState> Fan::restore_state_() {
|
||||||
FanRestoreState recovered{};
|
FanRestoreState recovered{};
|
||||||
this->rtc_ = this->make_entity_preference<FanRestoreState>(RESTORE_STATE_VERSION);
|
this->rtc_ = this->make_entity_preference<FanRestoreState>(RESTORE_STATE_VERSION);
|
||||||
bool restored = this->rtc_.load(&recovered);
|
bool restored = this->rtc_.load(&recovered);
|
||||||
|
|
||||||
|
if (!restored) {
|
||||||
|
// No valid saved data; ensure preset_mode sentinel is set
|
||||||
|
recovered.preset_mode = FanRestoreState::NO_PRESET;
|
||||||
|
}
|
||||||
|
|
||||||
switch (this->restore_mode_) {
|
switch (this->restore_mode_) {
|
||||||
case FanRestoreMode::NO_RESTORE:
|
case FanRestoreMode::NO_RESTORE:
|
||||||
return {};
|
return {};
|
||||||
@@ -264,6 +269,7 @@ void Fan::save_state_() {
|
|||||||
state.oscillating = this->oscillating;
|
state.oscillating = this->oscillating;
|
||||||
state.speed = this->speed;
|
state.speed = this->speed;
|
||||||
state.direction = this->direction;
|
state.direction = this->direction;
|
||||||
|
state.preset_mode = FanRestoreState::NO_PRESET;
|
||||||
|
|
||||||
if (this->has_preset_mode()) {
|
if (this->has_preset_mode()) {
|
||||||
const auto &preset_modes = traits.supported_preset_modes();
|
const auto &preset_modes = traits.supported_preset_modes();
|
||||||
|
|||||||
@@ -91,11 +91,13 @@ class FanCall {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct FanRestoreState {
|
struct FanRestoreState {
|
||||||
|
static constexpr uint8_t NO_PRESET = UINT8_MAX;
|
||||||
|
|
||||||
bool state;
|
bool state;
|
||||||
int speed;
|
int speed;
|
||||||
bool oscillating;
|
bool oscillating;
|
||||||
FanDirection direction;
|
FanDirection direction;
|
||||||
uint8_t preset_mode;
|
uint8_t preset_mode{NO_PRESET};
|
||||||
|
|
||||||
/// Convert this struct to a fan call that can be performed.
|
/// Convert this struct to a fan call that can be performed.
|
||||||
FanCall to_call(Fan &fan);
|
FanCall to_call(Fan &fan);
|
||||||
|
|||||||
@@ -28,15 +28,15 @@ fan::FanCall HBridgeFan::brake() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HBridgeFan::setup() {
|
void HBridgeFan::setup() {
|
||||||
|
// Construct traits before restore so preset modes can be looked up by index
|
||||||
|
this->traits_ = fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_);
|
||||||
|
this->traits_.set_supported_preset_modes(this->preset_modes_);
|
||||||
|
|
||||||
auto restore = this->restore_state_();
|
auto restore = this->restore_state_();
|
||||||
if (restore.has_value()) {
|
if (restore.has_value()) {
|
||||||
restore->apply(*this);
|
restore->apply(*this);
|
||||||
this->write_state_();
|
this->write_state_();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct traits
|
|
||||||
this->traits_ = fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_);
|
|
||||||
this->traits_.set_supported_preset_modes(this->preset_modes_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HBridgeFan::dump_config() {
|
void HBridgeFan::dump_config() {
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ namespace json {
|
|||||||
// Build an allocator for the JSON Library using the RAMAllocator class
|
// Build an allocator for the JSON Library using the RAMAllocator class
|
||||||
// This is only compiled when PSRAM is enabled
|
// This is only compiled when PSRAM is enabled
|
||||||
struct SpiRamAllocator : ArduinoJson::Allocator {
|
struct SpiRamAllocator : ArduinoJson::Allocator {
|
||||||
void *allocate(size_t size) override { return allocator_.allocate(size); }
|
void *allocate(size_t size) override {
|
||||||
|
RAMAllocator<uint8_t> allocator;
|
||||||
|
return allocator.allocate(size);
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate(void *ptr) override {
|
void deallocate(void *ptr) override {
|
||||||
// ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate.
|
// ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate.
|
||||||
@@ -31,11 +34,9 @@ struct SpiRamAllocator : ArduinoJson::Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *reallocate(void *ptr, size_t new_size) override {
|
void *reallocate(void *ptr, size_t new_size) override {
|
||||||
return allocator_.reallocate(static_cast<uint8_t *>(ptr), new_size);
|
RAMAllocator<uint8_t> allocator;
|
||||||
|
return allocator.reallocate(static_cast<uint8_t *>(ptr), new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
RAMAllocator<uint8_t> allocator_{RAMAllocator<uint8_t>::NONE};
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ class RemoteComponentBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
#if SOC_RMT_SUPPORTED
|
||||||
class RemoteRMTChannel {
|
class RemoteRMTChannel {
|
||||||
public:
|
public:
|
||||||
void set_clock_resolution(uint32_t clock_resolution) { this->clock_resolution_ = clock_resolution; }
|
void set_clock_resolution(uint32_t clock_resolution) { this->clock_resolution_ = clock_resolution; }
|
||||||
@@ -137,7 +139,8 @@ class RemoteRMTChannel {
|
|||||||
uint32_t clock_resolution_{1000000};
|
uint32_t clock_resolution_{1000000};
|
||||||
uint32_t rmt_symbols_;
|
uint32_t rmt_symbols_;
|
||||||
};
|
};
|
||||||
#endif
|
#endif // SOC_RMT_SUPPORTED
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|
||||||
class RemoteTransmitterBase : public RemoteComponentBase {
|
class RemoteTransmitterBase : public RemoteComponentBase {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ RemoteReceiverComponent = remote_receiver_ns.class_(
|
|||||||
def validate_config(config):
|
def validate_config(config):
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
variant = esp32.get_esp32_variant()
|
variant = esp32.get_esp32_variant()
|
||||||
|
if variant in esp32_rmt.VARIANTS_NO_RMT:
|
||||||
|
return config
|
||||||
if variant in (esp32.VARIANT_ESP32, esp32.VARIANT_ESP32S2):
|
if variant in (esp32.VARIANT_ESP32, esp32.VARIANT_ESP32S2):
|
||||||
max_idle = 65535
|
max_idle = 65535
|
||||||
else:
|
else:
|
||||||
@@ -110,6 +112,8 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
|||||||
cv.SplitDefault(
|
cv.SplitDefault(
|
||||||
CONF_BUFFER_SIZE,
|
CONF_BUFFER_SIZE,
|
||||||
esp32="10000b",
|
esp32="10000b",
|
||||||
|
esp32_c2="1000b",
|
||||||
|
esp32_c61="1000b",
|
||||||
esp8266="1000b",
|
esp8266="1000b",
|
||||||
bk72xx="1000b",
|
bk72xx="1000b",
|
||||||
ln882x="1000b",
|
ln882x="1000b",
|
||||||
@@ -131,9 +135,11 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
|||||||
cv.SplitDefault(
|
cv.SplitDefault(
|
||||||
CONF_RMT_SYMBOLS,
|
CONF_RMT_SYMBOLS,
|
||||||
esp32=192,
|
esp32=192,
|
||||||
|
esp32_c2=cv.UNDEFINED,
|
||||||
esp32_c3=96,
|
esp32_c3=96,
|
||||||
esp32_c5=96,
|
esp32_c5=96,
|
||||||
esp32_c6=96,
|
esp32_c6=96,
|
||||||
|
esp32_c61=cv.UNDEFINED,
|
||||||
esp32_h2=96,
|
esp32_h2=96,
|
||||||
esp32_p4=192,
|
esp32_p4=192,
|
||||||
esp32_s2=192,
|
esp32_s2=192,
|
||||||
@@ -145,6 +151,8 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
|||||||
cv.SplitDefault(
|
cv.SplitDefault(
|
||||||
CONF_RECEIVE_SYMBOLS,
|
CONF_RECEIVE_SYMBOLS,
|
||||||
esp32=192,
|
esp32=192,
|
||||||
|
esp32_c2=cv.UNDEFINED,
|
||||||
|
esp32_c61=cv.UNDEFINED,
|
||||||
): cv.All(cv.only_on_esp32, cv.int_range(min=2)),
|
): cv.All(cv.only_on_esp32, cv.int_range(min=2)),
|
||||||
cv.Optional(CONF_USE_DMA): cv.All(
|
cv.Optional(CONF_USE_DMA): cv.All(
|
||||||
esp32.only_on_variant(
|
esp32.only_on_variant(
|
||||||
@@ -152,24 +160,45 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
|||||||
),
|
),
|
||||||
cv.boolean,
|
cv.boolean,
|
||||||
),
|
),
|
||||||
cv.SplitDefault(CONF_CARRIER_DUTY_PERCENT, esp32=100): cv.All(
|
cv.SplitDefault(
|
||||||
|
CONF_CARRIER_DUTY_PERCENT,
|
||||||
|
esp32=100,
|
||||||
|
esp32_c2=cv.UNDEFINED,
|
||||||
|
esp32_c61=cv.UNDEFINED,
|
||||||
|
): cv.All(
|
||||||
cv.only_on_esp32,
|
cv.only_on_esp32,
|
||||||
cv.percentage_int,
|
cv.percentage_int,
|
||||||
cv.Range(min=1, max=100),
|
cv.Range(min=1, max=100),
|
||||||
),
|
),
|
||||||
cv.SplitDefault(CONF_CARRIER_FREQUENCY, esp32="0Hz"): cv.All(
|
cv.SplitDefault(
|
||||||
cv.only_on_esp32, cv.frequency, cv.int_
|
CONF_CARRIER_FREQUENCY,
|
||||||
),
|
esp32="0Hz",
|
||||||
|
esp32_c2=cv.UNDEFINED,
|
||||||
|
esp32_c61=cv.UNDEFINED,
|
||||||
|
): cv.All(cv.only_on_esp32, cv.frequency, cv.int_),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.add_extra(
|
||||||
|
esp32_rmt.validate_rmt_not_supported(
|
||||||
|
[
|
||||||
|
CONF_CLOCK_RESOLUTION,
|
||||||
|
CONF_USE_DMA,
|
||||||
|
CONF_RMT_SYMBOLS,
|
||||||
|
CONF_FILTER_SYMBOLS,
|
||||||
|
CONF_RECEIVE_SYMBOLS,
|
||||||
|
CONF_CARRIER_DUTY_PERCENT,
|
||||||
|
CONF_CARRIER_FREQUENCY,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
.add_extra(validate_config)
|
.add_extra(validate_config)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32 and esp32.get_esp32_variant() not in esp32_rmt.VARIANTS_NO_RMT:
|
||||||
# Re-enable ESP-IDF's RMT driver (excluded by default to save compile time)
|
# Re-enable ESP-IDF's RMT driver (excluded by default to save compile time)
|
||||||
esp32.include_builtin_idf_component("esp_driver_rmt")
|
esp32.include_builtin_idf_component("esp_driver_rmt")
|
||||||
|
|
||||||
@@ -213,6 +242,8 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
|||||||
PlatformFramework.ESP32_IDF,
|
PlatformFramework.ESP32_IDF,
|
||||||
},
|
},
|
||||||
"remote_receiver.cpp": {
|
"remote_receiver.cpp": {
|
||||||
|
PlatformFramework.ESP32_ARDUINO,
|
||||||
|
PlatformFramework.ESP32_IDF,
|
||||||
PlatformFramework.ESP8266_ARDUINO,
|
PlatformFramework.ESP8266_ARDUINO,
|
||||||
PlatformFramework.BK72XX_ARDUINO,
|
PlatformFramework.BK72XX_ARDUINO,
|
||||||
PlatformFramework.RTL87XX_ARDUINO,
|
PlatformFramework.RTL87XX_ARDUINO,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2040)
|
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2040) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
|
||||||
|
|
||||||
namespace esphome::remote_receiver {
|
namespace esphome::remote_receiver {
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,15 @@
|
|||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
#if defined(USE_ESP32)
|
#if defined(USE_ESP32)
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
#if SOC_RMT_SUPPORTED
|
||||||
#include <driver/rmt_rx.h>
|
#include <driver/rmt_rx.h>
|
||||||
#endif
|
#endif // SOC_RMT_SUPPORTED
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|
||||||
namespace esphome::remote_receiver {
|
namespace esphome::remote_receiver {
|
||||||
|
|
||||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
|
||||||
struct RemoteReceiverComponentStore {
|
struct RemoteReceiverComponentStore {
|
||||||
static void gpio_intr(RemoteReceiverComponentStore *arg);
|
static void gpio_intr(RemoteReceiverComponentStore *arg);
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ struct RemoteReceiverComponentStore {
|
|||||||
volatile bool prev_level{false};
|
volatile bool prev_level{false};
|
||||||
volatile bool overflow{false};
|
volatile bool overflow{false};
|
||||||
};
|
};
|
||||||
#elif defined(USE_ESP32)
|
#elif defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
struct RemoteReceiverComponentStore {
|
struct RemoteReceiverComponentStore {
|
||||||
/// Stores RMT symbols and rx done event data
|
/// Stores RMT symbols and rx done event data
|
||||||
volatile uint8_t *buffer{nullptr};
|
volatile uint8_t *buffer{nullptr};
|
||||||
@@ -54,7 +57,7 @@ struct RemoteReceiverComponentStore {
|
|||||||
|
|
||||||
class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
||||||
public Component
|
public Component
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
,
|
,
|
||||||
public remote_base::RemoteRMTChannel
|
public remote_base::RemoteRMTChannel
|
||||||
#endif
|
#endif
|
||||||
@@ -66,7 +69,7 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
void set_filter_symbols(uint32_t filter_symbols) { this->filter_symbols_ = filter_symbols; }
|
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_receive_symbols(uint32_t receive_symbols) { this->receive_symbols_ = receive_symbols; }
|
||||||
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
|
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
|
||||||
@@ -78,7 +81,7 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
|||||||
void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; }
|
void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
void decode_rmt_(rmt_symbol_word_t *item, size_t item_count);
|
void decode_rmt_(rmt_symbol_word_t *item, size_t item_count);
|
||||||
rmt_channel_handle_t channel_{NULL};
|
rmt_channel_handle_t channel_{NULL};
|
||||||
uint32_t filter_symbols_{0};
|
uint32_t filter_symbols_{0};
|
||||||
@@ -94,7 +97,7 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
|||||||
RemoteReceiverComponentStore store_;
|
RemoteReceiverComponentStore store_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
|
||||||
HighFrequencyLoopRequester high_freq_;
|
HighFrequencyLoopRequester high_freq_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
#if SOC_RMT_SUPPORTED
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
#include <esp_clk_tree.h>
|
#include <esp_clk_tree.h>
|
||||||
|
|
||||||
@@ -248,4 +250,5 @@ void RemoteReceiverComponent::decode_rmt_(rmt_symbol_word_t *item, size_t item_c
|
|||||||
|
|
||||||
} // namespace esphome::remote_receiver
|
} // namespace esphome::remote_receiver
|
||||||
|
|
||||||
#endif
|
#endif // SOC_RMT_SUPPORTED
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|||||||
@@ -40,45 +40,66 @@ DigitalWriteAction = remote_transmitter_ns.class_(
|
|||||||
cg.Parented.template(RemoteTransmitterComponent),
|
cg.Parented.template(RemoteTransmitterComponent),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
CONFIG_SCHEMA = (
|
||||||
{
|
cv.Schema(
|
||||||
cv.GenerateID(): cv.declare_id(RemoteTransmitterComponent),
|
{
|
||||||
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
cv.GenerateID(): cv.declare_id(RemoteTransmitterComponent),
|
||||||
cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(
|
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||||
cv.percentage_int, cv.Range(min=1, max=100)
|
cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(
|
||||||
),
|
cv.percentage_int, cv.Range(min=1, max=100)
|
||||||
cv.Optional(CONF_CLOCK_RESOLUTION): cv.All(
|
|
||||||
cv.only_on_esp32,
|
|
||||||
esp32_rmt.validate_clock_resolution(),
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_EOT_LEVEL): cv.All(cv.only_on_esp32, cv.boolean),
|
|
||||||
cv.Optional(CONF_USE_DMA): cv.All(
|
|
||||||
esp32.only_on_variant(
|
|
||||||
supported=[esp32.VARIANT_ESP32P4, esp32.VARIANT_ESP32S3]
|
|
||||||
),
|
),
|
||||||
cv.boolean,
|
cv.Optional(CONF_CLOCK_RESOLUTION): cv.All(
|
||||||
),
|
cv.only_on_esp32,
|
||||||
cv.SplitDefault(
|
esp32_rmt.validate_clock_resolution(),
|
||||||
CONF_RMT_SYMBOLS,
|
),
|
||||||
esp32=64,
|
cv.Optional(CONF_EOT_LEVEL): cv.All(cv.only_on_esp32, cv.boolean),
|
||||||
esp32_c3=48,
|
cv.Optional(CONF_USE_DMA): cv.All(
|
||||||
esp32_c5=48,
|
esp32.only_on_variant(
|
||||||
esp32_c6=48,
|
supported=[esp32.VARIANT_ESP32P4, esp32.VARIANT_ESP32S3]
|
||||||
esp32_h2=48,
|
),
|
||||||
esp32_p4=48,
|
cv.boolean,
|
||||||
esp32_s2=64,
|
),
|
||||||
esp32_s3=48,
|
cv.SplitDefault(
|
||||||
): cv.All(cv.only_on_esp32, cv.int_range(min=2)),
|
CONF_RMT_SYMBOLS,
|
||||||
cv.Optional(CONF_NON_BLOCKING): cv.All(cv.only_on_esp32, cv.boolean),
|
esp32=64,
|
||||||
cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True),
|
esp32_c2=cv.UNDEFINED,
|
||||||
cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True),
|
esp32_c3=48,
|
||||||
}
|
esp32_c5=48,
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
esp32_c6=48,
|
||||||
|
esp32_c61=cv.UNDEFINED,
|
||||||
|
esp32_h2=48,
|
||||||
|
esp32_p4=48,
|
||||||
|
esp32_s2=64,
|
||||||
|
esp32_s3=48,
|
||||||
|
): cv.All(cv.only_on_esp32, cv.int_range(min=2)),
|
||||||
|
cv.Optional(CONF_NON_BLOCKING): cv.All(cv.only_on_esp32, cv.boolean),
|
||||||
|
cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True),
|
||||||
|
cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.add_extra(
|
||||||
|
esp32_rmt.validate_rmt_not_supported(
|
||||||
|
[
|
||||||
|
CONF_CLOCK_RESOLUTION,
|
||||||
|
CONF_EOT_LEVEL,
|
||||||
|
CONF_USE_DMA,
|
||||||
|
CONF_RMT_SYMBOLS,
|
||||||
|
CONF_NON_BLOCKING,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _validate_non_blocking(config):
|
def _validate_non_blocking(config):
|
||||||
if CORE.is_esp32 and CONF_NON_BLOCKING not in config:
|
if (
|
||||||
|
CORE.is_esp32
|
||||||
|
and esp32.get_esp32_variant() not in esp32_rmt.VARIANTS_NO_RMT
|
||||||
|
and CONF_NON_BLOCKING not in config
|
||||||
|
):
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"'non_blocking' is not set for 'remote_transmitter' and will default to 'true'.\n"
|
"'non_blocking' is not set for 'remote_transmitter' and will default to 'true'.\n"
|
||||||
"The default behavior changed in 2025.11.0; previously blocking mode was used.\n"
|
"The default behavior changed in 2025.11.0; previously blocking mode was used.\n"
|
||||||
@@ -111,7 +132,7 @@ async def digital_write_action_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32 and esp32.get_esp32_variant() not in esp32_rmt.VARIANTS_NO_RMT:
|
||||||
# Re-enable ESP-IDF's RMT driver (excluded by default to save compile time)
|
# Re-enable ESP-IDF's RMT driver (excluded by default to save compile time)
|
||||||
esp32.include_builtin_idf_component("esp_driver_rmt")
|
esp32.include_builtin_idf_component("esp_driver_rmt")
|
||||||
|
|
||||||
@@ -155,6 +176,8 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
|||||||
PlatformFramework.ESP32_IDF,
|
PlatformFramework.ESP32_IDF,
|
||||||
},
|
},
|
||||||
"remote_transmitter.cpp": {
|
"remote_transmitter.cpp": {
|
||||||
|
PlatformFramework.ESP32_ARDUINO,
|
||||||
|
PlatformFramework.ESP32_IDF,
|
||||||
PlatformFramework.ESP8266_ARDUINO,
|
PlatformFramework.ESP8266_ARDUINO,
|
||||||
PlatformFramework.BK72XX_ARDUINO,
|
PlatformFramework.BK72XX_ARDUINO,
|
||||||
PlatformFramework.RTL87XX_ARDUINO,
|
PlatformFramework.RTL87XX_ARDUINO,
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::remote_transmitter {
|
||||||
namespace remote_transmitter {
|
|
||||||
|
|
||||||
template<typename... Ts> class DigitalWriteAction : public Action<Ts...>, public Parented<RemoteTransmitterComponent> {
|
template<typename... Ts> class DigitalWriteAction : public Action<Ts...>, public Parented<RemoteTransmitterComponent> {
|
||||||
public:
|
public:
|
||||||
@@ -14,5 +13,4 @@ template<typename... Ts> class DigitalWriteAction : public Action<Ts...>, public
|
|||||||
void play(const Ts &...x) override { this->parent_->digital_write(this->value_.value(x...)); }
|
void play(const Ts &...x) override { this->parent_->digital_write(this->value_.value(x...)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace esphome::remote_transmitter
|
||||||
} // namespace esphome
|
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
|
||||||
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2040)
|
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2040) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::remote_transmitter {
|
||||||
namespace remote_transmitter {
|
|
||||||
|
|
||||||
static const char *const TAG = "remote_transmitter";
|
static const char *const TAG = "remote_transmitter";
|
||||||
|
|
||||||
@@ -105,7 +104,6 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
|||||||
this->complete_trigger_.trigger();
|
this->complete_trigger_.trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace esphome::remote_transmitter
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,13 +6,15 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if defined(USE_ESP32)
|
#if defined(USE_ESP32)
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
#if SOC_RMT_SUPPORTED
|
||||||
#include <driver/rmt_tx.h>
|
#include <driver/rmt_tx.h>
|
||||||
#endif
|
#endif // SOC_RMT_SUPPORTED
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::remote_transmitter {
|
||||||
namespace remote_transmitter {
|
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||||
// IDF version 5.5.1 and above is required because of a bug in
|
// IDF version 5.5.1 and above is required because of a bug in
|
||||||
// the RMT encoder: https://github.com/espressif/esp-idf/issues/17244
|
// the RMT encoder: https://github.com/espressif/esp-idf/issues/17244
|
||||||
@@ -33,7 +35,7 @@ struct RemoteTransmitterComponentStore {
|
|||||||
|
|
||||||
class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||||
public Component
|
public Component
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
,
|
,
|
||||||
public remote_base::RemoteRMTChannel
|
public remote_base::RemoteRMTChannel
|
||||||
#endif
|
#endif
|
||||||
@@ -51,7 +53,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
|||||||
|
|
||||||
void digital_write(bool value);
|
void digital_write(bool value);
|
||||||
|
|
||||||
#if defined(USE_ESP32)
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
|
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
|
||||||
void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; }
|
void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; }
|
||||||
void set_non_blocking(bool non_blocking) { this->non_blocking_ = non_blocking; }
|
void set_non_blocking(bool non_blocking) { this->non_blocking_ = non_blocking; }
|
||||||
@@ -62,7 +64,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void send_internal(uint32_t send_times, uint32_t send_wait) override;
|
void send_internal(uint32_t send_times, uint32_t send_wait) override;
|
||||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
|
||||||
void calculate_on_off_time_(uint32_t carrier_frequency, uint32_t *on_time_period, uint32_t *off_time_period);
|
void calculate_on_off_time_(uint32_t carrier_frequency, uint32_t *on_time_period, uint32_t *off_time_period);
|
||||||
|
|
||||||
void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec);
|
void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec);
|
||||||
@@ -73,7 +75,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
|||||||
uint32_t target_time_;
|
uint32_t target_time_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||||
void configure_rmt_();
|
void configure_rmt_();
|
||||||
void wait_for_rmt_();
|
void wait_for_rmt_();
|
||||||
|
|
||||||
@@ -100,5 +102,4 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
|||||||
Trigger<> complete_trigger_;
|
Trigger<> complete_trigger_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace esphome::remote_transmitter
|
||||||
} // namespace esphome
|
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
#if SOC_RMT_SUPPORTED
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::remote_transmitter {
|
||||||
namespace remote_transmitter {
|
|
||||||
|
|
||||||
static const char *const TAG = "remote_transmitter";
|
static const char *const TAG = "remote_transmitter";
|
||||||
|
|
||||||
@@ -358,7 +359,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace esphome::remote_transmitter
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif
|
#endif // SOC_RMT_SUPPORTED
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ namespace speed {
|
|||||||
static const char *const TAG = "speed.fan";
|
static const char *const TAG = "speed.fan";
|
||||||
|
|
||||||
void SpeedFan::setup() {
|
void SpeedFan::setup() {
|
||||||
|
// Construct traits before restore so preset modes can be looked up by index
|
||||||
|
this->traits_ = fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr, this->speed_count_);
|
||||||
|
this->traits_.set_supported_preset_modes(this->preset_modes_);
|
||||||
|
|
||||||
auto restore = this->restore_state_();
|
auto restore = this->restore_state_();
|
||||||
if (restore.has_value()) {
|
if (restore.has_value()) {
|
||||||
restore->apply(*this);
|
restore->apply(*this);
|
||||||
this->write_state_();
|
this->write_state_();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct traits
|
|
||||||
this->traits_ = fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr, this->speed_count_);
|
|
||||||
this->traits_.set_supported_preset_modes(this->preset_modes_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeedFan::dump_config() { LOG_FAN("", "Speed Fan", this); }
|
void SpeedFan::dump_config() { LOG_FAN("", "Speed Fan", this); }
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ namespace esphome::template_ {
|
|||||||
static const char *const TAG = "template.fan";
|
static const char *const TAG = "template.fan";
|
||||||
|
|
||||||
void TemplateFan::setup() {
|
void TemplateFan::setup() {
|
||||||
|
// Construct traits before restore so preset modes can be looked up by index
|
||||||
|
this->traits_ =
|
||||||
|
fan::FanTraits(this->has_oscillating_, this->speed_count_ > 0, this->has_direction_, this->speed_count_);
|
||||||
|
this->traits_.set_supported_preset_modes(this->preset_modes_);
|
||||||
|
|
||||||
auto restore = this->restore_state_();
|
auto restore = this->restore_state_();
|
||||||
if (restore.has_value()) {
|
if (restore.has_value()) {
|
||||||
restore->apply(*this);
|
restore->apply(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct traits
|
|
||||||
this->traits_ =
|
|
||||||
fan::FanTraits(this->has_oscillating_, this->speed_count_ > 0, this->has_direction_, this->speed_count_);
|
|
||||||
this->traits_.set_supported_preset_modes(this->preset_modes_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TemplateFan::dump_config() { LOG_FAN("", "Template Fan", this); }
|
void TemplateFan::dump_config() { LOG_FAN("", "Template Fan", this); }
|
||||||
|
|||||||
@@ -1673,13 +1673,10 @@ template<class T> class RAMAllocator {
|
|||||||
ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
|
ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
|
||||||
};
|
};
|
||||||
|
|
||||||
RAMAllocator() = default;
|
constexpr RAMAllocator() = default;
|
||||||
RAMAllocator(uint8_t flags) {
|
constexpr RAMAllocator(uint8_t flags)
|
||||||
// default is both external and internal
|
: flags_((flags & (ALLOC_INTERNAL | ALLOC_EXTERNAL)) != 0 ? (flags & (ALLOC_INTERNAL | ALLOC_EXTERNAL))
|
||||||
flags &= ALLOC_INTERNAL | ALLOC_EXTERNAL;
|
: (ALLOC_INTERNAL | ALLOC_EXTERNAL)) {}
|
||||||
if (flags != 0)
|
|
||||||
this->flags_ = flags;
|
|
||||||
}
|
|
||||||
template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
|
template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
|
||||||
|
|
||||||
T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
|
T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ sensor:
|
|||||||
name: Linearly combined temperatures
|
name: Linearly combined temperatures
|
||||||
sources:
|
sources:
|
||||||
- source: template_temperature1
|
- source: template_temperature1
|
||||||
coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;"
|
coefficient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;"
|
||||||
- source: template_temperature2
|
- source: template_temperature2
|
||||||
coeffecient: 1.5
|
coefficient: 1.5
|
||||||
- platform: combination
|
- platform: combination
|
||||||
type: max
|
type: max
|
||||||
name: Max of combined temperatures
|
name: Max of combined temperatures
|
||||||
|
|||||||
@@ -57,6 +57,23 @@ display:
|
|||||||
allow_other_uses: true
|
allow_other_uses: true
|
||||||
number: GPIO4
|
number: GPIO4
|
||||||
|
|
||||||
|
- platform: epaper_spi
|
||||||
|
spi_id: spi_bus
|
||||||
|
model: waveshare-7.5in-H
|
||||||
|
cs_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO5
|
||||||
|
dc_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO17
|
||||||
|
reset_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO16
|
||||||
|
busy_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO4
|
||||||
|
inverted: true
|
||||||
|
|
||||||
- platform: epaper_spi
|
- platform: epaper_spi
|
||||||
model: seeed-reterminal-e1002
|
model: seeed-reterminal-e1002
|
||||||
- platform: epaper_spi
|
- platform: epaper_spi
|
||||||
@@ -64,3 +81,66 @@ display:
|
|||||||
# Override pins to avoid conflict with other display configs
|
# Override pins to avoid conflict with other display configs
|
||||||
busy_pin: 43
|
busy_pin: 43
|
||||||
dc_pin: 42
|
dc_pin: 42
|
||||||
|
|
||||||
|
# WeAct 2.13" 3-color e-paper (122x250, SSD1680)
|
||||||
|
- platform: epaper_spi
|
||||||
|
spi_id: spi_bus
|
||||||
|
model: weact-2.13in-3c
|
||||||
|
cs_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO5
|
||||||
|
dc_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO17
|
||||||
|
reset_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO16
|
||||||
|
busy_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO4
|
||||||
|
lambda: |-
|
||||||
|
it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE);
|
||||||
|
it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color::BLACK);
|
||||||
|
it.circle(it.get_width() / 2, it.get_height() / 2, 15, Color(255, 0, 0));
|
||||||
|
|
||||||
|
# WeAct 2.9" 3-color e-paper (128x296, SSD1683)
|
||||||
|
- platform: epaper_spi
|
||||||
|
spi_id: spi_bus
|
||||||
|
model: weact-2.9in-3c
|
||||||
|
cs_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO5
|
||||||
|
dc_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO17
|
||||||
|
reset_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO16
|
||||||
|
busy_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO4
|
||||||
|
lambda: |-
|
||||||
|
it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE);
|
||||||
|
it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color::BLACK);
|
||||||
|
it.circle(it.get_width() / 2, it.get_height() / 2, 15, Color(255, 0, 0));
|
||||||
|
|
||||||
|
# WeAct 4.2" 3-color e-paper (400x300, SSD1683)
|
||||||
|
- platform: epaper_spi
|
||||||
|
spi_id: spi_bus
|
||||||
|
model: weact-4.2in-3c
|
||||||
|
cs_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO5
|
||||||
|
dc_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO17
|
||||||
|
reset_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO16
|
||||||
|
busy_pin:
|
||||||
|
allow_other_uses: true
|
||||||
|
number: GPIO4
|
||||||
|
lambda: |-
|
||||||
|
it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE);
|
||||||
|
it.circle(it.get_width() / 2, it.get_height() / 2, 30, Color::BLACK);
|
||||||
|
it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color(255, 0, 0));
|
||||||
|
|||||||
12
tests/components/remote_receiver/test.esp32-c2-idf.yaml
Normal file
12
tests/components/remote_receiver/test.esp32-c2-idf.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
remote_receiver:
|
||||||
|
id: rcvr
|
||||||
|
pin: GPIO2
|
||||||
|
dump: all
|
||||||
|
<<: !include common-actions.yaml
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: remote_receiver
|
||||||
|
name: Panasonic Remote Input
|
||||||
|
panasonic:
|
||||||
|
address: 0x4004
|
||||||
|
command: 0x100BCBD
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
remote_transmitter:
|
||||||
|
id: xmitr
|
||||||
|
pin: GPIO2
|
||||||
|
carrier_duty_percent: 50%
|
||||||
|
|
||||||
|
packages:
|
||||||
|
buttons: !include common-buttons.yaml
|
||||||
Reference in New Issue
Block a user