ESPHome 2026.1.5
Loading...
Searching...
No Matches
epaper_spi_spectra_e6.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4
5#include "esphome/core/log.h"
6
7namespace esphome::epaper_spi {
8static constexpr const char *const TAG = "epaper_spi.6c";
9static constexpr unsigned char GRAY_THRESHOLD = 50;
10
22
23static uint8_t color_to_hex(Color color) {
24 // --- Step 1: Check for Grayscale (Black or White) ---
25 // We define "grayscale" as a color where the min and max components
26 // are close to each other.
27 unsigned char max_rgb = std::max({color.r, color.g, color.b});
28 unsigned char min_rgb = std::min({color.r, color.g, color.b});
29
30 if ((max_rgb - min_rgb) < GRAY_THRESHOLD) {
31 // It's a shade of gray. Map to BLACK or WHITE.
32 // We split the luminance at the halfway point (382 = (255*3)/2)
33 if ((static_cast<int>(color.r) + color.g + color.b) > 382) {
34 return WHITE;
35 }
36 return BLACK;
37 }
38 // --- Step 2: Check for Primary/Secondary Colors ---
39 // If it's not gray, it's a color. We check which components are
40 // "on" (over 128) vs "off". This divides the RGB cube into 8 corners.
41 bool r_on = (color.r > 128);
42 bool g_on = (color.g > 128);
43 bool b_on = (color.b > 128);
44
45 if (r_on && g_on && !b_on) {
46 return YELLOW;
47 }
48 if (r_on && !g_on && !b_on) {
49 return RED;
50 }
51 if (!r_on && g_on && !b_on) {
52 return GREEN;
53 }
54 if (!r_on && !g_on && b_on) {
55 return BLUE;
56 }
57 // Handle "impure" colors (Cyan, Magenta)
58 if (!r_on && g_on && b_on) {
59 // Cyan (G+B) -> Closest is Green or Blue. Pick Green.
60 return GREEN;
61 }
62 if (r_on && !g_on) {
63 // Magenta (R+B) -> Closest is Red or Blue. Pick Red.
64 return RED;
65 }
66 // Handle the remaining corners (White-ish, Black-ish)
67 if (r_on) {
68 // All high (but not gray) -> White
69 return WHITE;
70 }
71 // !r_on && !g_on && !b_on
72 // All low (but not gray) -> Black
73 return BLACK;
74}
75
77 ESP_LOGV(TAG, "Power on");
78 this->command(0x04);
79}
80
82 ESP_LOGV(TAG, "Power off");
83 this->cmd_data(0x02, {0x00});
84}
85
87 ESP_LOGV(TAG, "Refresh");
88 this->cmd_data(0x12, {0x00});
89}
90
92 ESP_LOGV(TAG, "Deep sleep");
93 this->cmd_data(0x07, {0xA5});
94}
95
97 // If clipping is active, fall back to base implementation
98 if (this->get_clipping().is_set()) {
99 EPaperBase::fill(color);
100 return;
101 }
102
103 auto pixel_color = color_to_hex(color);
104
105 // We store 2 pixels per byte
106 this->buffer_.fill(pixel_color + (pixel_color << 4));
107}
108
110 // clear buffer to white, just like real paper.
111 this->fill(COLOR_ON);
112}
113
114void HOT EPaperSpectraE6::draw_pixel_at(int x, int y, Color color) {
115 if (!this->rotate_coordinates_(x, y))
116 return;
117 auto pixel_bits = color_to_hex(color);
118 uint32_t pixel_position = x + y * this->get_width_internal();
119 uint32_t byte_position = pixel_position / 2;
120 auto original = this->buffer_[byte_position];
121 if ((pixel_position & 1) != 0) {
122 this->buffer_[byte_position] = (original & 0xF0) | pixel_bits;
123 } else {
124 this->buffer_[byte_position] = (original & 0x0F) | (pixel_bits << 4);
125 }
126}
127
129 const uint32_t start_time = App.get_loop_component_start_time();
130 const size_t buffer_length = this->buffer_length_;
131 if (this->current_data_index_ == 0) {
132 this->command(0x10);
133 }
134
135 size_t buf_idx = 0;
136 uint8_t bytes_to_send[MAX_TRANSFER_SIZE];
137 while (this->current_data_index_ != buffer_length) {
138 bytes_to_send[buf_idx++] = this->buffer_[this->current_data_index_++];
139
140 if (buf_idx == sizeof bytes_to_send) {
141 this->start_data_();
142 this->write_array(bytes_to_send, buf_idx);
143 this->disable();
144 ESP_LOGV(TAG, "Wrote %d bytes at %ums", buf_idx, (unsigned) millis());
145 buf_idx = 0;
146
147 if (millis() - start_time > MAX_TRANSFER_TIME) {
148 // Let the main loop run and come back next loop
149 return false;
150 }
151 }
152 }
153 // Finished the entire dataset
154 if (buf_idx != 0) {
155 this->start_data_();
156 this->write_array(bytes_to_send, buf_idx);
157 this->disable();
158 }
159 this->current_data_index_ = 0;
160 return true;
161}
162} // namespace esphome::epaper_spi
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
Rect get_clipping() const
Get the current the clipping rectangle.
Definition display.cpp:764
void command(uint8_t value)
void fill(Color color) override
Definition epaper_spi.h:84
bool rotate_coordinates_(int &x, int &y)
Check and rotate coordinates based on the transform flags.
split_buffer::SplitBuffer buffer_
Definition epaper_spi.h:165
void cmd_data(uint8_t command, const uint8_t *ptr, size_t length)
void draw_pixel_at(int x, int y, Color color) override
void fill(uint8_t value) const
Fill the entire buffer with a single byte value.
const Color COLOR_ON(255, 255, 255, 255)
Turn the pixel ON.
Definition display.h:303
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
uint8_t g
Definition color.h:34
uint8_t b
Definition color.h:38
uint8_t r
Definition color.h:30
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6