ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
epaper_spi.h
Go to the documentation of this file.
1#pragma once
2
3#include "colorconv.h"
9
10namespace esphome::epaper_spi {
11using namespace display;
12
13enum class EPaperState : uint8_t {
14 IDLE, // not doing anything
15 UPDATE, // update the buffer
16 RESET, // drive reset low (active)
17 RESET_END, // drive reset high (inactive)
18
19 SHOULD_WAIT, // states higher than this should wait for the display to be not busy
20 INITIALISE, // send the init sequence
21 TRANSFER_DATA, // transfer data to the display
22 POWER_ON, // power on the display
23 REFRESH_SCREEN, // send refresh command
24 POWER_OFF, // power off the display
25 DEEP_SLEEP, // deep sleep the display
26};
27
28static constexpr uint8_t NONE = 0;
29static constexpr uint8_t MIRROR_X = 1;
30static constexpr uint8_t MIRROR_Y = 2;
31static constexpr uint8_t SWAP_XY = 4;
32
33static constexpr uint32_t MAX_TRANSFER_TIME = 10; // Transfer in 10ms blocks to allow the loop to run
34static constexpr size_t MAX_TRANSFER_SIZE = 128;
35// Longer busy waits are the waveform or a stuck pin, and are polled at the normal loop pace
36static constexpr uint32_t FAST_BUSY_POLL_MS = 50;
37static constexpr uint8_t DELAY_FLAG = 0xFF;
38
39class EPaperBase : public Display,
40 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
41 spi::DATA_RATE_2MHZ> {
42 public:
43 EPaperBase(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence = nullptr,
44 size_t init_sequence_length = 0, DisplayType display_type = DISPLAY_TYPE_BINARY,
45 uint8_t bits_per_pixel = 1, uint8_t planes = 1)
46 : name_(name),
47 width_(width),
48 row_width_((width * bits_per_pixel + 7) / 8),
49 height_(height),
50 bits_per_pixel_(bits_per_pixel),
51 planes_(planes),
52 init_sequence_(init_sequence),
53 init_sequence_length_(init_sequence_length),
54 display_type_(display_type),
55 buffer_length_((size_t) this->row_width_ * height * planes) {}
56 void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; }
57 float get_setup_priority() const override;
59 void set_busy_pin(GPIOPin *busy) { this->busy_pin_ = busy; }
60 void set_enable_pins(std::vector<GPIOPin *> enable_pins) { this->enable_pins_ = std::move(enable_pins); }
61 void set_reset_duration(uint32_t reset_duration) { this->reset_duration_ = reset_duration; }
62 void set_transform(uint8_t transform) {
63 this->transform_ = transform;
65 }
66 void set_rotation(DisplayRotation rotation) override {
67 Display::set_rotation(rotation);
69 }
70 void set_full_update_every(uint8_t full_update_every) { this->full_update_every_ = full_update_every; }
71 void dump_config() override;
72
73 void command(uint8_t value);
74 void cmd_data(uint8_t command, const uint8_t *ptr, size_t length);
75
76 // variant with in-place initializer list
77 void cmd_data(uint8_t command, std::initializer_list<uint8_t> data) {
78 this->cmd_data(command, data.begin(), data.size());
79 }
80
81 void update() override;
82 void loop() override;
83
84 void setup() override;
85
86 void on_safe_shutdown() override;
87
88 DisplayType get_display_type() override { return this->display_type_; };
89
90 // Default implementation for monochrome displays
91 void fill(Color color) override {
92 // If clipping is active, fall back to base implementation
93 if (this->get_clipping().is_set()) {
94 Display::fill(color);
95 return;
96 }
97
98 auto pixel_color = color_to_mono(color) ? 0xFF : 0x00;
99
100 // We store 8 pixels per byte
101 this->buffer_.fill(pixel_color);
102 this->mark_all_drawn_();
103 }
104
105 void clear() override {
106 // clear buffer to white, just like real paper.
107 this->fill(COLOR_ON);
108 }
109
110 int get_width() override { return this->effective_transform_ & SWAP_XY ? this->height_ : this->width_; }
111 int get_height() override { return this->effective_transform_ & SWAP_XY ? this->width_ : this->height_; }
112 bool pixel_axes_swapped() const override { return this->effective_transform_ & SWAP_XY; }
113 void draw_pixel_at(int x, int y, Color color) override;
114 // Make the next update a full one. Applied when that update starts, so one in progress is not affected.
116 // True from the start of an update until the display has refreshed and gone back to sleep.
117 bool is_updating() const { return this->state_ != EPaperState::IDLE; }
118
119 protected:
120 void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness,
121 bool big_endian, int x_offset, int y_offset, int x_pad) override;
122
123 int get_height_internal() override { return this->height_; };
124 int get_width_internal() override { return this->width_; };
125 bool is_using_partial_update_() const { return this->full_update_every_ > 1; }
126 void process_state_();
127
128 const char *epaper_state_to_string_();
129 bool is_idle_() const;
130 void setup_pins_() const;
131 virtual bool reset();
133 bool hardware_reset_();
134 virtual bool initialise(bool partial);
135 void send_init_sequence_(const uint8_t *sequence, size_t length);
136 void wait_for_idle_(bool should_wait);
137 void abandon_update_();
138 bool init_buffer_(size_t buffer_length);
139 // Allocates the copy of the previous frame; only drivers that diff against it (needs_previous_frame) call this
140 bool init_sent_frame_(size_t length);
141 void record_sent_(size_t index, const uint8_t *data, size_t length) {
142 if (this->sent_.is_valid()) {
143 this->sent_.write(index, data, length);
144 this->sent_count_ += length;
145 }
146 }
147 // Whether the driver reads the previous frame (sent_) for its differential refresh
148 virtual bool needs_previous_frame() const { return false; }
149 // FNV-1a hash of the current frame buffer, to detect an unchanged frame without a full copy
150 uint32_t buffer_hash_() const;
151 void reset_bounds_();
152 void set_full_bounds_();
153 // For a draw that writes the whole buffer without going through rotate_coordinates_()
155 this->set_full_bounds_();
156 this->drawn_since_hash_ = true;
157 }
159 bool rotate_coordinates_(int &x, int &y);
160 void draw_rgb565_block_(uint8_t *buffer, int x_start, int y_start, int w, int h, const uint8_t *ptr, bool big_endian,
161 int x_offset, int y_offset, int x_pad);
162 // Whether the buffer is a single plane of 1 bit per pixel, as draw_pixel_at() writes it
163 bool buffer_is_mono_() const { return this->bits_per_pixel_ == 1 && this->planes_ == 1; }
164
172 virtual bool transfer_data() = 0;
173 // Called when an update is abandoned in the middle of transfer_data(), to release anything it left open
174 virtual void abort_transfer() {}
178 virtual void refresh_screen(bool partial) = 0;
179
183 virtual void power_on() = 0;
187 virtual void power_off() = 0;
188
194 virtual bool after_refresh() { return true; }
195
199 virtual void deep_sleep() = 0;
200
201 void set_state_(EPaperState state, uint16_t delay = 0);
202
203 void start_data_();
204
205 // properties initialised in the constructor
206 const char *name_;
207 uint16_t width_;
208 uint16_t row_width_; // width of a row of one plane in bytes
209 uint16_t height_;
211 uint8_t planes_; // the buffer holds this many planes of row_width_ * height_ bytes, one after another
212 const uint8_t *init_sequence_;
215
217 size_t current_data_index_{}; // used by data transfer to track progress
219 split_buffer::SplitBuffer sent_{}; // the previous frame, kept only by drivers that diff against it
220 bool sent_valid_{}; // sent_ holds a whole frame
221 size_t sent_count_{}; // bytes recorded into sent_ by the update in progress
222 uint32_t shown_hash_{}; // hash of the frame on the panel, to skip an unchanged update
223 bool shown_hash_valid_{}; // shown_hash_ holds the hash of a frame whose refresh completed
224 bool sent_hash_intact_{}; // the buffer was unchanged by the end of the push of shown_hash_
225 bool drawn_since_hash_{}; // something was drawn after shown_hash_ was taken
229 std::vector<GPIOPin *> enable_pins_{};
232 uint32_t delay_until_{}; // timestamp until which to delay processing
233 uint16_t next_delay_{}; // milliseconds to delay before next state
234 uint8_t transform_{};
236 uint8_t update_count_{};
238 bool partial_{}; // the update in progress is a partial one; set when UPDATE moves to RESET
239 // these values represent the bounds of the updated buffer. Note that x_high and y_high
240 // point to the pixel past the last one updated, i.e. may range up to width/height.
241 uint16_t x_low_{}, y_low_{}, x_high_{}, y_high_{};
242
244#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
246#endif
247#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
249#endif
250
251 // properties with specific initialisers go last
253 // Longer than any panel takes to refresh; past it the busy pin is assumed stuck
257};
258
259} // namespace esphome::epaper_spi
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:2231
int int int const uint8_t ColorOrder ColorBitness bitness
Definition display.h:392
int int int const uint8_t ColorOrder ColorBitness bool int int y_offset
Definition display.h:392
virtual void fill(Color color)
Fill the entire screen with the given color.
Definition display.cpp:16
int int int const uint8_t ColorOrder ColorBitness bool int int int x_pad
Definition display.h:392
int int int const uint8_t * ptr
Definition display.h:391
virtual void set_rotation(DisplayRotation rotation)
Internal method to set the display rotation with.
Definition display.cpp:18
int int int const uint8_t ColorOrder ColorBitness bool big_endian
Definition display.h:392
Rect get_clipping() const
Get the current the clipping rectangle.
Definition display.cpp:826
int int int const uint8_t ColorOrder ColorBitness bool int x_offset
Definition display.h:392
virtual void power_off()=0
Power the display off.
void command(uint8_t value)
void set_rotation(DisplayRotation rotation) override
Definition epaper_spi.h:66
void set_enable_pins(std::vector< GPIOPin * > enable_pins)
Definition epaper_spi.h:60
void set_transform(uint8_t transform)
Definition epaper_spi.h:62
void fill(Color color) override
Definition epaper_spi.h:91
void process_state_()
Process the state machine.
virtual void deep_sleep()=0
Place the display into deep sleep.
void draw_pixel_at(int x, int y, Color color) override
Default implementation for monochrome displays where 8 pixels are packed to a byte.
bool init_sent_frame_(size_t length)
virtual bool after_refresh()
Called after the refresh, before power off, repeatedly until it returns true.
Definition epaper_spi.h:194
bool pixel_axes_swapped() const override
Definition epaper_spi.h:112
bool rotate_coordinates_(int &x, int &y)
Check and rotate coordinates based on the transform flags.
void record_sent_(size_t index, const uint8_t *data, size_t length)
Definition epaper_spi.h:141
void loop() override
Called during the loop task.
virtual void refresh_screen(bool partial)=0
Refresh the screen after data transfer.
virtual bool initialise(bool partial)
void set_dc_pin(GPIOPin *dc_pin)
Definition epaper_spi.h:56
void send_init_sequence_(const uint8_t *sequence, size_t length)
void set_reset_pin(GPIOPin *reset)
Definition epaper_spi.h:58
virtual bool transfer_data()=0
Methods that must be implemented by concrete classes to control the display.
split_buffer::SplitBuffer buffer_
Definition epaper_spi.h:218
void cmd_data(uint8_t command, const uint8_t *ptr, size_t length)
void cmd_data(uint8_t command, std::initializer_list< uint8_t > data)
Definition epaper_spi.h:77
virtual bool needs_previous_frame() const
Definition epaper_spi.h:148
void draw_rgb565_block_(uint8_t *buffer, int x_start, int y_start, int w, int h, const uint8_t *ptr, bool big_endian, int x_offset, int y_offset, int x_pad)
split_buffer::SplitBuffer sent_
Definition epaper_spi.h:219
void set_state_(EPaperState state, uint16_t delay=0)
const char * epaper_state_to_string_()
virtual void power_on()=0
Power the display on.
HighFrequencyLoopRequester high_frequency_loop_
Definition epaper_spi.h:231
void set_reset_duration(uint32_t reset_duration)
Definition epaper_spi.h:61
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_full_update_every(uint8_t full_update_every)
Definition epaper_spi.h:70
EPaperBase(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence=nullptr, size_t init_sequence_length=0, DisplayType display_type=DISPLAY_TYPE_BINARY, uint8_t bits_per_pixel=1, uint8_t planes=1)
Definition epaper_spi.h:43
DisplayType get_display_type() override
Definition epaper_spi.h:88
void wait_for_idle_(bool should_wait)
bool hardware_reset_()
The reset pin part of reset(), for a driver that must skip what its parent adds to it.
float get_setup_priority() const override
bool init_buffer_(size_t buffer_length)
void set_busy_pin(GPIOPin *busy)
Definition epaper_spi.h:59
std::vector< GPIOPin * > enable_pins_
Definition epaper_spi.h:229
The SPIDevice is what components using the SPI will create.
Definition spi.h:456
A SplitBuffer allocates a large memory buffer potentially as multiple smaller buffers to facilitate a...
void fill(uint8_t value) const
Fill the entire buffer with a single byte value.
void write(size_t index, const uint8_t *data, size_t length)
bool state
Definition fan.h:2
uint32_t buffer
Definition gif.cpp:50
constexpr Color COLOR_ON(255, 255, 255, 255)
Turn the pixel ON.
static void int x_start
Definition display.cpp:62
constexpr uint8_t color_to_mono(Color color)
Map an RGB color to a single monochrome bit: 1 = white, 0 = black.
void HOT delay(uint32_t ms)
Definition hal.cpp:85
static void uint32_t
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6