17static constexpr uint32_t MAX_TRANSFER_TIME_MS = 20;
22 if (!this->
queue_.push_back(
Op{type, a, b})) {
23 ESP_LOGE(TAG,
"Op queue overflow (cap=%u); dropping op type=%u",
static_cast<unsigned>(OP_QUEUE_SIZE),
24 static_cast<unsigned>(
type));
29 if (!this->
queue_.push_front(
Op{type, a, b})) {
30 ESP_LOGE(TAG,
"Op queue overflow (cap=%u); dropping op type=%u",
static_cast<unsigned>(OP_QUEUE_SIZE),
31 static_cast<unsigned>(
type));
47 if (this->
queue_.empty()) {
63 if (needs_hardware_ready && this->
is_busy_()) {
67 ESP_LOGV(TAG,
"HW_RDY is LOW (busy) in phase %u, elapsed=%" PRId32
"ms",
static_cast<unsigned>(this->
phase_),
70 ESP_LOGW(TAG,
"Busy timeout (%" PRIu32
"ms) in phase %u, recovering", elapsed,
71 static_cast<unsigned>(this->
phase_));
82 ESP_LOGV(TAG,
"Processing op type=%u a=0x%04X b=0x%04X",
static_cast<unsigned>(op.
type), op.
a, op.
b);
155 ESP_LOGV(TAG,
"Phase %u -> %u",
static_cast<unsigned>(this->
phase_),
static_cast<unsigned>(next));
193 if (this->
dev_info_.
panel_width == 0 || this->dev_info_.panel_width > 2048 || this->dev_info_.panel_height == 0 ||
194 this->dev_info_.panel_height > 2048 || this->dev_info_.panel_width == 0xFFFF ||
195 this->dev_info_.panel_height == 0xFFFF) {
197 ESP_LOGW(TAG,
"DevInfo attempt %u returned invalid data (W=%u H=%u), retrying...", this->
dev_info_attempts_,
204 ESP_LOGE(TAG,
"DevInfo invalid after %u attempts (W=%u H=%u)", this->
dev_info_attempts_,
206 this->
mark_failed(LOG_STR(
"Failed to read IT8951 device info"));
211 if (this->
dev_info_.
panel_width != this->width_ || this->dev_info_.panel_height != this->height_) {
212 ESP_LOGE(TAG,
"Panel dimension mismatch: configured=%ux%u, DevInfo=%ux%u. Check model/dimensions settings.",
214 this->
mark_failed(LOG_STR(
"IT8951 panel dimensions do not match DevInfo"));
252 ESP_LOGCONFIG(TAG,
"IT8951 setup complete");
261 ESP_LOGD(TAG,
"Nothing to update");
302 ESP_LOGCONFIG(TAG,
"Setting up IT8951...");
310 pin->digital_write(
true);
333 if (this->
buffer_ ==
nullptr) {
334 this->
mark_failed(LOG_STR(
"Failed to allocate IT8951 framebuffer"));
356 pin->digital_write(
false);
435 ESP_LOGV(TAG,
"Enqueueing refresh ops: grayscale=%u", this->
grayscale_);
477static constexpr uint32_t INTRA_CS_READY_TIMEOUT_US = 50;
479static inline void wait_for_hardware_ready(
GPIOPin *busy_pin) {
480 if (busy_pin ==
nullptr)
484 if (waited >= INTRA_CS_READY_TIMEOUT_US)
494 wait_for_hardware_ready(this->
busy_pin_);
502 wait_for_hardware_ready(this->
busy_pin_);
512 wait_for_hardware_ready(this->
busy_pin_);
522 wait_for_hardware_ready(this->
busy_pin_);
523 for (uint16_t i = 0; i <
count; i++)
533 wait_for_hardware_ready(this->
busy_pin_);
535 wait_for_hardware_ready(this->
busy_pin_);
550 wait_for_hardware_ready(this->
busy_pin_);
552 wait_for_hardware_ready(this->
busy_pin_);
553 auto *words =
reinterpret_cast<uint16_t *
>(&this->
dev_info_);
555 for (
uint32_t i = 0; i < word_count; i++) {
580 args[0] =
static_cast<uint16_t
>((LDIMG_B_ENDIAN << 8) | (PIXEL_4BPP << 4));
588 args[0] =
static_cast<uint16_t
>((LDIMG_L_ENDIAN << 8) | (PIXEL_8BPP << 4));
589 args[1] =
static_cast<uint16_t
>(this->
area_x_ / 8);
591 args[3] =
static_cast<uint16_t
>(this->
area_w_ / 8);
601 const uint16_t area_y = this->
area_y_;
602 const uint16_t area_h = this->
area_h_;
609 const uint16_t bytes_per_row =
611 const uint16_t row_x_bytes =
617 wait_for_hardware_ready(this->
busy_pin_);
626 if (
millis() - start_time >= MAX_TRANSFER_TIME_MS)
639 const uint16_t args[5] = {
645 const uint16_t args[7] = {
658 ESP_LOGV(TAG,
"Checking LUT idle, read_result_=0x%04X", this->
read_result_);
674 const uint16_t modified =
static_cast<uint16_t
>(this->
read_result_ | (1U << 2));
702 uint16_t temp_max = this->
x_high_ > 0 ?
static_cast<uint16_t
>(this->
x_high_ - 1) : 0;
703 temp_max =
static_cast<uint16_t
>(temp_max | 0x001F);
704 if (temp_max >= this->
width_)
705 temp_max =
static_cast<uint16_t
>(this->
width_ - 1);
706 this->
x_high_ =
static_cast<uint16_t
>(temp_max + 1);
714 const uint16_t
x = this->
x_low_;
715 const uint16_t
y = this->
y_low_;
716 const uint16_t width =
static_cast<uint16_t
>(this->
x_high_ - this->
x_low_);
717 const uint16_t height =
static_cast<uint16_t
>(this->
y_high_ - this->
y_low_);
720 ESP_LOGE(TAG,
"Dirty region (%u,%u %ux%u) out of bounds",
x,
y, width, height);
748 ESP_LOGV(TAG,
"Update: %ux%u@%u,%u mode=%u (%s)", width, height,
x,
y,
static_cast<unsigned>(
mode),
749 this->
grayscale_ ? LOG_STR_LITERAL(
"grayscale") : LOG_STR_LITERAL(
"mono"));
799 ESP_LOGE(TAG,
"Recovery failed after %u attempts; giving up. Check BUSY pin wiring and power.",
801 this->
mark_failed(LOG_STR(
"IT8951 recovery exhausted"));
807 ESP_LOGW(TAG,
"Recovering (attempt %u): hardware-resetting controller (was in phase %u)", this->
recovery_attempts_,
808 static_cast<unsigned>(this->
phase_));
878static uint8_t quantize_8bit_to_nibble(uint8_t value) {
879 uint8_t nibble =
static_cast<uint8_t
>((
static_cast<uint16_t
>(value) + 8) >> 4);
880 return nibble > 0x0F ? 0x0F : nibble;
883static uint8_t color_to_nibble(
const Color &color) {
886 if (color.
w == 0xFF && color.
r == color.
g && color.
g == color.
b)
887 return quantize_8bit_to_nibble(color.
r);
891 if (color.
raw_32 == 0xFFFFFFFF)
905static uint16_t dither_threshold(uint16_t
x, uint16_t
y) {
906 static const uint8_t BAYER4[16] = {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
907 return static_cast<uint16_t
>(BAYER4[((
y & 3) << 2) | (
x & 3)] * 4096u + 2048u);
917 uint8_t packed = color_to_nibble(color);
919 packed = 0x0F - packed;
922 fill_byte =
static_cast<uint8_t
>((packed << 4) | packed);
924 fill_byte = (packed <= 0x07) ? 0xFF : 0x00;
944 uint8_t nibble = color_to_nibble(color);
946 nibble =
static_cast<uint8_t
>(0x0F - nibble);
951 auto lum =
static_cast<uint16_t
>(77u * color.
r + 151u * color.
g + 29u * color.
b);
953 lum =
static_cast<uint16_t
>(65535u - lum);
957 const uint16_t threshold = this->
dithering_ ? dither_threshold(
x,
y) : 32768;
976 const size_t line_stride =
static_cast<size_t>(
x_offset) +
w +
x_pad;
977 for (
int y = 0;
y <
h;
y++) {
979 size_t source_idx = (
static_cast<size_t>(
y_offset) +
y) * line_stride +
x_offset;
980 for (
int x = 0;
x <
w;
x++, source_idx++) {
984 const size_t i = source_idx * 2;
990 const size_t i = source_idx * 3;
995 color_value =
ptr[source_idx];
1001 if (nx < 0 || ny < 0 || nx >= this->
width_ || ny >= this->
height_)
1015 const int nx_lo = std::max(0, std::min(x0, x1));
1016 const int ny_lo = std::max(0, std::min(y0, y1));
1017 const int nx_hi = std::min(this->
width_ - 1, std::max(x0, x1));
1018 const int ny_hi = std::min(this->
height_ - 1, std::max(y0, y1));
1019 if (nx_hi >= nx_lo && ny_hi >= ny_lo) {
1034 const uint16_t group =
static_cast<uint16_t
>(
x >> 4);
1035 const uint8_t sub =
static_cast<uint8_t
>(
x & 0x0F);
1036 const uint16_t byte_index =
static_cast<uint16_t
>(group * 2u + (sub < 8u ? 1u : 0u));
1037 const uint8_t mask =
static_cast<uint8_t
>(1u << (sub & 0x07));
1042 this->
buffer_[index] &=
static_cast<uint8_t
>(~mask);
1048 uint8_t buf = this->
buffer_[index];
1050 buf = (buf & 0xF0) | nibble;
1052 buf = (buf & 0x0F) |
static_cast<uint8_t
>(nibble << 4);
1060 LOG_DISPLAY(
"",
"IT8951 E-Paper",
this);
1061 char force_temperature[24];
1063 snprintf(force_temperature,
sizeof(force_temperature),
"%d °C", this->
force_temperature_);
1065 strncpy(force_temperature,
"(controller default)",
sizeof(force_temperature));
1066 force_temperature[
sizeof(force_temperature) - 1] =
'\0';
1071 "\n Dimensions: %dx%d"
1072 "\n Buffer: %u bytes"
1073 "\n Image buffer addr: 0x%04X%04X"
1074 "\n VCOM: %.02fV (set selector 0x%04X)"
1075 "\n Force temperature: %s"
1076 "\n Display command: %s"
1077 "\n Sleep when done: %s"
1078 "\n Full update every: %u"
1079 "\n Inverted colors: %s"
1080 "\n Pixel format: %s"
1081 "\n Reset duration: %" PRIu32
"ms",
1086 : LOG_STR_LITERAL(
"DPY_BUF_AREA (0x0037)"),
1090 LOG_PIN(
" Busy Pin: ", this->
busy_pin_);
1091 LOG_PIN(
" CS Pin: ", this->
cs_);
1092 LOG_UPDATE_INTERVAL(
this);
BedjetMode mode
BedJet operating mode.
void feed_wdt()
Feed the task watchdog.
void mark_failed()
Mark this component as failed.
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
void stop()
Stop running the loop continuously.
void start()
Start running the loop continuously.
An STL allocator that uses SPI or internal RAM.
static Color to_color(uint32_t colorcode, ColorBitness color_bitness=ColorBitness::COLOR_BITNESS_888, bool right_bit_aligned=true)
Unpacks a color of the given bitness, red in the most significant bits.
int int int const uint8_t ColorOrder ColorBitness bitness
void ESPHOME_ALWAYS_INLINE feed_wdt_per_pixel_()
Watchdog feed for per pixel loops.
int int int const uint8_t ColorOrder ColorBitness bool int int y_offset
virtual void fill(Color color)
Fill the entire screen with the given color.
int int int const uint8_t ColorOrder ColorBitness bool int int int x_pad
int int int const uint8_t * ptr
bool ESPHOME_ALWAYS_INLINE is_point_clipped(int x, int y) const
Whether (x, y) falls outside the active clipping rectangle.
int int int const uint8_t ColorOrder ColorBitness bool big_endian
Rect get_clipping() const
Get the current the clipping rectangle.
int int int const uint8_t ColorOrder ColorBitness bool int x_offset
DisplayRotation rotation_
virtual void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad)
Draws a block for draw_pixels_at(); the parameters are the same.
uint8_t full_update_every_
void on_powerdown() override
void update_mode(UpdateMode mode)
bool use_legacy_dpy_area_
void start_update_(UpdateMode mode)
void spi_write_args_(const uint16_t *args, uint16_t count)
void enqueue_init_temp_()
void enqueue_update_refresh_()
HighFrequencyLoopRequester high_freq_
void reset_dirty_region_()
uint32_t partial_update_count_
int16_t force_temperature_
void set_phase_(Phase next)
void spi_cmd_(uint16_t cmd)
UpdateMode default_update_mode_
bool rotate_coordinates_(int &x, int &y)
void dump_config() override
void enqueue_(OpType type, uint16_t a=0, uint16_t b=0)
uint16_t compute_row_width_() const
void spi_write_reg_(uint16_t addr, uint16_t value)
bool force_temperature_set_
void prepend_(OpType type, uint16_t a=0, uint16_t b=0)
std::vector< GPIOPin * > enable_pins_
int get_height_internal() override
void enqueue_init_dev_info_()
StaticOpQueue< Op, OP_QUEUE_SIZE > queue_
uint32_t phase_started_at_
void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
void set_mono_pixel_(uint16_t x, uint16_t y, bool value) const
void update_effective_transform_()
void enqueue_update_transfer_()
void spi_read_dev_info_()
uint8_t dev_info_attempts_
void enqueue_init_vcom_()
uint32_t configured_data_rate_
void enqueue_update_sleep_()
void write_pixel_native_(uint16_t x, uint16_t y, const Color &color) const
uint32_t update_started_at_
UpdateMode pending_update_mode_
void apply_transform_(int &x, int &y) const
uint8_t effective_transform_
void enqueue_init_reset_()
void process_op_(const Op &op)
void fill(Color color) override
uint16_t spi_read_word_()
void spi_write_word_(uint16_t value)
void on_safe_shutdown() override
static constexpr uint32_t BUSY_TIMEOUT_MS
bool prepare_update_region_(UpdateMode &mode)
void set_gray_pixel_(uint16_t x, uint16_t y, uint8_t nibble) const
uint8_t recovery_attempts_
void op_xfer_area_args_()
void op_check_lut_idle_()
int get_width_internal() override
void draw_pixel_at(int x, int y, Color color) override
void set_data_rate(uint32_t data_rate)
void write_byte16(uint16_t data)
void spi_setup() override
uint8_t transfer_byte(uint8_t data)
void write_array(const uint8_t *data, size_t length)
void spi_teardown() override
@ DISPLAY_ROTATION_270_DEGREES
@ DISPLAY_ROTATION_180_DEGREES
@ DISPLAY_ROTATION_90_DEGREES
constexpr uint8_t rec601_luma(Color color)
Rec.601 luma of an RGB color, for optimum perceptual brightness.
static void int int int int const uint8_t ColorOrder ColorBitness bool big_endian
static void int int int int const uint8_t ColorOrder ColorBitness bitness
static void int int int int const uint8_t int x_offset
static void int int y_start
static void int int int int const uint8_t int int y_offset
static void int int int w
ESPHOME_LOG_TAG(TAG, "display")
static void int int int int h
static void int int int int const uint8_t * ptr
static void int int int int const uint8_t int int int x_pad
T clamp_at_most(T value, U max)
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
T clamp_at_least(T value, U min)
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.