From f516a441fd73d58c9df284ca6e32f5f2a35a2fa3 Mon Sep 17 00:00:00 2001 From: Michael Davidson Date: Thu, 31 Oct 2024 22:19:50 +1100 Subject: [PATCH] Commit of Grayscale support as provided by niceboygithub. See: https://github.com/esphome/esphome/pull/6209#issuecomment-2388682338 --- .../components/waveshare_epaper/display.py | 15 + .../waveshare_epaper/waveshare_42v2.cpp | 313 ++++++++++++++++-- .../waveshare_epaper/waveshare_42v2.h | 22 +- 3 files changed, 311 insertions(+), 39 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index c4eea2975f..d79107d2eb 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -7,6 +7,7 @@ from esphome.const import ( CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, CONF_ID, + CONF_INITIAL_MODE, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, @@ -168,6 +169,16 @@ def validate_reset_pin_required(config): ) return config +def validate_grayscale4_supported(config): + print(config[CONF_MODEL]) + if CONF_INITIAL_MODE in config: + if config[CONF_MODEL] in ['4.20in-v2']: + return config + raise cv.Invalid( + f"'{CONF_INITIAL_MODE}' is supported for model {config[CONF_MODEL]}" + ) + return config + CONFIG_SCHEMA = cv.All( display.FULL_DISPLAY_SCHEMA.extend( @@ -182,12 +193,14 @@ CONFIG_SCHEMA = cv.All( cv.positive_time_period_milliseconds, cv.Range(max=core.TimePeriod(milliseconds=500)), ), + cv.Optional(CONF_INITIAL_MODE): cv.int_range(min=1, max=3), } ) .extend(cv.polling_component_schema("1s")) .extend(spi.spi_device_schema()), validate_full_update_every_only_types_ac, validate_reset_pin_required, + validate_grayscale4_supported, cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) @@ -224,3 +237,5 @@ async def to_code(config): cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY])) if CONF_RESET_DURATION in config: cg.add(var.set_reset_duration(config[CONF_RESET_DURATION])) + if CONF_INITIAL_MODE in config: + cg.add(var.set_initial_mode(config[CONF_INITIAL_MODE])) diff --git a/esphome/components/waveshare_epaper/waveshare_42v2.cpp b/esphome/components/waveshare_epaper/waveshare_42v2.cpp index fc97581c3c..f5a0f32b78 100644 --- a/esphome/components/waveshare_epaper/waveshare_42v2.cpp +++ b/esphome/components/waveshare_epaper/waveshare_42v2.cpp @@ -7,35 +7,151 @@ namespace waveshare_epaper { static const char *const TAG = "waveshare_4.2v2"; -void WaveshareEPaper4P2InV2::display() { - ESP_LOGD(TAG, "Performing full update"); - this->full_update_(); +static const uint8_t LUT_ALL[233]={ + 0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01, + 0x05, 0x0A, 0x01, 0x0A, 0x01, 0x01, 0x01, + 0x05, 0x08, 0x03, 0x02, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x03, 0x82, 0x84, 0x01, 0x01, + 0x01, 0x84, 0x84, 0x82, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x0A, 0x1B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x83, 0x82, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x8A, 0x1B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x83, 0x02, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x8A, 0x9B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x03, 0x42, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x42, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x07, 0x17, 0x41, 0xA8, + 0x32, 0x30, +}; + +uint32_t WaveshareEPaper4P2InV2::get_buffer_length_() { + if (this->initial_mode_ == MODE_GRAYSCALE4) { + // black and gray buffer + return this->get_width_controller() * this->get_height_internal() / 4u; + } else { + // just a black buffer + return this->get_width_controller() * this->get_height_internal() / 8u; + } } -void WaveshareEPaper4P2InV2::full_update_() { +void WaveshareEPaper4P2InV2::display() { + if (this->is_busy_ || (this->busy_pin_ != nullptr && this->busy_pin_->digital_read())) + return; + this->is_busy_ = true; + if (this->initial_mode_ == MODE_GRAYSCALE4) { + ESP_LOGD(TAG, "Performing grayscale4 update"); + this->update_(MODE_GRAYSCALE4); + } else if (this->initial_mode_ == MODE_FAST) { + ESP_LOGD(TAG, "Performing fast update"); + this->update_(MODE_FAST); + } else { + const bool partial = this->at_update_ != 0; + this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; + if (partial) { + ESP_LOGD(TAG, "Performing partial update"); + this->update_(MODE_PARTIAL); + } else { + ESP_LOGD(TAG, "Performing full update"); + //this->init_(); + this->update_(MODE_FULL); + } + } +} + +void WaveshareEPaper4P2InV2::update_(TurnOnMode mode) { + const uint32_t buf_half_len = this->get_buffer_length_() / 2u; + this->reset_(); - this->wait_until_idle_(); + if (!this->wait_until_idle_()) { + ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?"); + } + + if (mode == MODE_PARTIAL) { + this->command(0x21); + this->data(0x00); + this->data(0x00); + + this->command(0x3C); + this->data(0x80); + +// this->set_window_(0, 0, this->get_width_internal() - 1, this->get_height_internal() - 1); +// this->set_cursor_(0, 0); + } else if (mode == MODE_FULL) { + this->command(0x21); + this->data(0x40); + this->data(0x00); + + this->command(0x3C); + this->data(0x05); + } this->command(0x24); this->start_data_(); - this->write_array(this->buffer_, this->get_buffer_length_()); + if (mode == MODE_GRAYSCALE4) + this->write_array(this->buffer_, buf_half_len); + else + this->write_array(this->buffer_, this->get_buffer_length_()); this->end_data_(); - this->command(0x26); - this->start_data_(); - this->write_array(this->buffer_, this->get_buffer_length_()); - this->end_data_(); + // new data + if ((mode == MODE_FULL) || (mode == MODE_FAST)) { + this->command(0x26); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + } else if (mode == MODE_GRAYSCALE4) { + this->command(0x26); + this->start_data_(); + this->write_array(this->buffer_ + buf_half_len, buf_half_len); + this->end_data_(); + } - this->turn_on_display_full_(); + this->turn_on_display_(mode); this->deep_sleep(); + this->is_busy_ = false; } -void WaveshareEPaper4P2InV2::turn_on_display_full_() { +void WaveshareEPaper4P2InV2::turn_on_display_(TurnOnMode mode) { this->command(0x22); - this->data(0xc7); + switch (mode) { + case MODE_GRAYSCALE4: + this->data(0xcf); + break; + case MODE_PARTIAL: + this->data(0xff); + break; + case MODE_FAST: + this->data(0xc7); + break; + case MODE_FULL: + default: + this->data(0xf7); + break; + } this->command(0x20); + // possible timeout. this->wait_until_idle_(); } @@ -52,17 +168,23 @@ void WaveshareEPaper4P2InV2::set_window_(uint16_t x, uint16_t y, uint16_t x2, ui } void WaveshareEPaper4P2InV2::clear_() { - uint8_t *buffer = (uint8_t *) calloc(this->get_buffer_length_(), sizeof(uint8_t)); - memset(buffer, 0xff, this->get_buffer_length_()); + uint32_t bufflen; + if (this->initial_mode_ == MODE_GRAYSCALE4) { + bufflen = this->get_buffer_length_() / 2u; + } else { + bufflen = this->get_buffer_length_(); + } + uint8_t *buffer = (uint8_t *) calloc(bufflen, sizeof(uint8_t)); + memset(buffer, 0xff, bufflen); this->command(0x24); this->start_data_(); - this->write_array(buffer, this->get_buffer_length_()); + this->write_array(buffer, bufflen); this->end_data_(); this->command(0x26); this->start_data_(); - this->write_array(buffer, this->get_buffer_length_()); + this->write_array(buffer, bufflen); this->end_data_(); free(buffer); @@ -77,7 +199,28 @@ void WaveshareEPaper4P2InV2::set_cursor_(uint16_t x, uint16_t y) { this->data((y >> 8) & 0xFF); } -void WaveshareEPaper4P2InV2::fast_initialize_() { +void WaveshareEPaper4P2InV2::write_lut_() { + this->command(0x32); + this->start_data_(); + this->write_array(LUT_ALL, 227); + this->end_data_(); + + this->command(0x3F); + this->data(LUT_ALL[227]); + + this->command(0x03); + this->data(LUT_ALL[228]); + + this->command(0x04); + this->data(LUT_ALL[229]); + this->data(LUT_ALL[230]); + this->data(LUT_ALL[231]); + + this->command(0x2c); + this->data(LUT_ALL[232]); +} + +void WaveshareEPaper4P2InV2::initialize_internal_(TurnOnMode mode) { #define MODE_1_SECOND 1 #define MODE_1_5_SECOND 0 @@ -89,32 +232,51 @@ void WaveshareEPaper4P2InV2::fast_initialize_() { if (!this->wait_until_idle_()) { ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?"); } + this->command(0x21); - this->data(0x40); + if (mode == MODE_GRAYSCALE4) + this->data(0x00); + else + this->data(0x40); this->data(0x00); - this->command(0x3C); - this->data(0x05); + this->command(0x3C); + if (mode == MODE_GRAYSCALE4) + this->data(0x03); + else + this->data(0x05); + + if (mode == MODE_FAST) { #if MODE_1_5_SECOND - // 1.5s - this->command(0x1A); // Write to temperature register - this->data(0x6E); + // 1.5s + this->command(0x1A); // Write to temperature register + this->data(0x6E); #endif #if MODE_1_SECOND - // 1s - this->command(0x1A); // Write to temperature register - this->data(0x5A); + // 1s + this->command(0x1A); // Write to temperature register + this->data(0x5A); #endif - this->command(0x22); // Load temperature value - this->data(0x91); - this->command(0x20); - if (!this->wait_until_idle_()) { - ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?"); + this->command(0x22); // Load temperature value + this->data(0x91); + this->command(0x20); + if (!this->wait_until_idle_()) { + ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?"); + } + } else if (mode == MODE_GRAYSCALE4) { + this->command(0x0C); + this->data(0x8B); + this->data(0x9C); + this->data(0xA4); + this->data(0x0F); + + this->write_lut_(); } - this->command(0x11); // data entry mode - this->data(0x03); // X-mode + + this->command(0x11); // data entry mode + this->data(0x03); // X-mode this->set_window_(0, 0, this->get_width_internal() - 1, this->get_height_internal() - 1); this->set_cursor_(0, 0); @@ -124,11 +286,22 @@ void WaveshareEPaper4P2InV2::fast_initialize_() { } this->clear_(); - this->turn_on_display_full_(); + this->turn_on_display_(mode); } void WaveshareEPaper4P2InV2::initialize() { - this->fast_initialize_(); + if (this->initial_mode_ == MODE_PARTIAL) + this->initialize_internal_(MODE_FULL); + else + this->initialize_internal_(this->initial_mode_); +} + +uint32_t WaveshareEPaper4P2InV2::idle_timeout_() { + if (this->initial_mode_ == MODE_GRAYSCALE4) { + return 1000; + } else { + return 100; + } } void WaveshareEPaper4P2InV2::deep_sleep() { @@ -154,5 +327,73 @@ void WaveshareEPaper4P2InV2::reset_() { delay(100); } +void HOT WaveshareEPaper4P2InV2::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) + return; + const uint32_t pos = (x + y * this->get_width_controller()) / 8u; + const uint8_t subpos = x & 0x07; + const uint32_t buf_half_len = this->get_buffer_length_() / 2u; + + if (!color.is_on()) { + if (this->initial_mode_ == MODE_GRAYSCALE4) { + // flip logic + this->buffer_[pos] &= ~(0x80 >> subpos); + this->buffer_[pos + buf_half_len] &= ~(0x80 >> subpos); + } else { + this->buffer_[pos] &= ~(0x80 >> subpos); + } + } else { + if (this->initial_mode_ == MODE_GRAYSCALE4) { + +/****Color display description**** + white gray1 gray2 black +0x10| 01 01 00 00 +0x13| 01 00 01 00 +*********************************/ + + if (((color.red > 0) || (color.green > 0) || (color.blue > 0))) { + // draw gray pixels + if ((color.red >= 0xc0) || (color.green >= 0xc0) || (color.blue >= 0xc0)) { + // white + this->buffer_[pos] |= 0x80 >> subpos; + this->buffer_[pos + buf_half_len] |= 0x80 >> subpos; + } else if ((color.red >= 0x80) || (color.green >= 0x80) || (color.blue >= 0x80)) { + // gray 1 + this->buffer_[pos] &= ~(0x80 >> subpos); + this->buffer_[pos + buf_half_len] |= 0x80 >> subpos; + } else { + // gray 2 + this->buffer_[pos] |= 0x80 >> subpos; + this->buffer_[pos + buf_half_len] &= ~(0x80 >> subpos); + } + } else { + // black + this->buffer_[pos] &= ~(0x80 >> subpos); + this->buffer_[pos + buf_half_len] &= ~(0x80 >> subpos); + } + } else { + // flip logic + if ((color.r > 0) || (color.g > 0) || (color.b > 0)) + this->buffer_[pos] &= ~(0x80 >> subpos); + else + this->buffer_[pos] |= (0x80 >> subpos); + } + } +} + +void WaveshareEPaper4P2InV2::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this) + ESP_LOGCONFIG(TAG, " Model: 4.20inV2"); + if (this->initial_mode_ == MODE_GRAYSCALE4) + ESP_LOGCONFIG(TAG, " Initial Mode: 4 Grayscale"); + else if (this->initial_mode_ == MODE_FAST) + ESP_LOGCONFIG(TAG, " Initial Mode: Fast"); + LOG_PIN(" CS Pin: ", this->cs_) + 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) +} + } // namespace waveshare_epaper } // namespace esphome \ No newline at end of file diff --git a/esphome/components/waveshare_epaper/waveshare_42v2.h b/esphome/components/waveshare_epaper/waveshare_42v2.h index 96661562b7..c1ef3714a6 100644 --- a/esphome/components/waveshare_epaper/waveshare_42v2.h +++ b/esphome/components/waveshare_epaper/waveshare_42v2.h @@ -3,29 +3,45 @@ namespace esphome { namespace waveshare_epaper { +enum TurnOnMode { + MODE_PARTIAL = 0, + MODE_FULL, + MODE_FAST, + MODE_GRAYSCALE4 +}; + class WaveshareEPaper4P2InV2 : public WaveshareEPaper { public: + void dump_config() override; void display() override; void initialize() override; void deep_sleep() override; void set_full_update_every(uint32_t full_update_every); + void set_initial_mode(uint8_t mode) { this->initial_mode_ = (TurnOnMode)mode; } protected: - void fast_initialize_(); - void full_update_(); + void initialize_internal_(TurnOnMode mode); + void update_(TurnOnMode mode); + void turn_on_display_(TurnOnMode mode); + + void write_lut_(); void reset_(); void set_window_(uint16_t x, uint16_t y, uint16_t x1, uint16_t y2); void set_cursor_(uint16_t x, uint16_t y); - void turn_on_display_full_(); void clear_(); int get_width_internal() override; int get_height_internal() override; + uint32_t get_buffer_length_() override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; + uint32_t idle_timeout_() override; uint32_t full_update_every_{30}; uint32_t at_update_{0}; + bool is_busy_{false}; + TurnOnMode initial_mode_{MODE_FAST}; }; } // namespace waveshare_epaper