mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	GDEY042T81 e-paper displays support (#8061)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
		| @@ -56,6 +56,7 @@ GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper) | |||||||
| WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_( | WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_( | ||||||
|     "WaveshareEPaper2P9InDKE", WaveshareEPaper |     "WaveshareEPaper2P9InDKE", WaveshareEPaper | ||||||
| ) | ) | ||||||
|  | GDEY042T81 = waveshare_epaper_ns.class_("GDEY042T81", WaveshareEPaper) | ||||||
| WaveshareEPaper2P9InD = waveshare_epaper_ns.class_( | WaveshareEPaper2P9InD = waveshare_epaper_ns.class_( | ||||||
|     "WaveshareEPaper2P9InD", WaveshareEPaper |     "WaveshareEPaper2P9InD", WaveshareEPaper | ||||||
| ) | ) | ||||||
| @@ -141,6 +142,7 @@ MODELS = { | |||||||
|     "2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2), |     "2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2), | ||||||
|     "2.90in-d": ("b", WaveshareEPaper2P9InD), |     "2.90in-d": ("b", WaveshareEPaper2P9InD), | ||||||
|     "2.90in-dke": ("c", WaveshareEPaper2P9InDKE), |     "2.90in-dke": ("c", WaveshareEPaper2P9InDKE), | ||||||
|  |     "gdey042t81": ("c", GDEY042T81), | ||||||
|     "4.20in": ("b", WaveshareEPaper4P2In), |     "4.20in": ("b", WaveshareEPaper4P2In), | ||||||
|     "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), |     "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), | ||||||
|     "4.20in-bv2-bwr": ("b", WaveshareEPaper4P2InBV2BWR), |     "4.20in-bv2-bwr": ("b", WaveshareEPaper4P2InBV2BWR), | ||||||
|   | |||||||
| @@ -2169,6 +2169,206 @@ void GDEW0154M09::dump_config() { | |||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ======================================================== | ||||||
|  | //     Good Display 4.2in black/white GDEY042T81 (SSD1683) | ||||||
|  | // Product page: | ||||||
|  | //  - https://www.good-display.com/product/386.html | ||||||
|  | // Datasheet: | ||||||
|  | //  - https://v4.cecdn.yun300.cn/100001_1909185148/GDEY042T81.pdf | ||||||
|  | //  - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1683.PDF | ||||||
|  | // Reference code from GoodDisplay: | ||||||
|  | //  - https://www.good-display.com/companyfile/1572.html (2024-08-01 15:40:41) | ||||||
|  | // Other reference code: | ||||||
|  | //  - https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp | ||||||
|  | // ======================================================== | ||||||
|  |  | ||||||
|  | void GDEY042T81::initialize() { | ||||||
|  |   this->init_display_(); | ||||||
|  |   ESP_LOGD(TAG, "Initialization complete, set the display to deep sleep"); | ||||||
|  |   this->deep_sleep(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // conflicting documentation / examples regarding reset timings | ||||||
|  | //   https://v4.cecdn.yun300.cn/100001_1909185148/SSD1683.PDF -> 10ms | ||||||
|  | //   GD sample code (Display_EPD_W21.cpp, see above) -> 10 ms | ||||||
|  | //   https://v4.cecdn.yun300.cn/100001_1909185148/GDEY042T81.pdf (section 14.2) -> 0.2ms (200us) | ||||||
|  | //   https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L351 | ||||||
|  | //   -> 10ms | ||||||
|  | //  10 ms seems to work, so we use this | ||||||
|  | GDEY042T81::GDEY042T81() { this->reset_duration_ = 10; } | ||||||
|  |  | ||||||
|  | void GDEY042T81::reset_() { | ||||||
|  |   if (this->reset_pin_ != nullptr) { | ||||||
|  |     this->reset_pin_->digital_write(false); | ||||||
|  |     delay(reset_duration_);  // NOLINT | ||||||
|  |     this->reset_pin_->digital_write(true); | ||||||
|  |     delay(reset_duration_);  // NOLINT | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GDEY042T81::init_display_() { | ||||||
|  |   this->reset_(); | ||||||
|  |  | ||||||
|  |   this->wait_until_idle_(); | ||||||
|  |   this->command(0x12);  // SWRESET | ||||||
|  |   this->wait_until_idle_(); | ||||||
|  |  | ||||||
|  |   // Specify number of lines for the driver: 300 (MUX 300) | ||||||
|  |   // https://v4.cecdn.yun300.cn/100001_1909185148/SSD1683.PDF (section 8.1) | ||||||
|  |   // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L354 | ||||||
|  |   this->command(0x01);  //  driver output control | ||||||
|  |   this->data(0x2B);     // (height - 1) % 256 | ||||||
|  |   this->data(0x01);     // (height - 1) / 256 | ||||||
|  |   this->data(0x00); | ||||||
|  |  | ||||||
|  |   // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L360 | ||||||
|  |   this->command(0x3C);  // BorderWaveform | ||||||
|  |   this->data(0x01); | ||||||
|  |   this->command(0x18);  // Read built-in temperature sensor | ||||||
|  |   this->data(0x80); | ||||||
|  |  | ||||||
|  |   // GD sample code (Display_EPD_W21.cpp@90ff) | ||||||
|  |   this->command(0x11);  // data entry mode | ||||||
|  |   this->data(0x03); | ||||||
|  |   // set windows (0,0,400,300) | ||||||
|  |   this->command(0x44);  // set Ram-X address start/end position | ||||||
|  |   this->data(0); | ||||||
|  |   this->data(0x31);  // (width / 8 -1) | ||||||
|  |  | ||||||
|  |   this->command(0x45);  //  set Ram-y address start/end position | ||||||
|  |   this->data(0); | ||||||
|  |   this->data(0); | ||||||
|  |   this->data(0x2B);  // (height - 1) % 256 | ||||||
|  |   this->data(0x01);  // (height - 1) / 256 | ||||||
|  |  | ||||||
|  |   // set cursor (0,0) | ||||||
|  |   this->command(0x4E);  // set RAM x address count to 0; | ||||||
|  |   this->data(0); | ||||||
|  |   this->command(0x4F);  // set RAM y address count to 0; | ||||||
|  |   this->data(0); | ||||||
|  |   this->data(0); | ||||||
|  |  | ||||||
|  |   this->wait_until_idle_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L366 | ||||||
|  | void GDEY042T81::update_full_() { | ||||||
|  |   this->command(0x21);  // display update control | ||||||
|  |   this->data(0x40);     // bypass RED as 0 | ||||||
|  |   this->data(0x00);     // single chip application | ||||||
|  |  | ||||||
|  |   // only ever do a fast update because slow updates are only relevant | ||||||
|  |   // for lower operating temperatures | ||||||
|  |   // see | ||||||
|  |   // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_290_GDEY029T94.h#L30 | ||||||
|  |   // | ||||||
|  |   // Should slow/fast updates be made configurable similar to how GxEPD2 does it? No idea if anyone would need it... | ||||||
|  |   this->command(0x1A);  // Write to temperature register | ||||||
|  |   this->data(0x6E); | ||||||
|  |   this->command(0x22); | ||||||
|  |   this->data(0xd7); | ||||||
|  |  | ||||||
|  |   this->command(0x20); | ||||||
|  |   this->wait_until_idle_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L389 | ||||||
|  | void GDEY042T81::update_part_() { | ||||||
|  |   this->command(0x21);  // display update control | ||||||
|  |   this->data(0x00);     // RED normal | ||||||
|  |   this->data(0x00);     // single chip application | ||||||
|  |  | ||||||
|  |   this->command(0x22); | ||||||
|  |   this->data(0xfc); | ||||||
|  |  | ||||||
|  |   this->command(0x20); | ||||||
|  |   this->wait_until_idle_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void HOT GDEY042T81::display() { | ||||||
|  |   ESP_LOGD(TAG, "Wake up the display"); | ||||||
|  |   this->init_display_(); | ||||||
|  |  | ||||||
|  |   if (!this->wait_until_idle_()) { | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     ESP_LOGE(TAG, "Failed to perform update, display is busy"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // basic code structure copied from WaveshareEPaper2P9InV2R2 | ||||||
|  |   if (this->full_update_every_ == 1) { | ||||||
|  |     ESP_LOGD(TAG, "Full update"); | ||||||
|  |     // do single full update | ||||||
|  |     this->command(0x24); | ||||||
|  |     this->start_data_(); | ||||||
|  |     this->write_array(this->buffer_, this->get_buffer_length_()); | ||||||
|  |     this->end_data_(); | ||||||
|  |  | ||||||
|  |     // TurnOnDisplay | ||||||
|  |     this->update_full_(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // if (this->full_update_every_ == 1 || | ||||||
|  |   if (this->at_update_ == 0) { | ||||||
|  |     ESP_LOGD(TAG, "Update"); | ||||||
|  |     // do base update | ||||||
|  |     this->command(0x24); | ||||||
|  |     this->start_data_(); | ||||||
|  |     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_(); | ||||||
|  |  | ||||||
|  |     // TurnOnDisplay; | ||||||
|  |     this->update_full_(); | ||||||
|  |   } else { | ||||||
|  |     // do partial update (full screen) | ||||||
|  |     // no need to load a LUT for GoodDisplays as they seem to have the LUT onboard | ||||||
|  |     // GD example code (Display_EPD_W21.cpp@283ff) | ||||||
|  |     // | ||||||
|  |     // not setting the BorderWaveform here again (contrary to the GD example) because according to | ||||||
|  |     // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L358 | ||||||
|  |     // it seems to be enough to set it during display initialization | ||||||
|  |     ESP_LOGD(TAG, "Partial update"); | ||||||
|  |     this->reset_(); | ||||||
|  |     if (!this->wait_until_idle_()) { | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       ESP_LOGE(TAG, "Failed to perform partial update, display is busy"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     this->command(0x24); | ||||||
|  |     this->start_data_(); | ||||||
|  |     this->write_array(this->buffer_, this->get_buffer_length_()); | ||||||
|  |     this->end_data_(); | ||||||
|  |  | ||||||
|  |     // TurnOnDisplay | ||||||
|  |     this->update_part_(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; | ||||||
|  |   this->wait_until_idle_(); | ||||||
|  |   ESP_LOGD(TAG, "Set the display back to deep sleep"); | ||||||
|  |   this->deep_sleep(); | ||||||
|  | } | ||||||
|  | void GDEY042T81::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } | ||||||
|  | int GDEY042T81::get_width_internal() { return 400; } | ||||||
|  | int GDEY042T81::get_height_internal() { return 300; } | ||||||
|  | uint32_t GDEY042T81::idle_timeout_() { return 5000; } | ||||||
|  | void GDEY042T81::dump_config() { | ||||||
|  |   LOG_DISPLAY("", "GoodDisplay E-Paper", this); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Model: 4.2in B/W GDEY042T81"); | ||||||
|  |   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); | ||||||
|  | } | ||||||
|  |  | ||||||
| static const uint8_t LUT_VCOM_DC_4_2[] = { | static const uint8_t LUT_VCOM_DC_4_2[] = { | ||||||
|     0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 0x00, 0x0A, 0x01, |     0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 0x00, 0x0A, 0x01, | ||||||
|     0x00, 0x00, 0x01, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |     0x00, 0x00, 0x01, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
|   | |||||||
| @@ -466,6 +466,43 @@ class WaveshareEPaper2P9InD : public WaveshareEPaper { | |||||||
|   int get_height_internal() override; |   int get_height_internal() override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class GDEY042T81 : public WaveshareEPaper { | ||||||
|  |  public: | ||||||
|  |   GDEY042T81(); | ||||||
|  |  | ||||||
|  |   void initialize() override; | ||||||
|  |  | ||||||
|  |   void display() override; | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |   void deep_sleep() override { | ||||||
|  |     // COMMAND POWER OFF | ||||||
|  |     this->command(0x22); | ||||||
|  |     this->data(0x83); | ||||||
|  |     this->command(0x20); | ||||||
|  |     // COMMAND DEEP SLEEP | ||||||
|  |     this->command(0x10); | ||||||
|  |     this->data(0x01); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_full_update_every(uint32_t full_update_every); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint32_t full_update_every_{30}; | ||||||
|  |   uint32_t at_update_{0}; | ||||||
|  |  | ||||||
|  |   int get_width_internal() override; | ||||||
|  |   int get_height_internal() override; | ||||||
|  |   uint32_t idle_timeout_() override; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   void reset_(); | ||||||
|  |   void update_full_(); | ||||||
|  |   void update_part_(); | ||||||
|  |   void init_display_(); | ||||||
|  | }; | ||||||
|  |  | ||||||
| class WaveshareEPaper4P2In : public WaveshareEPaper { | class WaveshareEPaper4P2In : public WaveshareEPaper { | ||||||
|  public: |  public: | ||||||
|   void initialize() override; |   void initialize() override; | ||||||
|   | |||||||
| @@ -463,6 +463,26 @@ display: | |||||||
|     lambda: |- |     lambda: |- | ||||||
|       it.rectangle(0, 0, it.get_width(), it.get_height()); |       it.rectangle(0, 0, it.get_width(), it.get_height()); | ||||||
|  |  | ||||||
|  |   - platform: waveshare_epaper | ||||||
|  |     id: epd_gdew042t81 | ||||||
|  |     model: gdey042t81 | ||||||
|  |     spi_id: spi_waveshare_epaper | ||||||
|  |     cs_pin: | ||||||
|  |       allow_other_uses: true | ||||||
|  |       number: ${cs_pin} | ||||||
|  |     dc_pin: | ||||||
|  |       allow_other_uses: true | ||||||
|  |       number: ${dc_pin} | ||||||
|  |     busy_pin: | ||||||
|  |       allow_other_uses: true | ||||||
|  |       number: ${busy_pin} | ||||||
|  |     reset_pin: | ||||||
|  |       allow_other_uses: true | ||||||
|  |       number: ${reset_pin} | ||||||
|  |     full_update_every: 30 | ||||||
|  |     lambda: |- | ||||||
|  |       it.rectangle(0, 0, it.get_width(), it.get_height()); | ||||||
|  |  | ||||||
|   # 4.2 inch displays |   # 4.2 inch displays | ||||||
|   - platform: waveshare_epaper |   - platform: waveshare_epaper | ||||||
|     id: epd_4_20 |     id: epd_4_20 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user