ESPHome 2025.12.0-dev
Loading...
Searching...
No Matches
epaper_spi.cpp
Go to the documentation of this file.
1#include "epaper_spi.h"
2#include <cinttypes>
5#include "esphome/core/log.h"
6
8
9static const char *const TAG = "epaper_spi";
10
11static constexpr const char *const EPAPER_STATE_STRINGS[] = {
12 "IDLE", "UPDATE", "RESET", "RESET_END",
13
14 "SHOULD_WAIT", "INITIALISE", "TRANSFER_DATA", "POWER_ON", "REFRESH_SCREEN", "POWER_OFF", "DEEP_SLEEP",
15};
16
18 if (auto idx = static_cast<unsigned>(this->state_); idx < std::size(EPAPER_STATE_STRINGS))
19 return EPAPER_STATE_STRINGS[idx];
20 return "Unknown";
21}
22
24 if (!this->init_buffer_(this->buffer_length_)) {
25 this->mark_failed("Failed to initialise buffer");
26 return;
27 }
28 this->setup_pins_();
29 this->spi_setup();
30}
31
32bool EPaperBase::init_buffer_(size_t buffer_length) {
33 if (!this->buffer_.init(buffer_length)) {
34 return false;
35 }
36 this->clear();
37 return true;
38}
39
41 this->dc_pin_->setup(); // OUTPUT
42 this->dc_pin_->digital_write(false);
43
44 if (this->reset_pin_ != nullptr) {
45 this->reset_pin_->setup(); // OUTPUT
46 this->reset_pin_->digital_write(true);
47 }
48
49 if (this->busy_pin_ != nullptr) {
50 this->busy_pin_->setup(); // INPUT
51 }
52}
53
55
56void EPaperBase::command(uint8_t value) {
57 this->start_command_();
58 this->write_byte(value);
59 this->end_command_();
60}
61
62void EPaperBase::data(uint8_t value) {
63 this->start_data_();
64 this->write_byte(value);
65 this->end_data_();
66}
67
68// write a command followed by zero or more bytes of data.
69// The command is the first byte, length is the length of data only in the second byte, followed by the data.
70// [COMMAND, LENGTH, DATA...]
71void EPaperBase::cmd_data(uint8_t command, const uint8_t *ptr, size_t length) {
72 ESP_LOGVV(TAG, "Command: 0x%02X, Length: %d, Data: %s", command, length,
73 format_hex_pretty(ptr, length, '.', false).c_str());
74
75 this->dc_pin_->digital_write(false);
76 this->enable();
77 this->write_byte(command);
78 if (length > 0) {
79 this->dc_pin_->digital_write(true);
80 this->write_array(ptr, length);
81 }
82 this->disable();
83}
84
86 if (this->busy_pin_ == nullptr) {
87 return true;
88 }
89 return !this->busy_pin_->digital_read();
90}
91
92bool EPaperBase::reset_() const {
93 if (this->reset_pin_ != nullptr) {
94 if (this->state_ == EPaperState::RESET) {
95 this->reset_pin_->digital_write(false);
96 return false;
97 }
98 this->reset_pin_->digital_write(true);
99 }
100 return true;
101}
102
104 if (this->state_ != EPaperState::IDLE) {
105 ESP_LOGE(TAG, "Display already in state %s", epaper_state_to_string_());
106 return;
107 }
109 this->enable_loop();
110}
111
112void EPaperBase::wait_for_idle_(bool should_wait) {
113#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
114 if (should_wait) {
117 }
118#endif
119 this->waiting_for_idle_ = should_wait;
120}
121
129 auto now = millis();
130 if (this->delay_until_ != 0) {
131 // using modulus arithmetic to handle wrap-around
132 int diff = now - this->delay_until_;
133 if (diff < 0) {
134 return;
135 }
136 this->delay_until_ = 0;
137 }
138 if (this->waiting_for_idle_) {
139 if (this->is_idle_()) {
140 this->waiting_for_idle_ = false;
141 ESP_LOGV(TAG, "Screen now idle after %u ms", (unsigned) (millis() - this->waiting_for_idle_start_));
142 } else {
143#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
144 if (now - this->waiting_for_idle_last_print_ >= 1000) {
145 ESP_LOGV(TAG, "Waiting for idle in state %s", this->epaper_state_to_string_());
147 }
148#endif
149 return;
150 }
151 }
152 this->process_state_();
153}
154
164 ESP_LOGV(TAG, "Process state entered in state %s", epaper_state_to_string_());
165 switch (this->state_) {
166 default:
167 ESP_LOGD(TAG, "Display is in unhandled state %s", epaper_state_to_string_());
168 this->disable_loop();
169 break;
171 this->disable_loop();
172 break;
175 if (this->reset_()) {
177 } else {
179 }
180 break;
182 this->do_update_(); // Calls ESPHome (current page) lambda
184 break;
186 this->initialise_();
188 break;
190 if (!this->transfer_data()) {
191 return; // Not done yet, come back next loop
192 }
194 break;
196 this->power_on();
198 break;
200 this->refresh_screen();
202 break;
204 this->power_off();
206 break;
208 this->deep_sleep();
210 break;
211 }
212}
213
215 ESP_LOGV(TAG, "Exit state %s", this->epaper_state_to_string_());
216 this->state_ = state;
218 if (delay != 0) {
219 this->delay_until_ = millis() + delay;
220 } else {
221 this->delay_until_ = 0;
222 }
223 ESP_LOGV(TAG, "Enter state %s, delay %u, wait_for_idle=%s", this->epaper_state_to_string_(), delay,
224 TRUEFALSE(this->waiting_for_idle_));
225}
226
228 this->dc_pin_->digital_write(false);
229 this->enable();
230}
231
233
235 this->dc_pin_->digital_write(true);
236 this->enable();
237}
239
241
243 size_t index = 0;
244
245 auto *sequence = this->init_sequence_;
246 auto length = this->init_sequence_length_;
247 while (index != length) {
248 if (length - index < 2) {
249 this->mark_failed("Malformed init sequence");
250 return;
251 }
252 const uint8_t cmd = sequence[index++];
253 if (const uint8_t x = sequence[index++]; x == DELAY_FLAG) {
254 ESP_LOGV(TAG, "Delay %dms", cmd);
255 delay(cmd);
256 } else {
257 const uint8_t num_args = x & 0x7F;
258 if (length - index < num_args) {
259 ESP_LOGE(TAG, "Malformed init sequence, cmd = %X, num_args = %u", cmd, num_args);
260 this->mark_failed();
261 return;
262 }
263 ESP_LOGV(TAG, "Command %02X, length %d", cmd, num_args);
264 this->cmd_data(cmd, sequence + index, num_args);
265 index += num_args;
266 }
267 }
268}
269
271 LOG_DISPLAY("", "E-Paper SPI", this);
272 ESP_LOGCONFIG(TAG, " Model: %s", this->name_);
273 LOG_PIN(" Reset Pin: ", this->reset_pin_);
274 LOG_PIN(" DC Pin: ", this->dc_pin_);
275 LOG_PIN(" Busy Pin: ", this->busy_pin_);
276 LOG_UPDATE_INTERVAL(this);
277}
278
279} // namespace esphome::epaper_spi
virtual 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 setup()=0
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
virtual void clear()
Clear the entire screen by filling it with OFF pixels.
Definition display.cpp:17
virtual void power_off()=0
Power the display off.
void command(uint8_t value)
void process_state_()
Process the state machine.
virtual void deep_sleep()=0
Place the display into deep sleep.
void loop() override
Called during the loop task.
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:136
void cmd_data(uint8_t command, const uint8_t *ptr, size_t length)
void set_state_(EPaperState state, uint16_t delay=0)
const char * epaper_state_to_string_()
virtual void power_on()=0
Power the display on.
void wait_for_idle_(bool should_wait)
float get_setup_priority() const override
bool init_buffer_(size_t buffer_length)
virtual void refresh_screen()=0
Refresh the screen after data transfer.
bool init(size_t total_length)
bool state
Definition fan.h:0
const float PROCESSOR
For components that use data from sensors like displays.
Definition component.cpp:60
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:317
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:31
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:30
uint16_t length
Definition tt21100.cpp:0
uint16_t x
Definition tt21100.cpp:5