From 56044ed0a53885f801d2e327fd1c929e6eeb8d8d Mon Sep 17 00:00:00 2001 From: Nisarg Jhaveri Date: Thu, 20 Jun 2024 01:15:47 +0530 Subject: [PATCH] [waveshare_epaper] Add support for 4.26inch display --- .../components/waveshare_epaper/display.py | 9 +- .../waveshare_epaper/waveshare_4_24in.cpp | 170 ++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.cpp | 9 + .../waveshare_epaper/waveshare_epaper.h | 28 +++ .../waveshare_epaper/test.esp32-ard.yaml | 18 ++ 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 esphome/components/waveshare_epaper/waveshare_4_24in.cpp diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 4d3965449f..535c1868b4 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -27,6 +27,9 @@ WaveshareEPaperBWR = waveshare_epaper_ns.class_( WaveshareEPaperTypeA = waveshare_epaper_ns.class_( "WaveshareEPaperTypeA", WaveshareEPaper ) +WaveshareEPaper4P26In = waveshare_epaper_ns.class_( + "WaveshareEPaper4P26In", WaveshareEPaper +) WaveshareEPaper2P7In = waveshare_epaper_ns.class_( "WaveshareEPaper2P7In", WaveshareEPaper ) @@ -113,6 +116,7 @@ MODELS = { "2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74), "2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), + "4.26in": ("a", WaveshareEPaper4P26In), "gdew029t5": ("c", GDEW029T5), "2.70in": ("b", WaveshareEPaper2P7In), "2.70in-b": ("b", WaveshareEPaper2P7InB), @@ -190,7 +194,10 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): model_type, model = MODELS[config[CONF_MODEL]] - if model_type == "a": + if config[CONF_MODEL] == "4.26in": + rhs = model.new() + var = cg.Pvariable(config[CONF_ID], rhs, model) + elif model_type == "a": rhs = WaveshareEPaperTypeA.new(model) var = cg.Pvariable(config[CONF_ID], rhs, WaveshareEPaperTypeA) elif model_type in ("b", "c"): diff --git a/esphome/components/waveshare_epaper/waveshare_4_24in.cpp b/esphome/components/waveshare_epaper/waveshare_4_24in.cpp new file mode 100644 index 0000000000..12908496ce --- /dev/null +++ b/esphome/components/waveshare_epaper/waveshare_4_24in.cpp @@ -0,0 +1,170 @@ +#include "waveshare_epaper.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include + +namespace esphome { +namespace waveshare_epaper { + +static const char *const TAG = "waveshare_epaper_4.26in"; + +WaveshareEPaper4P26In::WaveshareEPaper4P26In() : WaveshareEPaper() { reset_duration_ = 20; } + +void WaveshareEPaper4P26In::init_display_async_(bool fast_update, const std::function f) { + // Reset the display + this->reset_(); + wait_until_idle_async_([this, fast_update, f] { + this->command(0x12); // SWRESET + + wait_until_idle_async_([this, fast_update, f] { + // Use the internal temperature sensor + this->command(0x18); + this->data(0x80); + + // Set soft start + this->command(0x0C); + this->data(0xAE); + this->data(0xC7); + this->data(0xC3); + this->data(0xC0); + this->data(0x80); + + // Drive output control + this->command(0x01); + this->data((this->get_height_internal() - 1) % 256); // Y + this->data((this->get_height_internal() - 1) / 256); // Y + this->data(0x02); + + // // Border settings + // this->command(0x3C); + // this->data(0x01); + + // Data entry mode setting + this->command(0x11); + this->data(0x01); // x increase, y decrease : as in demo code + + // Set window, this is always the full screen for now + this->command(0x44); // SET_RAM_X_ADDRESS_START_END_POSITION + this->data(0x00); + this->data(0x00); + this->data((this->get_width_internal() - 1) & 0xFF); + this->data(((this->get_width_internal() - 1) >> 8) & 0x03); + + this->command(0x45); // SET_RAM_Y_ADDRESS_START_END_POSITION + this->data((this->get_height_internal() - 1) & 0xFF); + this->data(((this->get_height_internal() - 1) >> 8) & 0x03); + this->data(0x00); + this->data(0x00); + + // Set cursor + this->command(0x4E); // SET_RAM_X_ADDRESS_COUNTER + this->data(0x00); + this->data(0x00); + + this->command(0x4F); // SET_RAM_Y_ADDRESS_COUNTER + this->data(0x00); + this->data(0x00); + + if (fast_update) { + this->wait_until_idle_async_([this, f] { + this->command(0x1A); + this->data(0x5A); + + this->command(0x22); + this->data(0x91); + this->command(0x20); + + this->wait_until_idle_async_(std::move(f)); + }); + } else { + this->wait_until_idle_async_(std::move(f)); + } + }); + }); +} + +void WaveshareEPaper4P26In::initialize() {} + +void WaveshareEPaper4P26In::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 4.26in"); + ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + +void HOT WaveshareEPaper4P26In::display() { + if (this->is_busy_) { + ESP_LOGE(TAG, "Skipping display, display is busy"); + return; + } + + this->is_busy_ = true; + + bool full_update = this->at_update_ == 0; + bool prev_full_update = this->at_update_ == 1; + + if (this->full_update_every_ >= 1) { + this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; + } + + ESP_LOGI(TAG, "Wake up the display"); + this->init_display_async_(!full_update, [this, full_update] { + // Border settings + this->command(0x3C); + // this->data(full_update ? 0x01 : 0x80); + this->data(0x01); + + // Write RAM + this->command(0x24); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + if (full_update) { + // Write base image again on full refresh + this->command(0x26); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + } + + // Display Update Control + this->command(0x22); + this->data(full_update ? 0xF7 : 0xC7); // 0xF7 = Full update, 0xC7 = Fast update, 0xFF = Partial update + + // Activate Display Update Sequence + this->command(0x20); + + this->wait_until_idle_async_([this] { + ESP_LOGI(TAG, "Display updated, set it back to deep sleep"); + this->deep_sleep(); + this->is_busy_ = false; + }); + + this->status_clear_warning(); + }); +} + +void WaveshareEPaper4P26In::deep_sleep() { + // Set deep sleep mode + this->command(0x10); + this->data(0x01); + + delay(50); +} + +int WaveshareEPaper4P26In::get_width_internal() { return 800; } + +int WaveshareEPaper4P26In::get_height_internal() { return 480; } + +uint32_t WaveshareEPaper4P26In::idle_timeout_() { return 4000; } + +void WaveshareEPaper4P26In::set_full_update_every(uint32_t full_update_every) { + this->full_update_every_ = full_update_every; +} + +} // namespace waveshare_epaper +} // namespace esphome diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 24df428e6f..ec056713c7 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -163,6 +163,15 @@ bool WaveshareEPaperBase::wait_until_idle_() { } return true; } + +void WaveshareEPaperBase::wait_until_idle_async_(const std::function f) { + if (this->busy_pin_ == nullptr || !this->busy_pin_->digital_read()) { + return f(); + } + + this->set_timeout(20, [this, f] { this->wait_until_idle_async_(std::move(f)); }); +} + void WaveshareEPaperBase::update() { this->do_update_(); this->display(); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 7572982a20..f4e1f8d434 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -36,6 +36,7 @@ class WaveshareEPaperBase : public display::DisplayBuffer, protected: bool wait_until_idle_(); + void wait_until_idle_async_(const std::function f); void setup_pins_(); @@ -154,6 +155,33 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { bool deep_sleep_between_updates_{false}; }; +class WaveshareEPaper4P26In : public WaveshareEPaper { + public: + WaveshareEPaper4P26In(); + + void initialize() override; + void display() override; + void deep_sleep() override; + + void dump_config() override; + void set_full_update_every(uint32_t full_update_every); + + protected: + void init_display_async_(bool fast_update, const std::function f); + + int get_width_internal() override; + int get_height_internal() override; + + uint32_t idle_timeout_() override; + + private: + bool is_busy_ = false; + uint32_t full_update_every_{30}; + uint32_t at_update_{0}; + + bool deep_sleep_between_updates_{true}; +}; + enum WaveshareEPaperTypeBModel { WAVESHARE_EPAPER_2_7_IN = 0, WAVESHARE_EPAPER_2_7_IN_B, diff --git a/tests/components/waveshare_epaper/test.esp32-ard.yaml b/tests/components/waveshare_epaper/test.esp32-ard.yaml index 2f06c5c51b..a0d28ac7c7 100644 --- a/tests/components/waveshare_epaper/test.esp32-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-ard.yaml @@ -188,3 +188,21 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 4.26in + spi_id: spi_id_1 + cs_pin: + allow_other_uses: true + number: GPIO25 + dc_pin: + allow_other_uses: true + number: GPIO26 + busy_pin: + allow_other_uses: true + number: GPIO27 + reset_pin: + allow_other_uses: true + number: GPIO32 + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height());