Inkplate 6 support for ESPHome (#1283)

* Add Inkplate 6 support

Inkplate 6 is e-paper display based on ESP32. This commit adds support for integrating Inkplate 6 into the ESPHome. Find more info here: inkplate.io

* Greyscale working

* Update inkplate.h

* Fix formatting

* Formatting

* Update esphome/components/inkplate6/display.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Update esphome/components/inkplate6/display.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Fix some lint errors
Ignore some lint errors
Only allow on ESP32

* Update the codeowners file

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
David Zovko 2021-01-16 01:19:35 +01:00 committed by GitHub
parent 5fcd1e391d
commit d9a2651a5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 930 additions and 0 deletions

View file

@ -37,6 +37,7 @@ esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/homeassistant/* @OttoWinter
esphome/components/i2c/* @esphome/core
esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
esphome/components/interval/* @esphome/core
esphome/components/json/* @OttoWinter

View file

@ -0,0 +1 @@
CODEOWNERS = ['@jesserockz']

View file

@ -0,0 +1,141 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import display, i2c
from esphome.const import CONF_FULL_UPDATE_EVERY, CONF_ID, CONF_LAMBDA, CONF_PAGES, \
CONF_WAKEUP_PIN, ESP_PLATFORM_ESP32
DEPENDENCIES = ['i2c']
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONF_DISPLAY_DATA_0_PIN = 'display_data_0_pin'
CONF_DISPLAY_DATA_1_PIN = 'display_data_1_pin'
CONF_DISPLAY_DATA_2_PIN = 'display_data_2_pin'
CONF_DISPLAY_DATA_3_PIN = 'display_data_3_pin'
CONF_DISPLAY_DATA_4_PIN = 'display_data_4_pin'
CONF_DISPLAY_DATA_5_PIN = 'display_data_5_pin'
CONF_DISPLAY_DATA_6_PIN = 'display_data_6_pin'
CONF_DISPLAY_DATA_7_PIN = 'display_data_7_pin'
CONF_CL_PIN = 'cl_pin'
CONF_CKV_PIN = 'ckv_pin'
CONF_GREYSCALE = 'greyscale'
CONF_GMOD_PIN = 'gmod_pin'
CONF_GPIO0_ENABLE_PIN = 'gpio0_enable_pin'
CONF_LE_PIN = 'le_pin'
CONF_OE_PIN = 'oe_pin'
CONF_PARTIAL_UPDATING = 'partial_updating'
CONF_POWERUP_PIN = 'powerup_pin'
CONF_SPH_PIN = 'sph_pin'
CONF_SPV_PIN = 'spv_pin'
CONF_VCOM_PIN = 'vcom_pin'
inkplate6_ns = cg.esphome_ns.namespace('inkplate6')
Inkplate6 = inkplate6_ns.class_('Inkplate6', cg.PollingComponent, i2c.I2CDevice,
display.DisplayBuffer)
CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(Inkplate6),
cv.Optional(CONF_GREYSCALE, default=False): cv.boolean,
cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean,
cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
# Control pins
cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema,
# Data pins
cv.Optional(CONF_DISPLAY_DATA_0_PIN, default=4): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_1_PIN, default=5): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_2_PIN, default=18): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_3_PIN, default=19): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_4_PIN, default=23): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_5_PIN, default=25): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_6_PIN, default=26): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_7_PIN, default=27): pins.internal_gpio_output_pin_schema,
}).extend(cv.polling_component_schema('5s').extend(i2c.i2c_device_schema(0x48))),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield display.register_display(var, config)
yield i2c.register_i2c_device(var, config)
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')],
return_type=cg.void)
cg.add(var.set_writer(lambda_))
cg.add(var.set_greyscale(config[CONF_GREYSCALE]))
cg.add(var.set_partial_updating(config[CONF_PARTIAL_UPDATING]))
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
ckv = yield cg.gpio_pin_expression(config[CONF_CKV_PIN])
cg.add(var.set_ckv_pin(ckv))
gmod = yield cg.gpio_pin_expression(config[CONF_GMOD_PIN])
cg.add(var.set_gmod_pin(gmod))
gpio0_enable = yield cg.gpio_pin_expression(config[CONF_GPIO0_ENABLE_PIN])
cg.add(var.set_gpio0_enable_pin(gpio0_enable))
oe = yield cg.gpio_pin_expression(config[CONF_OE_PIN])
cg.add(var.set_oe_pin(oe))
powerup = yield cg.gpio_pin_expression(config[CONF_POWERUP_PIN])
cg.add(var.set_powerup_pin(powerup))
sph = yield cg.gpio_pin_expression(config[CONF_SPH_PIN])
cg.add(var.set_sph_pin(sph))
spv = yield cg.gpio_pin_expression(config[CONF_SPV_PIN])
cg.add(var.set_spv_pin(spv))
vcom = yield cg.gpio_pin_expression(config[CONF_VCOM_PIN])
cg.add(var.set_vcom_pin(vcom))
wakeup = yield cg.gpio_pin_expression(config[CONF_WAKEUP_PIN])
cg.add(var.set_wakeup_pin(wakeup))
cl = yield cg.gpio_pin_expression(config[CONF_CL_PIN])
cg.add(var.set_cl_pin(cl))
le = yield cg.gpio_pin_expression(config[CONF_LE_PIN])
cg.add(var.set_le_pin(le))
display_data_0 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_0_PIN])
cg.add(var.set_display_data_0_pin(display_data_0))
display_data_1 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_1_PIN])
cg.add(var.set_display_data_1_pin(display_data_1))
display_data_2 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_2_PIN])
cg.add(var.set_display_data_2_pin(display_data_2))
display_data_3 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_3_PIN])
cg.add(var.set_display_data_3_pin(display_data_3))
display_data_4 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_4_PIN])
cg.add(var.set_display_data_4_pin(display_data_4))
display_data_5 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_5_PIN])
cg.add(var.set_display_data_5_pin(display_data_5))
display_data_6 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_6_PIN])
cg.add(var.set_display_data_6_pin(display_data_6))
display_data_7 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN])
cg.add(var.set_display_data_7_pin(display_data_7))
cg.add_build_flag('-DBOARD_HAS_PSRAM')

View file

@ -0,0 +1,630 @@
#include "inkplate.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace inkplate6 {
static const char *TAG = "inkplate";
void Inkplate6::setup() {
this->initialize_();
this->vcom_pin_->setup();
this->powerup_pin_->setup();
this->wakeup_pin_->setup();
this->gpio0_enable_pin_->setup();
this->gpio0_enable_pin_->digital_write(true);
this->cl_pin_->setup();
this->le_pin_->setup();
this->ckv_pin_->setup();
this->gmod_pin_->setup();
this->oe_pin_->setup();
this->sph_pin_->setup();
this->spv_pin_->setup();
this->display_data_0_pin_->setup();
this->display_data_1_pin_->setup();
this->display_data_2_pin_->setup();
this->display_data_3_pin_->setup();
this->display_data_4_pin_->setup();
this->display_data_5_pin_->setup();
this->display_data_6_pin_->setup();
this->display_data_7_pin_->setup();
this->clean();
this->display();
}
void Inkplate6::initialize_() {
uint32_t buffer_size = this->get_buffer_length_();
if (this->partial_buffer_ != nullptr) {
free(this->partial_buffer_); // NOLINT
}
if (this->partial_buffer_2_ != nullptr) {
free(this->partial_buffer_2_); // NOLINT
}
if (this->buffer_ != nullptr) {
free(this->buffer_); // NOLINT
}
this->buffer_ = (uint8_t *) ps_malloc(buffer_size);
if (this->buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate buffer for display!");
this->mark_failed();
return;
}
if (!this->greyscale_) {
this->partial_buffer_ = (uint8_t *) ps_malloc(buffer_size);
if (this->partial_buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate partial buffer for display!");
this->mark_failed();
return;
}
this->partial_buffer_2_ = (uint8_t *) ps_malloc(buffer_size * 2);
if (this->partial_buffer_2_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate partial buffer 2 for display!");
this->mark_failed();
return;
}
memset(this->partial_buffer_, 0, buffer_size);
memset(this->partial_buffer_2_, 0, buffer_size * 2);
}
memset(this->buffer_, 0, buffer_size);
}
float Inkplate6::get_setup_priority() const { return setup_priority::PROCESSOR; }
size_t Inkplate6::get_buffer_length_() {
if (this->greyscale_) {
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 2u;
} else {
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
}
}
void Inkplate6::update() {
this->do_update_();
if (this->full_update_every_ > 0 && this->partial_updates_ >= this->full_update_every_) {
this->block_partial_ = true;
}
this->display();
}
void HOT Inkplate6::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;
if (this->greyscale_) {
int x1 = x / 2;
int x_sub = x % 2;
uint32_t pos = (x1 + y * (this->get_width_internal() / 2));
uint8_t current = this->buffer_[pos];
// float px = (0.2126 * (color.red / 255.0)) + (0.7152 * (color.green / 255.0)) + (0.0722 * (color.blue / 255.0));
// px = pow(px, 1.5);
// uint8_t gs = (uint8_t)(px*7);
uint8_t gs = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
this->buffer_[pos] = (pixelMaskGLUT[x_sub] & current) | (x_sub ? gs : gs << 4);
} else {
int x1 = x / 8;
int x_sub = x % 8;
uint32_t pos = (x1 + y * (this->get_width_internal() / 8));
uint8_t current = this->partial_buffer_[pos];
this->partial_buffer_[pos] = (~pixelMaskLUT[x_sub] & current) | (color.is_on() ? 0 : pixelMaskLUT[x_sub]);
}
}
void Inkplate6::dump_config() {
LOG_DISPLAY("", "Inkplate", this);
ESP_LOGCONFIG(TAG, " Greyscale: %s", YESNO(this->greyscale_));
ESP_LOGCONFIG(TAG, " Partial Updating: %s", YESNO(this->partial_updating_));
ESP_LOGCONFIG(TAG, " Full Update Every: %d", this->full_update_every_);
// Log pins
LOG_PIN(" CKV Pin: ", this->ckv_pin_);
LOG_PIN(" CL Pin: ", this->cl_pin_);
LOG_PIN(" GPIO0 Enable Pin: ", this->gpio0_enable_pin_);
LOG_PIN(" GMOD Pin: ", this->gmod_pin_);
LOG_PIN(" LE Pin: ", this->le_pin_);
LOG_PIN(" OE Pin: ", this->oe_pin_);
LOG_PIN(" POWERUP Pin: ", this->powerup_pin_);
LOG_PIN(" SPH Pin: ", this->sph_pin_);
LOG_PIN(" SPV Pin: ", this->spv_pin_);
LOG_PIN(" VCOM Pin: ", this->vcom_pin_);
LOG_PIN(" WAKEUP Pin: ", this->wakeup_pin_);
LOG_PIN(" Data 0 Pin: ", this->display_data_0_pin_);
LOG_PIN(" Data 1 Pin: ", this->display_data_1_pin_);
LOG_PIN(" Data 2 Pin: ", this->display_data_2_pin_);
LOG_PIN(" Data 3 Pin: ", this->display_data_3_pin_);
LOG_PIN(" Data 4 Pin: ", this->display_data_4_pin_);
LOG_PIN(" Data 5 Pin: ", this->display_data_5_pin_);
LOG_PIN(" Data 6 Pin: ", this->display_data_6_pin_);
LOG_PIN(" Data 7 Pin: ", this->display_data_7_pin_);
LOG_UPDATE_INTERVAL(this);
}
void Inkplate6::eink_off_() {
ESP_LOGV(TAG, "Eink off called");
unsigned long start_time = millis();
if (panel_on_ == 0)
return;
panel_on_ = 0;
this->gmod_pin_->digital_write(false);
this->oe_pin_->digital_write(false);
GPIO.out &= ~(get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()) | (1 << this->le_pin_->get_pin()));
this->sph_pin_->digital_write(false);
this->spv_pin_->digital_write(false);
this->powerup_pin_->digital_write(false);
this->wakeup_pin_->digital_write(false);
this->vcom_pin_->digital_write(false);
pins_z_state_();
}
void Inkplate6::eink_on_() {
ESP_LOGV(TAG, "Eink on called");
unsigned long start_time = millis();
if (panel_on_ == 1)
return;
panel_on_ = 1;
pins_as_outputs_();
this->wakeup_pin_->digital_write(true);
this->powerup_pin_->digital_write(true);
this->vcom_pin_->digital_write(true);
this->write_byte(0x01, 0x3F);
delay(40);
this->write_byte(0x0D, 0x80);
delay(2);
this->read_byte(0x00, &temperature_, 0);
this->le_pin_->digital_write(false);
this->oe_pin_->digital_write(false);
this->cl_pin_->digital_write(false);
this->sph_pin_->digital_write(true);
this->gmod_pin_->digital_write(true);
this->spv_pin_->digital_write(true);
this->ckv_pin_->digital_write(false);
this->oe_pin_->digital_write(true);
}
void Inkplate6::fill(Color color) {
ESP_LOGV(TAG, "Fill called");
unsigned long start_time = millis();
if (this->greyscale_) {
uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->buffer_[i] = (fill << 4) | fill;
} else {
uint8_t fill = color.is_on() ? 0x00 : 0xFF;
for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->partial_buffer_[i] = fill;
}
ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time);
}
void Inkplate6::display() {
ESP_LOGV(TAG, "Display called");
unsigned long start_time = millis();
if (this->greyscale_) {
this->display3b_();
} else {
if (this->partial_updating_ && this->partial_update_()) {
ESP_LOGV(TAG, "Display finished (partial) (%lums)", millis() - start_time);
return;
}
this->display1b_();
}
ESP_LOGV(TAG, "Display finished (full) (%lums)", millis() - start_time);
}
void Inkplate6::display1b_() {
ESP_LOGV(TAG, "Display1b called");
unsigned long start_time = millis();
for (int i = 0; i < this->get_buffer_length_(); i++) {
this->buffer_[i] &= this->partial_buffer_[i];
this->buffer_[i] |= this->partial_buffer_[i];
}
uint16_t pos;
uint32_t send;
uint8_t data;
uint8_t buffer_value;
eink_on_();
clean_fast_(0, 1);
clean_fast_(1, 5);
clean_fast_(2, 1);
clean_fast_(0, 5);
clean_fast_(2, 1);
clean_fast_(1, 12);
clean_fast_(2, 1);
clean_fast_(0, 11);
ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time);
for (int k = 0; k < 3; k++) {
pos = this->get_buffer_length_() - 1;
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = this->buffer_[pos];
data = LUTB[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
data = LUTB[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
buffer_value = this->buffer_[pos];
data = LUTB[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
data = LUTB[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
vscan_end_();
}
delayMicroseconds(230);
}
ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time);
pos = this->get_buffer_length_() - 1;
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = this->buffer_[pos];
data = LUT2[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
data = LUT2[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
buffer_value = this->buffer_[pos];
data = LUT2[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
data = LUT2[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
vscan_end_();
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Display1b second loop (%lums)", millis() - start_time);
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = this->buffer_[pos];
data = 0b00000000;
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
vscan_end_();
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Display1b third loop (%lums)", millis() - start_time);
vscan_start_();
eink_off_();
this->block_partial_ = false;
this->partial_updates_ = 0;
ESP_LOGV(TAG, "Display1b finished (%lums)", millis() - start_time);
}
void Inkplate6::display3b_() {
ESP_LOGV(TAG, "Display3b called");
unsigned long start_time = millis();
eink_on_();
clean_fast_(0, 1);
clean_fast_(1, 12);
clean_fast_(2, 1);
clean_fast_(0, 11);
clean_fast_(2, 1);
clean_fast_(1, 12);
clean_fast_(2, 1);
clean_fast_(0, 11);
for (int k = 0; k < 8; k++) {
uint32_t pos = this->get_buffer_length_() - 1;
uint32_t send;
uint8_t pix1;
uint8_t pix2;
uint8_t pix3;
uint8_t pix4;
uint8_t pixel;
uint8_t pixel2;
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
pix1 = this->buffer_[pos--];
pix2 = this->buffer_[pos--];
pix3 = this->buffer_[pos--];
pix4 = this->buffer_[pos--];
pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) |
(((pixel & B11100000) >> 5) << 25);
hscan_start_(send);
send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) |
(((pixel2 & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
pix1 = this->buffer_[pos--];
pix2 = this->buffer_[pos--];
pix3 = this->buffer_[pos--];
pix4 = this->buffer_[pos--];
pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) |
(((pixel & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) |
(((pixel2 & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
vscan_end_();
}
delayMicroseconds(230);
}
clean_fast_(2, 1);
clean_fast_(3, 1);
vscan_start_();
eink_off_();
ESP_LOGV(TAG, "Display3b finished (%lums)", millis() - start_time);
}
bool Inkplate6::partial_update_() {
ESP_LOGV(TAG, "Partial update called");
unsigned long start_time = millis();
if (this->greyscale_)
return false;
if (this->block_partial_)
return false;
this->partial_updates_++;
uint16_t pos = this->get_buffer_length_() - 1;
uint32_t send;
uint8_t data;
uint8_t diffw, diffb;
uint32_t n = (this->get_buffer_length_() * 2) - 1;
for (int i = 0; i < this->get_height_internal(); i++) {
for (int j = 0; j < (this->get_width_internal() / 8); j++) {
diffw = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & ~(this->partial_buffer_[pos]);
diffb = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & this->partial_buffer_[pos];
pos--;
this->partial_buffer_2_[n--] = LUTW[diffw >> 4] & LUTB[diffb >> 4];
this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F];
}
}
ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time);
eink_on_();
for (int k = 0; k < 5; k++) {
vscan_start_();
n = (this->get_buffer_length_() * 2) - 1;
for (int i = 0; i < this->get_height_internal(); i++) {
data = this->partial_buffer_2_[n--];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
for (int j = 0; j < (this->get_width_internal() / 4) - 1; j++) {
data = this->partial_buffer_2_[n--];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
vscan_end_();
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Partial update loop k=%d (%lums)", k, millis() - start_time);
}
clean_fast_(2, 2);
clean_fast_(3, 1);
vscan_start_();
eink_off_();
for (int i = 0; i < this->get_buffer_length_(); i++) {
this->buffer_[i] = this->partial_buffer_[i];
}
ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time);
return true;
}
void Inkplate6::vscan_start_() {
this->ckv_pin_->digital_write(true);
delayMicroseconds(7);
this->spv_pin_->digital_write(false);
delayMicroseconds(10);
this->ckv_pin_->digital_write(false);
delayMicroseconds(0);
this->ckv_pin_->digital_write(true);
delayMicroseconds(8);
this->spv_pin_->digital_write(true);
delayMicroseconds(10);
this->ckv_pin_->digital_write(false);
delayMicroseconds(0);
this->ckv_pin_->digital_write(true);
delayMicroseconds(18);
this->ckv_pin_->digital_write(false);
delayMicroseconds(0);
this->ckv_pin_->digital_write(true);
delayMicroseconds(18);
this->ckv_pin_->digital_write(false);
delayMicroseconds(0);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::vscan_write_() {
this->ckv_pin_->digital_write(false);
this->le_pin_->digital_write(true);
this->le_pin_->digital_write(false);
delayMicroseconds(0);
this->sph_pin_->digital_write(false);
this->cl_pin_->digital_write(true);
this->cl_pin_->digital_write(false);
this->sph_pin_->digital_write(true);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::hscan_start_(uint32_t d) {
this->sph_pin_->digital_write(false);
GPIO.out_w1ts = (d) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
this->sph_pin_->digital_write(true);
}
void Inkplate6::vscan_end_() {
this->ckv_pin_->digital_write(false);
this->le_pin_->digital_write(true);
this->le_pin_->digital_write(false);
delayMicroseconds(1);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::clean() {
ESP_LOGV(TAG, "Clean called");
unsigned long start_time = millis();
eink_on_();
clean_fast_(0, 1); // White
clean_fast_(0, 8); // White to White
clean_fast_(0, 1); // White to Black
clean_fast_(0, 8); // Black to Black
clean_fast_(2, 1); // Black to White
clean_fast_(1, 10); // White to White
ESP_LOGV(TAG, "Clean finished (%lums)", millis() - start_time);
}
void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep);
unsigned long start_time = millis();
eink_on_();
uint8_t data = 0;
if (c == 0) // White
data = B10101010;
else if (c == 1) // Black
data = B01010101;
else if (c == 2) // Discharge
data = B00000000;
else if (c == 3) // Skip
data = B11111111;
uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
for (int k = 0; k < rep; k++) {
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
hscan_start_(send);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
for (int j = 0; j < this->get_width_internal() / 8; j++) {
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
vscan_end_();
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Clean fast rep loop %d finished (%lums)", k, millis() - start_time);
}
ESP_LOGV(TAG, "Clean fast finished (%lums)", millis() - start_time);
}
void Inkplate6::pins_z_state_() {
this->ckv_pin_->pin_mode(INPUT);
this->sph_pin_->pin_mode(INPUT);
this->oe_pin_->pin_mode(INPUT);
this->gmod_pin_->pin_mode(INPUT);
this->spv_pin_->pin_mode(INPUT);
this->display_data_0_pin_->pin_mode(INPUT);
this->display_data_1_pin_->pin_mode(INPUT);
this->display_data_2_pin_->pin_mode(INPUT);
this->display_data_3_pin_->pin_mode(INPUT);
this->display_data_4_pin_->pin_mode(INPUT);
this->display_data_5_pin_->pin_mode(INPUT);
this->display_data_6_pin_->pin_mode(INPUT);
this->display_data_7_pin_->pin_mode(INPUT);
}
void Inkplate6::pins_as_outputs_() {
this->ckv_pin_->pin_mode(OUTPUT);
this->sph_pin_->pin_mode(OUTPUT);
this->oe_pin_->pin_mode(OUTPUT);
this->gmod_pin_->pin_mode(OUTPUT);
this->spv_pin_->pin_mode(OUTPUT);
this->display_data_0_pin_->pin_mode(OUTPUT);
this->display_data_1_pin_->pin_mode(OUTPUT);
this->display_data_2_pin_->pin_mode(OUTPUT);
this->display_data_3_pin_->pin_mode(OUTPUT);
this->display_data_4_pin_->pin_mode(OUTPUT);
this->display_data_5_pin_->pin_mode(OUTPUT);
this->display_data_6_pin_->pin_mode(OUTPUT);
this->display_data_7_pin_->pin_mode(OUTPUT);
}
} // namespace inkplate6
} // namespace esphome
#endif

View file

@ -0,0 +1,157 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/display/display_buffer.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace inkplate6 {
class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public i2c::I2CDevice {
public:
const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101,
B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101};
const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010,
B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010};
const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101,
B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101};
const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000,
B00010000, B00100000, B01000000, B10000000};
const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000};
const uint8_t waveform3Bit[8][8] = {{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0},
{0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0},
{1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0}};
const uint32_t waveform[50] = {
0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868,
0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68,
0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595,
0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484,
0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9,
0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111};
void set_greyscale(bool greyscale) {
this->greyscale_ = greyscale;
this->initialize_();
this->block_partial_ = true;
}
void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; }
void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }
void set_display_data_0_pin(GPIOPin *data) { this->display_data_0_pin_ = data; }
void set_display_data_1_pin(GPIOPin *data) { this->display_data_1_pin_ = data; }
void set_display_data_2_pin(GPIOPin *data) { this->display_data_2_pin_ = data; }
void set_display_data_3_pin(GPIOPin *data) { this->display_data_3_pin_ = data; }
void set_display_data_4_pin(GPIOPin *data) { this->display_data_4_pin_ = data; }
void set_display_data_5_pin(GPIOPin *data) { this->display_data_5_pin_ = data; }
void set_display_data_6_pin(GPIOPin *data) { this->display_data_6_pin_ = data; }
void set_display_data_7_pin(GPIOPin *data) { this->display_data_7_pin_ = data; }
void set_ckv_pin(GPIOPin *ckv) { this->ckv_pin_ = ckv; }
void set_cl_pin(GPIOPin *cl) { this->cl_pin_ = cl; }
void set_gpio0_enable_pin(GPIOPin *gpio0_enable) { this->gpio0_enable_pin_ = gpio0_enable; }
void set_gmod_pin(GPIOPin *gmod) { this->gmod_pin_ = gmod; }
void set_le_pin(GPIOPin *le) { this->le_pin_ = le; }
void set_oe_pin(GPIOPin *oe) { this->oe_pin_ = oe; }
void set_powerup_pin(GPIOPin *powerup) { this->powerup_pin_ = powerup; }
void set_sph_pin(GPIOPin *sph) { this->sph_pin_ = sph; }
void set_spv_pin(GPIOPin *spv) { this->spv_pin_ = spv; }
void set_vcom_pin(GPIOPin *vcom) { this->vcom_pin_ = vcom; }
void set_wakeup_pin(GPIOPin *wakeup) { this->wakeup_pin_ = wakeup; }
float get_setup_priority() const override;
void dump_config() override;
void display();
void clean();
void fill(Color color) override;
void update() override;
void setup() override;
uint8_t get_panel_state() { return this->panel_on_; }
bool get_greyscale() { return this->greyscale_; }
bool get_partial_updating() { return this->partial_updating_; }
uint8_t get_temperature() { return this->temperature_; }
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void display1b_();
void display3b_();
void initialize_();
bool partial_update_();
void clean_fast_(uint8_t c, uint8_t rep);
void hscan_start_(uint32_t d);
void vscan_end_();
void vscan_start_();
void vscan_write_();
void eink_off_();
void eink_on_();
void setup_pins_();
void pins_z_state_();
void pins_as_outputs_();
int get_width_internal() override { return 800; }
int get_height_internal() override { return 600; }
size_t get_buffer_length_();
int get_data_pin_mask_() {
int data = 0;
data |= (1 << this->display_data_0_pin_->get_pin());
data |= (1 << this->display_data_1_pin_->get_pin());
data |= (1 << this->display_data_2_pin_->get_pin());
data |= (1 << this->display_data_3_pin_->get_pin());
data |= (1 << this->display_data_4_pin_->get_pin());
data |= (1 << this->display_data_5_pin_->get_pin());
data |= (1 << this->display_data_6_pin_->get_pin());
data |= (1 << this->display_data_7_pin_->get_pin());
return data;
}
uint8_t panel_on_ = 0;
uint8_t temperature_;
uint8_t *partial_buffer_{nullptr};
uint8_t *partial_buffer_2_{nullptr};
uint32_t full_update_every_;
uint32_t partial_updates_{0};
bool block_partial_;
bool greyscale_;
bool partial_updating_;
GPIOPin *display_data_0_pin_;
GPIOPin *display_data_1_pin_;
GPIOPin *display_data_2_pin_;
GPIOPin *display_data_3_pin_;
GPIOPin *display_data_4_pin_;
GPIOPin *display_data_5_pin_;
GPIOPin *display_data_6_pin_;
GPIOPin *display_data_7_pin_;
GPIOPin *ckv_pin_;
GPIOPin *cl_pin_;
GPIOPin *gpio0_enable_pin_;
GPIOPin *gmod_pin_;
GPIOPin *le_pin_;
GPIOPin *oe_pin_;
GPIOPin *powerup_pin_;
GPIOPin *sph_pin_;
GPIOPin *spv_pin_;
GPIOPin *vcom_pin_;
GPIOPin *wakeup_pin_;
};
} // namespace inkplate6
} // namespace esphome
#endif