#include "waveshare_epaper.h" #include "esphome/core/log.h" #include "esphome/core/application.h" namespace esphome { namespace waveshare_epaper { static const char *const TAG = "waveshare_2.13v3"; static const uint8_t PARTIAL_LUT[] = { 0x32, // cmd 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, }; static const uint8_t FULL_LUT[] = { 0x32, // CMD 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, }; static const uint8_t SW_RESET = 0x12; static const uint8_t ACTIVATE = 0x20; static const uint8_t WRITE_BUFFER = 0x24; static const uint8_t WRITE_BASE = 0x26; static const uint8_t DRV_OUT_CTL[] = {0x01, 0x27, 0x01, 0x00}; // driver output control static const uint8_t GATEV[] = {0x03, 0x17}; static const uint8_t SRCV[] = {0x04, 0x41, 0x0C, 0x32}; static const uint8_t SLEEP[] = {0x10, 0x01}; static const uint8_t DATA_ENTRY[] = {0x11, 0x03}; // data entry mode static const uint8_t TEMP_SENS[] = {0x18, 0x80}; // Temp sensor static const uint8_t DISPLAY_UPDATE[] = {0x21, 0x00, 0x80}; // Display update control static const uint8_t UPSEQ[] = {0x22, 0xC0}; static const uint8_t ON_FULL[] = {0x22, 0xC7}; static const uint8_t ON_PARTIAL[] = {0x22, 0x0F}; static const uint8_t VCOM[] = {0x2C, 0x36}; static const uint8_t CMD5[] = {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00}; static const uint8_t BORDER_PART[] = {0x3C, 0x80}; // border waveform static const uint8_t BORDER_FULL[] = {0x3C, 0x05}; // border waveform static const uint8_t CMD1[] = {0x3F, 0x22}; static const uint8_t RAM_X_START[] = {0x44, 0x00, 121 / 8}; // set ram_x_address_start_end static const uint8_t RAM_Y_START[] = {0x45, 0x00, 0x00, 250 - 1, 0}; // set ram_y_address_start_end static const uint8_t RAM_X_POS[] = {0x4E, 0x00}; // set ram_x_address_counter // static const uint8_t RAM_Y_POS[] = {0x4F, 0x00, 0x00}; // set ram_y_address_counter #define SEND(x) this->cmd_data(x, sizeof(x)) void WaveshareEPaper2P13InV3::write_lut_(const uint8_t *lut) { this->wait_until_idle_(); this->cmd_data(lut, sizeof(PARTIAL_LUT)); SEND(CMD1); SEND(GATEV); SEND(SRCV); SEND(VCOM); } // write the buffer starting on line top, up to line bottom. void WaveshareEPaper2P13InV3::write_buffer_(uint8_t cmd, int top, int bottom) { this->wait_until_idle_(); this->set_window_(top, bottom); this->command(cmd); this->start_data_(); auto width_bytes = this->get_width_controller() / 8; this->write_array(this->buffer_ + top * width_bytes, (bottom - top) * width_bytes); this->end_data_(); } void WaveshareEPaper2P13InV3::send_reset_() { if (this->reset_pin_ != nullptr) { this->reset_pin_->digital_write(false); delay(2); this->reset_pin_->digital_write(true); } } void WaveshareEPaper2P13InV3::setup() { this->init_internal_(this->get_buffer_length_()); this->setup_pins_(); this->spi_setup(); this->reset_(); delay(20); this->send_reset_(); // as a one-off delay this is not worth working around. delay(100); // NOLINT this->wait_until_idle_(); this->command(SW_RESET); this->wait_until_idle_(); SEND(DRV_OUT_CTL); SEND(DATA_ENTRY); SEND(CMD5); this->set_window_(0, this->get_height_internal()); SEND(BORDER_FULL); SEND(DISPLAY_UPDATE); SEND(TEMP_SENS); this->wait_until_idle_(); this->write_lut_(FULL_LUT); } // t and b are y positions, i.e. line numbers. void WaveshareEPaper2P13InV3::set_window_(int t, int b) { uint8_t buffer[3]; SEND(RAM_X_START); SEND(RAM_Y_START); SEND(RAM_X_POS); buffer[0] = 0x4F; buffer[1] = (uint8_t) t; buffer[2] = (uint8_t) (t >> 8); SEND(buffer); } // must implement, but we override setup to have more control void WaveshareEPaper2P13InV3::initialize() {} void WaveshareEPaper2P13InV3::partial_update_() { this->send_reset_(); this->set_timeout(100, [this] { this->write_lut_(PARTIAL_LUT); SEND(BORDER_PART); SEND(UPSEQ); this->command(ACTIVATE); this->set_timeout(100, [this] { this->wait_until_idle_(); this->write_buffer_(WRITE_BUFFER, 0, this->get_height_internal()); SEND(ON_PARTIAL); this->command(ACTIVATE); // Activate Display Update Sequence this->is_busy_ = false; }); }); } void WaveshareEPaper2P13InV3::full_update_() { ESP_LOGI(TAG, "Performing full e-paper update."); this->write_lut_(FULL_LUT); this->write_buffer_(WRITE_BUFFER, 0, this->get_height_internal()); this->write_buffer_(WRITE_BASE, 0, this->get_height_internal()); SEND(ON_FULL); this->command(ACTIVATE); // don't wait here this->is_busy_ = false; } void WaveshareEPaper2P13InV3::display() { if (this->is_busy_ || (this->busy_pin_ != nullptr && this->busy_pin_->digital_read())) return; this->is_busy_ = true; const bool partial = this->at_update_ != 0; this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; if (partial) { this->partial_update_(); } else { this->full_update_(); } } int WaveshareEPaper2P13InV3::get_width_controller() { return 128; } int WaveshareEPaper2P13InV3::get_width_internal() { return 122; } int WaveshareEPaper2P13InV3::get_height_internal() { return 250; } uint32_t WaveshareEPaper2P13InV3::idle_timeout_() { return 5000; } void WaveshareEPaper2P13InV3::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this) ESP_LOGCONFIG(TAG, " Model: 2.13inV3"); 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) } void WaveshareEPaper2P13InV3::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } } // namespace waveshare_epaper } // namespace esphome