/* Main Sketch CYD_Checker_V1.1.0.ino
Version: 1.1.0
Purpose: Single-file LVGL + ST7789 + XPT2046 example with runtime colour/order toggles
Pins (adjust if needed):
MOSI = 13, SCLK = 14, DC = 2, CS = 15, RST = 12, BL = 21
TOUCH_CS = 33, TOUCH_IRQ = 36
Runtime Serial commands:
c -> run color test (ble, green, red, white, black)
s -> toggle tft.setSwapBytes(true/false)
p -> toggle pushSwap flag used in flush
o -> print current settings
r -> rotate display (cycles 0..3)
*/
#include <Arduino.h>
#include <TFT_eSPI.h>
#include <lvgl.h>
#include <XPT2046_Touchscreen.h>
#include <SPI.h>
#include <esp_heap_caps.h>
#define SKETCH_VERSION “1.1.0”
// Hardware pins (match your User_Setup.h)
#define TFT_BL_PIN 21
#define TFT_RST_PIN 12
#define TOUCH_CS_PIN 33
#define TOUCH_IRQ_PIN 36
// Global single TFT instance (only one definition)
TFT_eSPI tft = TFT_eSPI(); // uses TFT_eSPI/User_Setup.h
// Touch controller
XPT2046_Touchscreen ts(TOUCH_CS_PIN, TOUCH_IRQ_PIN);
// LVGL buffers and drivers
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf1 = nullptr;
static lv_disp_drv_t disp_drv;
static lv_indev_drv_t indev_drv;
// Runtime flags (can be toggled via Serial)
static bool runtime_setSwapBytes = true; // applied after tft.init()
static bool runtime_pushSwap = false; // passed to pushColors in flush
static uint8_t runtime_rotation = 1; // 0..3
// Forward declarations
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
void my_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data);
void create_ui();
void run_color_test();
void print_settings();
void create_ui() {
lv_obj_t *scr = lv_scr_act();
lv_obj_t *label = lv_label_create(scr);
lv_label_set_text_fmt(label, “ST7789 LVGL v%s”, SKETCH_VERSION);
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 8);
lv_obj_t *spinner = lv_spinner_create(scr, 1000, 60);
lv_obj_set_size(spinner, 80, 80);
lv_obj_align(spinner, LV_ALIGN_CENTER, 0, 10);
}
void setup() {
Serial.begin(115200);
delay(100);
Serial.println();
Serial.printf(“Starting LVGL_ST7789_v%s\n”, SKETCH_VERSION);
// Backlight
pinMode(TFT_BL_PIN, OUTPUT);
digitalWrite(TFT_BL_PIN, HIGH);
// Manual reset
pinMode(TFT_RST_PIN, OUTPUT);
digitalWrite(TFT_RST_PIN, LOW); delay(50);
digitalWrite(TFT_RST_PIN, HIGH); delay(50);
// TFT init
tft.init();
tft.invertDisplay(false); // try false first; if colours are still wrong try true eg. Cyan, Magenta and Yellow – typical in v3 displays
tft.setRotation(runtime_rotation);
tft.setSwapBytes(runtime_setSwapBytes);
Serial.printf(“TFT init w=%d h=%d\n”, tft.width(), tft.height());
tft.fillScreen(TFT_BLACK);
// LVGL init
lv_init();
// Allocate DMA-capable buffer if possible
const uint32_t buf_pixels = 240 * 40;
buf1 = (lv_color_t*)heap_caps_malloc(buf_pixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
if (!buf1) {
Serial.println(“DMA alloc failed, falling back to malloc”);
buf1 = (lv_color_t*)malloc(buf_pixels * sizeof(lv_color_t));
if (!buf1) {
Serial.println(“Buffer allocation failed – halting”);
while (1) delay(1000);
}
} else {
Serial.println(“DMA buffer allocated”);
}
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, buf_pixels);
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = tft.width();
disp_drv.ver_res = tft.height();
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
// Touch init (optional)
if (ts.begin()) {
Serial.println(“XPT2046 init OK”);
} else {
Serial.println(“XPT2046 init FAILED – disconnect for TFT-only testing”);
}
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touch_read;
lv_indev_drv_register(&indev_drv);
create_ui();
print_settings();
Serial.println(“Setup complete. Use Serial commands: c s p o r”);
}
void loop() {
// Handle LVGL timersc
lv_timer_handler();
delay(5);
// Serial command handling (non-blocking)
if (Serial.available()) {
char c = Serial.read();
switch (c) {
case ‘c’: run_color_test(); break;
case ‘s’:
runtime_setSwapBytes = !runtime_setSwapBytes;
tft.setSwapBytes(runtime_setSwapBytes);
Serial.printf(“setSwapBytes -> %s\n”, runtime_setSwapBytes ? “true” : “false”);
break;
case ‘p’:
runtime_pushSwap = !runtime_pushSwap;
Serial.printf(“pushSwap -> %s\n”, runtime_pushSwap ? “true” : “false”);
break;
case ‘o’:
print_settings();
break;
case ‘r’:
runtime_rotation = (runtime_rotation + 1) & 3;
tft.setRotation(runtime_rotation);
Serial.printf(“rotation -> %u\n”, runtime_rotation);
break;
default:
// ignore other bytes
break;
}
}
}
/* Display flush callback */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
int32_t x1 = area->x1;
int32_t y1 = area->y1;
int32_t x2 = area->x2;
int32_t y2 = area->y2;
uint32_t w = (x2 – x1 + 1);
uint32_t h = (y2 – y1 + 1);
tft.startWrite();
tft.setAddrWindow(x1, y1, w, h);
// Use runtime_pushSwap to flip bytes at push time if needed
tft.pushColors((uint16_t*)colour_p, w * h, runtime_pushSwap);
tft.endWrite();
lv_disp_flush_ready(disp);
}
/* Touch read callback */
void my_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
if (!ts.touched()) {
data->state = LV_INDEV_STATE_REL;
return;
}
TS_Point p = ts.getPoint();
// Default raw ranges; calibrate if needed
const int rawMinX = 200;
const int rawMaxX = 3900;
const int rawMinY = 200;
const int rawMaxY = 3900;
int sx = map(p.x, rawMinX, rawMaxX, 0, tft.width());
int sy = map(p.y, rawMinY, rawMaxY, 0, tft.height());
sx = constrain(sx, 0, tft.width() – 1);
sy = constrain(sy, 0, tft.height() – 1);
data->point.x = sx;
data->point.y = sy;
data->state = (p.z > 0) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
}
/* This utility will run a quick colour test on the TFT */
void run_color_test() {
Serial.println(“Running colour test: BLUE, GREEN, RED, WHITE, BLACK”);
tft.fillScreen(TFT_RED); delay(500);
tft.fillScreen(TFT_GREEN); delay(500);
tft.fillScreen(TFT_BLUE); delay(500);
tft.fillScreen(TFT_WHITE); delay(500);
tft.fillScreen(TFT_BLACK); delay(500);
// restore LVGL screen
lv_refr_now(NULL);
Serial.println(“Colour test complete”);
}
/* Utility to print the users current runtime settings */
void print_settings() {
Serial.printf(“Version: %s\n”, SKETCH_VERSION);
Serial.printf(“setSwapBytes: %s\n”, runtime_setSwapBytes ? “true” : “false”);
Serial.printf(“pushSwap (flush): %s\n”, runtime_pushSwap ? “true” : “false”);
Serial.printf(“rotation: %u\n”, runtime_rotation);
Serial.printf(“TFT size: %d x %d\n”, tft.width(), tft.height());
}