// ============================================================================
// 1. PORTABLE CYD HARDWARE CONFIGURATION (Overrides external User_Setup.h)
// ============================================================================
#define USER_SETUP_LOADED // Prevents TFT_eSPI from loading external setups
#define ILI9341_2_DRIVER // Screen controller for ESP32-2432S028
#define TFT_WIDTH 240
#define TFT_HEIGHT 320
// Pin map specifically matching the Cheap Yellow Display (CYD)
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST -1 // Connected to ESP32 reset pin
#define TFT_BL 21 // Backlight control pin
#define TFT_BACKLIGHT_ON HIGH
#define TOUCH_CS 33 // XPT2046 Touch Controller Chip Select
#define SPI_FREQUENCY 55000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000 // Stable speed for touch register reading
// ============================================================================
// 2. LIBRARY INCLUDES
// ============================================================================
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <XPT2046_Bitbang.h> // Common touch library used for CYD
// Initialize hardware instances
TFT_eSPI tft = TFT_eSPI();
// Hardware SPI pins for the touch controller on CYD
XPT2046_Bitbang touch(32, 39, 25, 33);
// Global screen buffers for LVGL
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[TFT_HEIGHT * 10];
// UI Widget references
static lv_obj_t * slider_label;
static lv_obj_t * left_led;
static lv_obj_t * right_led;
// ============================================================================
// 3. LVGL INTERACTION DRIVERS (Display Flush & Touch Read)
// ============================================================================
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data) {
TouchPoint p = touch.getTouch();
if (p.z > 200) { // Check pressure threshold for valid touch
data->state = LV_INDEV_STATE_PR;
// Map touch raw coordinates to screen pixel matrix landscape mode
// Note: Adjust mapping ranges slightly if your touch points don't track the finger perfectly
data->point.x = map(p.x, 300, 3900, 0, TFT_HEIGHT);
data->point.y = map(p.y, 200, 3700, 0, TFT_WIDTH);
} else {
data->state = LV_INDEV_STATE_REL;
}
}
// ============================================================================
// 4. INTERACTIVE UI CALLBACKS
// ============================================================================
static void slider_event_cb(lv_event_t * e) {
lv_obj_t * slider = lv_event_get_target(e);
int value = (int)lv_slider_get_value(slider);
lv_label_set_text_fmt(slider_label, "%d%%", value);
}
static void switch_event_cb(lv_event_t * e) {
lv_obj_t * sw = lv_event_get_target(e);
if(lv_obj_has_state(sw, LV_STATE_CHECKED)) {
lv_led_on(left_led);
} else {
lv_led_off(left_led);
}
}
static void button_event_cb(lv_event_t * e) {
if(lv_led_get_brightness(right_led) > 0) {
lv_led_off(right_led);
} else {
lv_led_on(right_led);
}
}
// ============================================================================
// 5. GRAPHICS LAYOUT CREATION
// ============================================================================
void create_cyd_mockup_ui() {
lv_obj_t * scr = lv_scr_act();
lv_obj_set_style_bg_color(scr, lv_color_hex(0xDCE4EC), LV_PART_MAIN);
// Header Title
lv_obj_t * header_label = lv_label_create(scr);
lv_label_set_text(header_label, "Hello, Kafkar.com!");
lv_obj_set_style_text_color(header_label, lv_color_hex(0x333333), LV_PART_MAIN);
lv_obj_align(header_label, LV_ALIGN_TOP_MID, 0, 25);
// Slider Toggle Switch
lv_obj_t * sw = lv_switch_create(scr);
lv_obj_align(sw, LV_ALIGN_TOP_MID, 0, 65);
lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_set_style_bg_color(sw, lv_color_hex(0x5B7CFA), LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_event_cb(sw, switch_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// "Left" Action Button
lv_obj_t * btn = lv_btn_create(scr);
lv_obj_set_size(btn, 70, 35);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 10);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x5B7CFA), LV_PART_MAIN);
lv_obj_set_style_radius(btn, 8, LV_PART_MAIN);
lv_obj_add_event_cb(btn, button_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t * btn_label = lv_label_create(btn);
lv_label_set_text(btn_label, "Left");
lv_obj_center(btn_label);
// Indicators (Dark Gray & Olive Green)
left_led = lv_led_create(scr);
lv_obj_set_size(left_led, 20, 20);
lv_obj_align(left_led, LV_ALIGN_CENTER, -65, 10);
lv_led_set_color(left_led, lv_color_hex(0x5A5A52));
lv_led_on(left_led);
right_led = lv_led_create(scr);
lv_obj_set_size(right_led, 24, 24);
lv_obj_align(right_led, LV_ALIGN_CENTER, 65, 25);
lv_led_set_color(right_led, lv_color_hex(0x7D8E2B));
lv_led_on(right_led);
// Bottom Progress Slider
lv_obj_t * slider = lv_slider_create(scr);
lv_obj_set_size(slider, 200, 12);
lv_obj_align(slider, LV_ALIGN_BOTTOM_MID, 0, -60);
lv_slider_set_value(slider, 0, LV_ANIM_OFF);
lv_obj_set_style_bg_color(slider, lv_color_hex(0x5B7CFA), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider, lv_color_hex(0x5B7CFA), LV_PART_KNOB);
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// Value Dynamic Text Indicator
slider_label = lv_label_create(scr);
lv_label_set_text(slider_label, "0%");
lv_obj_set_style_text_color(slider_label, lv_color_hex(0x555555), LV_PART_MAIN);
lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 15);
}
// ============================================================================
// 6. HARDWARE INITIALIZATION & CORE LOOP
// ============================================================================
void setup() {
Serial.begin(115200);
// Init display hardware
tft.begin();
tft.setRotation(1); // Standard Landscape presentation for CYD
// Turn on the screen backlight
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
// Init touch panel hardware
touch.begin();
// Init LVGL Core engine
lv_init();
lv_disp_draw_buf_init(&draw_buf, buf, NULL, TFT_HEIGHT * 10);
// Register Display driver to LVGL context
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = TFT_HEIGHT; // Swap variables to respect landscape orientation
disp_drv.ver_res = TFT_WIDTH;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
// Register Touch Input driver to LVGL context
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
// Draw UI Elements
create_cyd_mockup_ui();
}
void loop() {
lv_timer_handler(); // Updates and manages screen tasks
delay(5);
}