Commit of Grayscale support as provided by niceboygithub. See: https://github.com/esphome/esphome/pull/6209#issuecomment-2388682338

This commit is contained in:
Michael Davidson 2024-10-31 22:19:50 +11:00
parent 89c972c85a
commit f516a441fd
No known key found for this signature in database
GPG key ID: B8D1A99712B8B0EB
3 changed files with 311 additions and 39 deletions

View file

@ -7,6 +7,7 @@ from esphome.const import (
CONF_DC_PIN, CONF_DC_PIN,
CONF_FULL_UPDATE_EVERY, CONF_FULL_UPDATE_EVERY,
CONF_ID, CONF_ID,
CONF_INITIAL_MODE,
CONF_LAMBDA, CONF_LAMBDA,
CONF_MODEL, CONF_MODEL,
CONF_PAGES, CONF_PAGES,
@ -168,6 +169,16 @@ def validate_reset_pin_required(config):
) )
return 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( CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend( display.FULL_DISPLAY_SCHEMA.extend(
@ -182,12 +193,14 @@ CONFIG_SCHEMA = cv.All(
cv.positive_time_period_milliseconds, cv.positive_time_period_milliseconds,
cv.Range(max=core.TimePeriod(milliseconds=500)), 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(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema()), .extend(spi.spi_device_schema()),
validate_full_update_every_only_types_ac, validate_full_update_every_only_types_ac,
validate_reset_pin_required, validate_reset_pin_required,
validate_grayscale4_supported,
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), 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])) cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
if CONF_RESET_DURATION in config: if CONF_RESET_DURATION in config:
cg.add(var.set_reset_duration(config[CONF_RESET_DURATION])) 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]))

View file

@ -7,35 +7,151 @@ namespace waveshare_epaper {
static const char *const TAG = "waveshare_4.2v2"; static const char *const TAG = "waveshare_4.2v2";
void WaveshareEPaper4P2InV2::display() { static const uint8_t LUT_ALL[233]={
ESP_LOGD(TAG, "Performing full update"); 0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01,
this->full_update_(); 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->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->command(0x24);
this->start_data_(); 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->end_data_();
this->command(0x26); // new data
this->start_data_(); if ((mode == MODE_FULL) || (mode == MODE_FAST)) {
this->write_array(this->buffer_, this->get_buffer_length_()); this->command(0x26);
this->end_data_(); 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->deep_sleep();
this->is_busy_ = false;
} }
void WaveshareEPaper4P2InV2::turn_on_display_full_() { void WaveshareEPaper4P2InV2::turn_on_display_(TurnOnMode mode) {
this->command(0x22); 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); this->command(0x20);
// possible timeout.
this->wait_until_idle_(); 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_() { void WaveshareEPaper4P2InV2::clear_() {
uint8_t *buffer = (uint8_t *) calloc(this->get_buffer_length_(), sizeof(uint8_t)); uint32_t bufflen;
memset(buffer, 0xff, this->get_buffer_length_()); 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->command(0x24);
this->start_data_(); this->start_data_();
this->write_array(buffer, this->get_buffer_length_()); this->write_array(buffer, bufflen);
this->end_data_(); this->end_data_();
this->command(0x26); this->command(0x26);
this->start_data_(); this->start_data_();
this->write_array(buffer, this->get_buffer_length_()); this->write_array(buffer, bufflen);
this->end_data_(); this->end_data_();
free(buffer); free(buffer);
@ -77,7 +199,28 @@ void WaveshareEPaper4P2InV2::set_cursor_(uint16_t x, uint16_t y) {
this->data((y >> 8) & 0xFF); 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_SECOND 1
#define MODE_1_5_SECOND 0 #define MODE_1_5_SECOND 0
@ -89,32 +232,51 @@ void WaveshareEPaper4P2InV2::fast_initialize_() {
if (!this->wait_until_idle_()) { if (!this->wait_until_idle_()) {
ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?"); ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?");
} }
this->command(0x21); this->command(0x21);
this->data(0x40); if (mode == MODE_GRAYSCALE4)
this->data(0x00);
else
this->data(0x40);
this->data(0x00); 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 #if MODE_1_5_SECOND
// 1.5s // 1.5s
this->command(0x1A); // Write to temperature register this->command(0x1A); // Write to temperature register
this->data(0x6E); this->data(0x6E);
#endif #endif
#if MODE_1_SECOND #if MODE_1_SECOND
// 1s // 1s
this->command(0x1A); // Write to temperature register this->command(0x1A); // Write to temperature register
this->data(0x5A); this->data(0x5A);
#endif #endif
this->command(0x22); // Load temperature value this->command(0x22); // Load temperature value
this->data(0x91); this->data(0x91);
this->command(0x20); this->command(0x20);
if (!this->wait_until_idle_()) { if (!this->wait_until_idle_()) {
ESP_LOGW(TAG, "wait_until_idle_ returned FALSE. Is your busy pin set?"); 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_window_(0, 0, this->get_width_internal() - 1, this->get_height_internal() - 1);
this->set_cursor_(0, 0); this->set_cursor_(0, 0);
@ -124,11 +286,22 @@ void WaveshareEPaper4P2InV2::fast_initialize_() {
} }
this->clear_(); this->clear_();
this->turn_on_display_full_(); this->turn_on_display_(mode);
} }
void WaveshareEPaper4P2InV2::initialize() { 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() { void WaveshareEPaper4P2InV2::deep_sleep() {
@ -154,5 +327,73 @@ void WaveshareEPaper4P2InV2::reset_() {
delay(100); 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 waveshare_epaper
} // namespace esphome } // namespace esphome

View file

@ -3,29 +3,45 @@
namespace esphome { namespace esphome {
namespace waveshare_epaper { namespace waveshare_epaper {
enum TurnOnMode {
MODE_PARTIAL = 0,
MODE_FULL,
MODE_FAST,
MODE_GRAYSCALE4
};
class WaveshareEPaper4P2InV2 : public WaveshareEPaper { class WaveshareEPaper4P2InV2 : public WaveshareEPaper {
public: public:
void dump_config() override;
void display() override; void display() override;
void initialize() override; void initialize() override;
void deep_sleep() override; void deep_sleep() override;
void set_full_update_every(uint32_t full_update_every); void set_full_update_every(uint32_t full_update_every);
void set_initial_mode(uint8_t mode) { this->initial_mode_ = (TurnOnMode)mode; }
protected: protected:
void fast_initialize_(); void initialize_internal_(TurnOnMode mode);
void full_update_(); void update_(TurnOnMode mode);
void turn_on_display_(TurnOnMode mode);
void write_lut_();
void reset_(); void reset_();
void set_window_(uint16_t x, uint16_t y, uint16_t x1, uint16_t y2); 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 set_cursor_(uint16_t x, uint16_t y);
void turn_on_display_full_();
void clear_(); void clear_();
int get_width_internal() override; int get_width_internal() override;
int get_height_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 full_update_every_{30};
uint32_t at_update_{0}; uint32_t at_update_{0};
bool is_busy_{false};
TurnOnMode initial_mode_{MODE_FAST};
}; };
} // namespace waveshare_epaper } // namespace waveshare_epaper