mirror of
https://github.com/esphome/esphome.git
synced 2025-10-18 17:53:47 +01:00
Add support for ST7789V display module (as on TTGO T-Display) (#1050)
* TFT-LCD ST7789V of ESP32 TTGO. This patch allows you to use TFT-LCD ST7789V of ESP32 TTGO * Lots of polish and a few tweaks * Add test * Add color to core, take 1 * Where did those tabs come from? * Fix lines too long * Added color component * Linted * Rebase, SPI fix, test * Shuffle bits * One more thing...oops * Image type fix...oops * Make display_buffer use Color * Fix BGR/RGB, remove predefined colors * Fix all the things * renamed colors to color * migrate max7219 Co-authored-by: musk95 <musk95@naver.com> Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
@@ -7,8 +7,8 @@ namespace display {
|
||||
|
||||
static const char *TAG = "display";
|
||||
|
||||
const uint8_t COLOR_OFF = 0;
|
||||
const uint8_t COLOR_ON = 1;
|
||||
const Color COLOR_OFF = 0;
|
||||
const Color COLOR_ON = 1;
|
||||
|
||||
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
|
||||
this->buffer_ = new uint8_t[buffer_length];
|
||||
@@ -18,7 +18,7 @@ void DisplayBuffer::init_internal_(uint32_t buffer_length) {
|
||||
}
|
||||
this->clear();
|
||||
}
|
||||
void DisplayBuffer::fill(int color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
|
||||
void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
|
||||
void DisplayBuffer::clear() { this->fill(COLOR_OFF); }
|
||||
int DisplayBuffer::get_width() {
|
||||
switch (this->rotation_) {
|
||||
@@ -43,7 +43,7 @@ int DisplayBuffer::get_height() {
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
|
||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, int color) {
|
||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
|
||||
switch (this->rotation_) {
|
||||
case DISPLAY_ROTATION_0_DEGREES:
|
||||
break;
|
||||
@@ -63,7 +63,7 @@ void HOT DisplayBuffer::draw_pixel_at(int x, int y, int color) {
|
||||
this->draw_absolute_pixel_internal(x, y, color);
|
||||
App.feed_wdt();
|
||||
}
|
||||
void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, int color) {
|
||||
void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, Color color) {
|
||||
const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
|
||||
const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
|
||||
int32_t err = dx + dy;
|
||||
@@ -83,29 +83,29 @@ void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, int color) {
|
||||
}
|
||||
}
|
||||
}
|
||||
void HOT DisplayBuffer::horizontal_line(int x, int y, int width, int color) {
|
||||
void HOT DisplayBuffer::horizontal_line(int x, int y, int width, Color color) {
|
||||
// Future: Could be made more efficient by manipulating buffer directly in certain rotations.
|
||||
for (int i = x; i < x + width; i++)
|
||||
this->draw_pixel_at(i, y, color);
|
||||
}
|
||||
void HOT DisplayBuffer::vertical_line(int x, int y, int height, int color) {
|
||||
void HOT DisplayBuffer::vertical_line(int x, int y, int height, Color color) {
|
||||
// Future: Could be made more efficient by manipulating buffer directly in certain rotations.
|
||||
for (int i = y; i < y + height; i++)
|
||||
this->draw_pixel_at(x, i, color);
|
||||
}
|
||||
void DisplayBuffer::rectangle(int x1, int y1, int width, int height, int color) {
|
||||
void DisplayBuffer::rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
this->horizontal_line(x1, y1, width, color);
|
||||
this->horizontal_line(x1, y1 + height - 1, width, color);
|
||||
this->vertical_line(x1, y1, height, color);
|
||||
this->vertical_line(x1 + width - 1, y1, height, color);
|
||||
}
|
||||
void DisplayBuffer::filled_rectangle(int x1, int y1, int width, int height, int color) {
|
||||
void DisplayBuffer::filled_rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
// Future: Use vertical_line and horizontal_line methods depending on rotation to reduce memory accesses.
|
||||
for (int i = y1; i < y1 + height; i++) {
|
||||
this->horizontal_line(x1, i, width, color);
|
||||
}
|
||||
}
|
||||
void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, int color) {
|
||||
void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, Color color) {
|
||||
int dx = -radius;
|
||||
int dy = 0;
|
||||
int err = 2 - 2 * radius;
|
||||
@@ -128,7 +128,7 @@ void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, int colo
|
||||
}
|
||||
} while (dx <= 0);
|
||||
}
|
||||
void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, int color) {
|
||||
void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, Color color) {
|
||||
int dx = -int32_t(radius);
|
||||
int dy = 0;
|
||||
int err = 2 - 2 * radius;
|
||||
@@ -155,7 +155,7 @@ void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, int co
|
||||
} while (dx <= 0);
|
||||
}
|
||||
|
||||
void DisplayBuffer::print(int x, int y, Font *font, int color, TextAlign align, const char *text) {
|
||||
void DisplayBuffer::print(int x, int y, Font *font, Color color, TextAlign align, const char *text) {
|
||||
int x_start, y_start;
|
||||
int width, height;
|
||||
this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height);
|
||||
@@ -197,16 +197,34 @@ void DisplayBuffer::print(int x, int y, Font *font, int color, TextAlign align,
|
||||
i += match_length;
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::vprintf_(int x, int y, Font *font, int color, TextAlign align, const char *format, va_list arg) {
|
||||
void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg) {
|
||||
char buffer[256];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
if (ret > 0)
|
||||
this->print(x, y, font, color, align, buffer);
|
||||
}
|
||||
void DisplayBuffer::image(int x, int y, Image *image) {
|
||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
||||
this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? COLOR_ON : COLOR_OFF);
|
||||
void DisplayBuffer::image(int x, int y, Image *image) { this->image(x, y, COLOR_ON, image); }
|
||||
void DisplayBuffer::image(int x, int y, Color color, Image *image, bool invert) {
|
||||
if (image->get_type() == BINARY) {
|
||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
||||
if (invert)
|
||||
this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? COLOR_OFF : color);
|
||||
else
|
||||
this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? color : COLOR_OFF);
|
||||
}
|
||||
}
|
||||
} else if (image->get_type() == GRAYSCALE4) {
|
||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
||||
this->draw_pixel_at(x + img_x, y + img_y, image->get_grayscale4_pixel(img_x, img_y));
|
||||
}
|
||||
}
|
||||
} else if (image->get_type() == RGB565) {
|
||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
||||
this->draw_pixel_at(x + img_x, y + img_y, image->get_color_pixel(img_x, img_y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,7 +266,7 @@ void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font,
|
||||
break;
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::print(int x, int y, Font *font, int color, const char *text) {
|
||||
void DisplayBuffer::print(int x, int y, Font *font, Color color, const char *text) {
|
||||
this->print(x, y, font, color, TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
void DisplayBuffer::print(int x, int y, Font *font, TextAlign align, const char *text) {
|
||||
@@ -257,13 +275,13 @@ void DisplayBuffer::print(int x, int y, Font *font, TextAlign align, const char
|
||||
void DisplayBuffer::print(int x, int y, Font *font, const char *text) {
|
||||
this->print(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
void DisplayBuffer::printf(int x, int y, Font *font, int color, TextAlign align, const char *format, ...) {
|
||||
void DisplayBuffer::printf(int x, int y, Font *font, Color color, TextAlign align, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, color, align, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void DisplayBuffer::printf(int x, int y, Font *font, int color, const char *format, ...) {
|
||||
void DisplayBuffer::printf(int x, int y, Font *font, Color color, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, color, TextAlign::TOP_LEFT, format, arg);
|
||||
@@ -306,14 +324,14 @@ void DisplayBuffer::do_update_() {
|
||||
}
|
||||
}
|
||||
#ifdef USE_TIME
|
||||
void DisplayBuffer::strftime(int x, int y, Font *font, int color, TextAlign align, const char *format,
|
||||
void DisplayBuffer::strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format,
|
||||
time::ESPTime time) {
|
||||
char buffer[64];
|
||||
size_t ret = time.strftime(buffer, sizeof(buffer), format);
|
||||
if (ret > 0)
|
||||
this->print(x, y, font, color, align, buffer);
|
||||
}
|
||||
void DisplayBuffer::strftime(int x, int y, Font *font, int color, const char *format, time::ESPTime time) {
|
||||
void DisplayBuffer::strftime(int x, int y, Font *font, Color color, const char *format, time::ESPTime time) {
|
||||
this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time);
|
||||
}
|
||||
void DisplayBuffer::strftime(int x, int y, Font *font, TextAlign align, const char *format, time::ESPTime time) {
|
||||
@@ -431,10 +449,30 @@ bool Image::get_pixel(int x, int y) const {
|
||||
const uint32_t pos = x + y * width_8;
|
||||
return pgm_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||
}
|
||||
int Image::get_color_pixel(int x, int y) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return 0;
|
||||
|
||||
const uint32_t pos = (x + y * this->width_) * 2;
|
||||
int color = (pgm_read_byte(this->data_start_ + pos) << 8) + (pgm_read_byte(this->data_start_ + pos + 1));
|
||||
return color;
|
||||
}
|
||||
int Image::get_grayscale4_pixel(int x, int y) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return 0;
|
||||
const uint32_t pos = (x + y * this->width_) / 2;
|
||||
// 2 = number of pixels per byte, 4 = pixel shift
|
||||
uint8_t shift = (x % 2) * 4;
|
||||
int color = (pgm_read_byte(this->data_start_ + pos) >> shift) & 0x0f;
|
||||
return color;
|
||||
}
|
||||
int Image::get_width() const { return this->width_; }
|
||||
int Image::get_height() const { return this->height_; }
|
||||
ImageType Image::get_type() const { return this->type_; }
|
||||
Image::Image(const uint8_t *data_start, int width, int height)
|
||||
: width_(width), height_(height), data_start_(data_start) {}
|
||||
Image::Image(const uint8_t *data_start, int width, int height, int type)
|
||||
: width_(width), height_(height), type_((ImageType) type), data_start_(data_start) {}
|
||||
|
||||
DisplayPage::DisplayPage(const display_writer_t &writer) : writer_(writer) {}
|
||||
void DisplayPage::show() { this->parent_->show_page(this); }
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/color.h"
|
||||
|
||||
#ifdef USE_TIME
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
@@ -63,9 +64,11 @@ enum class TextAlign {
|
||||
};
|
||||
|
||||
/// Turn the pixel OFF.
|
||||
extern const uint8_t COLOR_OFF;
|
||||
extern const Color COLOR_OFF;
|
||||
/// Turn the pixel ON.
|
||||
extern const uint8_t COLOR_ON;
|
||||
extern const Color COLOR_ON;
|
||||
|
||||
enum ImageType { BINARY = 0, GRAYSCALE4 = 1, RGB565 = 2 };
|
||||
|
||||
enum DisplayRotation {
|
||||
DISPLAY_ROTATION_0_DEGREES = 0,
|
||||
@@ -91,7 +94,7 @@ using display_writer_t = std::function<void(DisplayBuffer &)>;
|
||||
class DisplayBuffer {
|
||||
public:
|
||||
/// Fill the entire screen with the given color.
|
||||
virtual void fill(int color);
|
||||
virtual void fill(Color color);
|
||||
/// Clear the entire screen by filling it with OFF pixels.
|
||||
void clear();
|
||||
|
||||
@@ -100,29 +103,29 @@ class DisplayBuffer {
|
||||
/// Get the height of the image in pixels with rotation applied.
|
||||
int get_height();
|
||||
/// Set a single pixel at the specified coordinates to the given color.
|
||||
void draw_pixel_at(int x, int y, int color = COLOR_ON);
|
||||
void draw_pixel_at(int x, int y, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a straight line from the point [x1,y1] to [x2,y2] with the given color.
|
||||
void line(int x1, int y1, int x2, int y2, int color = COLOR_ON);
|
||||
void line(int x1, int y1, int x2, int y2, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a horizontal line from the point [x,y] to [x+width,y] with the given color.
|
||||
void horizontal_line(int x, int y, int width, int color = COLOR_ON);
|
||||
void horizontal_line(int x, int y, int width, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a vertical line from the point [x,y] to [x,y+width] with the given color.
|
||||
void vertical_line(int x, int y, int height, int color = COLOR_ON);
|
||||
void vertical_line(int x, int y, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a rectangle with the top left point at [x1,y1] and the bottom right point at
|
||||
/// [x1+width,y1+height].
|
||||
void rectangle(int x1, int y1, int width, int height, int color = COLOR_ON);
|
||||
void rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a rectangle with the top left point at [x1,y1] and the bottom right point at [x1+width,y1+height].
|
||||
void filled_rectangle(int x1, int y1, int width, int height, int color = COLOR_ON);
|
||||
void filled_rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void circle(int center_x, int center_xy, int radius, int color = COLOR_ON);
|
||||
void circle(int center_x, int center_xy, int radius, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void filled_circle(int center_x, int center_y, int radius, int color = COLOR_ON);
|
||||
void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
@@ -133,7 +136,7 @@ class DisplayBuffer {
|
||||
* @param align The alignment of the text.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, Font *font, int color, TextAlign align, const char *text);
|
||||
void print(int x, int y, Font *font, Color color, TextAlign align, const char *text);
|
||||
|
||||
/** Print `text` with the top left at [x,y] with `font`.
|
||||
*
|
||||
@@ -143,7 +146,7 @@ class DisplayBuffer {
|
||||
* @param color The color to draw the text with.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, Font *font, int color, const char *text);
|
||||
void print(int x, int y, Font *font, Color color, const char *text);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
@@ -174,7 +177,7 @@ class DisplayBuffer {
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, Font *font, int color, TextAlign align, const char *format, ...)
|
||||
void printf(int x, int y, Font *font, Color color, TextAlign align, const char *format, ...)
|
||||
__attribute__((format(printf, 7, 8)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
@@ -186,7 +189,7 @@ class DisplayBuffer {
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, Font *font, int color, const char *format, ...) __attribute__((format(printf, 6, 7)));
|
||||
void printf(int x, int y, Font *font, Color color, const char *format, ...) __attribute__((format(printf, 6, 7)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
@@ -220,7 +223,7 @@ class DisplayBuffer {
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, Font *font, int color, TextAlign align, const char *format, time::ESPTime time)
|
||||
void strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format, time::ESPTime time)
|
||||
__attribute__((format(strftime, 7, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
@@ -232,7 +235,7 @@ class DisplayBuffer {
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, Font *font, int color, const char *format, time::ESPTime time)
|
||||
void strftime(int x, int y, Font *font, Color color, const char *format, time::ESPTime time)
|
||||
__attribute__((format(strftime, 6, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
@@ -261,6 +264,7 @@ class DisplayBuffer {
|
||||
|
||||
/// Draw the `image` with the top-left corner at [x,y] to the screen.
|
||||
void image(int x, int y, Image *image);
|
||||
void image(int x, int y, Color color, Image *image, bool invert = false);
|
||||
|
||||
/** Get the text bounds of the given string.
|
||||
*
|
||||
@@ -290,9 +294,9 @@ class DisplayBuffer {
|
||||
void set_rotation(DisplayRotation rotation);
|
||||
|
||||
protected:
|
||||
void vprintf_(int x, int y, Font *font, int color, TextAlign align, const char *format, va_list arg);
|
||||
void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
|
||||
|
||||
virtual void draw_absolute_pixel_internal(int x, int y, int color) = 0;
|
||||
virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0;
|
||||
|
||||
virtual int get_height_internal() = 0;
|
||||
|
||||
@@ -378,13 +382,18 @@ class Font {
|
||||
class Image {
|
||||
public:
|
||||
Image(const uint8_t *data_start, int width, int height);
|
||||
Image(const uint8_t *data_start, int width, int height, int type);
|
||||
bool get_pixel(int x, int y) const;
|
||||
int get_color_pixel(int x, int y) const;
|
||||
int get_grayscale4_pixel(int x, int y) const;
|
||||
int get_width() const;
|
||||
int get_height() const;
|
||||
ImageType get_type() const;
|
||||
|
||||
protected:
|
||||
int width_;
|
||||
int height_;
|
||||
ImageType type_{BINARY};
|
||||
const uint8_t *data_start_;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user