1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 15:41:52 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Jesse Hills
b6e0502203 idf only 2025-09-13 10:12:49 +12:00
Jesse Hills
6d427bec49 [epdiy] ePaper display(s) 2025-09-13 10:01:42 +12:00
6 changed files with 250 additions and 0 deletions

View File

@@ -139,6 +139,7 @@ esphome/components/ens160_base/* @latonita @vincentscode
esphome/components/ens160_i2c/* @latonita
esphome/components/ens160_spi/* @latonita
esphome/components/ens210/* @itn3rd77
esphome/components/epdiy/* @jesserockz
esphome/components/es7210/* @kahrendt
esphome/components/es7243e/* @kbx81
esphome/components/es8156/* @kbx81

View File

View File

@@ -0,0 +1,107 @@
import esphome.codegen as cg
from esphome.components import display, esp32
import esphome.config_validation as cv
from esphome.const import (
CONF_FULL_UPDATE_EVERY,
CONF_ID,
CONF_LAMBDA,
CONF_MODEL,
CONF_PAGES,
)
from esphome.cpp_generator import MockObj
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["esp32", "psram"]
CONF_POWER_OFF_DELAY_ENABLED = "power_off_delay_enabled"
epdiy_ns = cg.esphome_ns.namespace("epdiy")
EPDiyDisplay = epdiy_ns.class_("EPDiyDisplay", display.Display)
class EpdBoardDefinition(MockObj):
def __str__(self):
return f"&{self.base}"
class EpdDisplay_t(MockObj):
def __str__(self):
return f"&{self.base}"
EpdInitOptions = cg.global_ns.enum("EpdInitOptions")
class Model:
def __init__(
self,
*,
board_definition: MockObj,
display_t: MockObj,
init_options: MockObj,
width: int,
height: int,
vcom_mv: int = 0,
):
self.board_definition = board_definition
self.display_t = display_t
self.init_options = init_options
self.width = width
self.height = height
self.vcom_mv = vcom_mv
MODELS: dict[str, Model] = {
"lilygo_t5_4.7": Model(
board_definition=EpdBoardDefinition("epd_board_lilygo_t5_47"),
display_t=EpdDisplay_t("ED047TC2"),
init_options=(EpdInitOptions.EPD_LUT_64K, EpdInitOptions.EPD_FEED_QUEUE_8),
width=960,
height=540,
),
}
CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(EPDiyDisplay),
cv.Required(CONF_MODEL): cv.one_of(*MODELS.keys()),
cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
cv.Optional(CONF_POWER_OFF_DELAY_ENABLED, default=False): cv.boolean,
}
).extend(cv.polling_component_schema("60s")),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
cv.only_with_esp_idf, # When trying to add library via platformio it breaks, using as an idf component works fine
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await display.register_display(var, config)
model = MODELS[config[CONF_MODEL]]
cg.add(
var.set_model_details(
model.board_definition,
model.display_t,
cg.RawExpression(
f"static_cast<EpdInitOptions>({'|'.join(str(o) for o in model.init_options)})"
),
model.vcom_mv,
)
)
if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(
config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))
cg.add(var.set_power_off_delay_enabled(config[CONF_POWER_OFF_DELAY_ENABLED]))
esp32.add_idf_component(
name="vroland/epdiy",
repo="https://github.com/vroland/epdiy",
ref="c61e9e923ce2418150d54f88cea5d196cdc40c54",
)

View File

@@ -0,0 +1,76 @@
#include "epdiy_display.h"
#include "esphome/core/application.h"
#ifdef USE_ESP_IDF
#include "esphome/core/log.h"
namespace esphome::epdiy {
static const char *const TAG = "epdiy";
static constexpr uint8_t TEMPERATURE = 23; // default temperature for e-paper displays
float EPDiyDisplay::get_setup_priority() const { return esphome::setup_priority::LATE; }
void EPDiyDisplay::setup() {
epd_init(this->board_definition_, this->display_t_, this->init_options_);
if (this->vcom_mv_ != 0) {
epd_set_vcom(this->vcom_mv_);
}
this->state_ = epd_hl_init(nullptr);
this->framebuffer_ = epd_hl_get_framebuffer(&this->state_);
}
void EPDiyDisplay::update() {
this->do_update_();
this->defer([this]() { this->flush_screen_changes_(); });
}
void EPDiyDisplay::fill(Color color) {
if (color == display::COLOR_OFF) {
memset(this->framebuffer_, 0xFF, this->get_buffer_length());
epd_poweron();
epd_hl_update_screen(&this->state_, MODE_GC16, TEMPERATURE);
epd_clear();
epd_poweroff();
App.feed_wdt();
} else {
Display::fill(color);
}
}
void EPDiyDisplay::flush_screen_changes_() {
epd_poweron();
epd_hl_update_screen(&this->state_, MODE_GC16, TEMPERATURE);
memset(this->state_.back_fb, 0xFF, this->get_buffer_length());
uint16_t delay = 0;
if (this->power_off_delay_enabled_) {
delay = 700;
}
this->set_timeout("poweroff", delay, []() { epd_poweroff(); });
}
void EPDiyDisplay::on_shutdown() {
epd_poweroff();
epd_deinit();
}
void HOT EPDiyDisplay::draw_pixel_at(int x, int y, Color color) {
if (color.red == 255 && color.green == 255 && color.blue == 255) {
epd_draw_pixel(x, y, 0, this->framebuffer_);
} else {
int col = (0.2126 * color.red) + (0.7152 * color.green) + (0.0722 * color.blue);
int cl = 255 - col;
epd_draw_pixel(x, y, cl, this->framebuffer_);
}
}
} // namespace esphome::epdiy
#endif // USE_ESP_IDF

View File

@@ -0,0 +1,63 @@
#pragma once
#ifdef USE_ESP_IDF
#include "esphome/core/component.h"
#include "esphome/components/display/display_buffer.h"
#include "esphome/components/display/display_color_utils.h"
#include "esphome/core/hal.h"
#include "esphome/core/version.h"
#include "epd_display.h"
#include "epd_highlevel.h"
namespace esphome::epdiy {
class EPDiyDisplay : public display::Display {
public:
float get_setup_priority() const override;
void setup() override;
void update() override;
void on_shutdown() override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_GRAYSCALE; }
int get_width_internal() override { return this->display_t_->width; };
int get_height_internal() override { return this->display_t_->height; };
size_t get_buffer_length() const { return this->display_t_->width / 2 * this->display_t_->height; }
void set_power_off_delay_enabled(bool power_off_delay_enabled) {
this->power_off_delay_enabled_ = power_off_delay_enabled;
}
void set_model_details(const EpdBoardDefinition *board_definition, const EpdDisplay_t *display_t,
enum EpdInitOptions init_options, uint16_t vcom) {
this->board_definition_ = board_definition;
this->display_t_ = display_t;
this->init_options_ = init_options;
this->vcom_mv_ = vcom;
}
void fill(Color color) override;
void draw_pixel_at(int x, int y, Color color) override;
protected:
void flush_screen_changes_();
EpdiyHighlevelState state_;
uint8_t *framebuffer_;
const EpdBoardDefinition *board_definition_;
const EpdDisplay_t *display_t_;
enum EpdInitOptions init_options_;
uint16_t vcom_mv_;
bool power_off_delay_enabled_;
};
} // namespace esphome::epdiy
#endif // USE_ESP_IDF

View File

@@ -19,3 +19,6 @@ dependencies:
- if: "target in [esp32h2, esp32p4]"
zorxx/multipart-parser:
version: 1.0.1
vroland/epdiy:
git: https://github.com/vroland/epdiy.git
version: c61e9e923ce2418150d54f88cea5d196cdc40c54