# LVGL Principles

## 1. ภาพรวมของ LVGL

#### LVGL คืออะไร?

**LVGL (Light and Versatile Graphics Library)** เป็น Open-source Graphics Library สำหรับระบบ Embedded ที่ต้องการสร้าง UI บนหน้าจอ

<figure><img src="/files/bLpd0avCOUKyL7VW24KK" alt=""><figcaption></figcaption></figure>

***

## 2. สถาปัตยกรรมของ LVGL

#### 2.1 Object Model

| องค์ประกอบ | คำอธิบาย                      | ตัวอย่าง                  |
| ---------- | ----------------------------- | ------------------------- |
| **Screen** | หน้าจอหลัก (Container สูงสุด) | `lv_screen_active()`      |
| **Object** | วัตถุพื้นฐาน (Base class)     | `lv_obj_t *`              |
| **Widget** | วัตถุที่มี function เฉพาะทาง  | Button, Label, Slider     |
| **Part**   | ส่วนประกอบย่อยของ Widget      | MAIN, INDICATOR, KNOB     |
| **State**  | สถานะของ Object               | DEFAULT, PRESSED, CHECKED |

#### WHY: ทำไมต้องเข้าใจ Object Model?

* UI ทุกตัวใน LVGL เป็น Object ที่มี Parent-Child relationship
* การจัดการ Memory และ Layout ขึ้นอยู่กับโครงสร้างนี้
* Event จะ propagate ตาม hierarchy

#### HOW: การประยุกต์ใช้จริง

<figure><img src="/files/pjNTCTwFQhCodGxjCyt9" alt=""><figcaption></figcaption></figure>

#### CAUTION: ข้อควรระวัง

* ห้าม delete parent ก่อน children (จะ crash)
* Object ที่สร้างแล้วจะถูก LVGL จัดการ Memory (ห้าม free เอง)
* Screen เดียวกันไม่ควรมี Object เกิน \~100 ตัว (performance)

#### Flowchart การวาง Widget

<figure><img src="/files/icSBcpKezxIP5Ccs3Wgl" alt=""><figcaption></figcaption></figure>

***

## 3. Basic Widgets

#### 3.1 Label - แสดงข้อความ

**ตัวอย่างจาก: `examples/get_started/lv_example_get_started_1.c`**

```c
void lv_example_get_started_1(void)
{
    /* เปลี่ยนสีพื้นหลังของ Screen */
    lv_obj_set_style_bg_color(lv_screen_active(),
                              lv_color_hex(0x003a57),
                              LV_PART_MAIN);

    /* สร้าง Label */
    lv_obj_t * label = lv_label_create(lv_screen_active());
    lv_label_set_text(label, "Hello world");

    /* ตั้งค่าสีข้อความ */
    lv_obj_set_style_text_color(lv_screen_active(),
                                lv_color_hex(0xffffff),
                                LV_PART_MAIN);

    /* จัดตำแหน่งไว้กลางจอ */
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
```

| Function                  | หน้าที่                  | Parameter                               |
| ------------------------- | ------------------------ | --------------------------------------- |
| `lv_label_create()`       | สร้าง Label widget       | parent object                           |
| `lv_label_set_text()`     | ตั้งค่าข้อความ           | object, string                          |
| `lv_label_set_text_fmt()` | ตั้งค่าข้อความแบบ format | object, format, ...                     |
| `lv_obj_align()`          | จัดตำแหน่ง               | object, alignment, x\_offset, y\_offset |

#### WHY: ทำไม Label สำคัญ?

* ใช้แสดงค่าจาก Sensor (ADC, IMU, Temperature)
* Debug output บนหน้าจอแทน UART
* Status message ให้ผู้ใช้

#### CAUTION: ข้อควรระวัง Label

* ข้อความยาวเกินไปจะ overflow (ใช้ `lv_label_set_long_mode()`)
* `lv_label_set_text()` จะ copy string ทุกครั้ง (memory allocation)
* ใช้ `lv_label_set_text_static()` ถ้า string เป็น constant

***

#### 3.2 Button - ปุ่มกด

**ตัวอย่างจาก: `examples/get_started/lv_example_get_started_2.c`**

```c
static void btn_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * btn = lv_event_get_target(e);

    if(code == LV_EVENT_CLICKED) {
        static uint8_t cnt = 0;
        cnt++;

        /* ดึง Label ที่เป็น child ของ Button */
        lv_obj_t * label = lv_obj_get_child(btn, 0);
        lv_label_set_text_fmt(label, "Button: %d", cnt);
    }
}

void lv_example_get_started_2(void)
{
    /* สร้าง Button */
    lv_obj_t * btn = lv_button_create(lv_screen_active());
    lv_obj_set_pos(btn, 10, 10);           /* กำหนดตำแหน่ง */
    lv_obj_set_size(btn, 120, 50);         /* กำหนดขนาด */

    /* ลงทะเบียน Event callback */
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);

    /* สร้าง Label ใน Button */
    lv_obj_t * label = lv_label_create(btn);
    lv_label_set_text(label, "Button");
    lv_obj_center(label);
}
```

#### WHY: ทำไม Button สำคัญ?

* เป็น input หลักสำหรับ user interaction
* ใช้ควบคุม GPIO (ON/OFF LED, Motor)
* Trigger การทำงานต่างๆ (Start measurement, Reset)

#### HOW: เชื่อมโยงกับ GPIO จากสัปดาห์ที่ 1

```c
/* Button บน UI → ควบคุม LED จริง */
static void btn_led_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);

    if(code == LV_EVENT_CLICKED) {
        /* Toggle LED - เชื่อมโยงกับ Week 1 GPIO */
        Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN);
    }
}
```

***

#### 3.3 Switch - สวิตช์เปิด/ปิด

**ตัวอย่างจาก: `examples/widgets/switch/lv_example_switch_1.c`**

```c
static void switch_event_handler(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * obj = lv_event_get_target(e);

    if(code == LV_EVENT_VALUE_CHANGED) {
        /* ตรวจสอบสถานะ Switch */
        bool is_on = lv_obj_has_state(obj, LV_STATE_CHECKED);

        if(is_on) {
            printf("Switch: ON\n");
        } else {
            printf("Switch: OFF\n");
        }
    }
}

void create_switch_example(void)
{
    lv_obj_t * sw = lv_switch_create(lv_screen_active());
    lv_obj_center(sw);
    lv_obj_add_event_cb(sw, switch_event_handler, LV_EVENT_VALUE_CHANGED, NULL);
}
```

#### WHY: ทำไม Switch เหมาะกับ GPIO?

* แสดงสถานะ ON/OFF ชัดเจน
* เหมาะกับการควบคุม Binary Output (LED, Relay, Motor)
* User-friendly กว่า Button สำหรับ toggle state

***

#### 3.4 LED Widget - แสดงสถานะ

**ตัวอย่างจาก: `examples/widgets/led/lv_example_led_1.c`**

```c
void lv_example_led_1(void)
{
    /* LED ปิด */
    lv_obj_t * led1 = lv_led_create(lv_screen_active());
    lv_obj_align(led1, LV_ALIGN_CENTER, -80, 0);
    lv_led_off(led1);

    /* LED สีแดง ความสว่าง 150 */
    lv_obj_t * led2 = lv_led_create(lv_screen_active());
    lv_obj_align(led2, LV_ALIGN_CENTER, 0, 0);
    lv_led_set_brightness(led2, 150);
    lv_led_set_color(led2, lv_palette_main(LV_PALETTE_RED));

    /* LED เปิด */
    lv_obj_t * led3 = lv_led_create(lv_screen_active());
    lv_obj_align(led3, LV_ALIGN_CENTER, 80, 0);
    lv_led_on(led3);
}
```

| Function                  | หน้าที่             |
| ------------------------- | ------------------- |
| `lv_led_create()`         | สร้าง LED widget    |
| `lv_led_on()`             | เปิด LED (สว่างสุด) |
| `lv_led_off()`            | ปิด LED (มืด)       |
| `lv_led_set_brightness()` | ตั้งความสว่าง 0-255 |
| `lv_led_set_color()`      | ตั้งสี              |

#### WHY: ทำไมต้องมี Virtual LED?

* แสดงสถานะ Hardware LED บนหน้าจอ
* ใช้เมื่อ Physical LED ไม่พอ
* สื่อสาร Status ให้ผู้ใช้ (Error, Warning, OK)

***

## 4. Event Handling

#### 4.1 Event Callback Pattern

```c
/* รูปแบบมาตรฐานของ Event Callback */
static void my_event_cb(lv_event_t * e)
{
    /* 1. ดึงประเภท Event */
    lv_event_code_t code = lv_event_get_code(e);

    /* 2. ดึง Object ที่เกิด Event */
    lv_obj_t * target = lv_event_get_target(e);

    /* 3. ดึง User Data (ถ้ามี) */
    void * user_data = lv_event_get_user_data(e);

    /* 4. Handle แต่ละ Event Type */
    switch(code) {
        case LV_EVENT_CLICKED:
            /* ผู้ใช้คลิก */
            break;
        case LV_EVENT_VALUE_CHANGED:
            /* ค่าเปลี่ยน (Slider, Switch) */
            break;
        case LV_EVENT_PRESSED:
            /* กดค้าง */
            break;
        case LV_EVENT_RELEASED:
            /* ปล่อย */
            break;
    }
}
```

#### 4.2 Event Types ที่สำคัญ

| Event Code               | เมื่อไหร่ถูกเรียก       | ใช้กับ Widget       |
| ------------------------ | ----------------------- | ------------------- |
| `LV_EVENT_CLICKED`       | Click (Press + Release) | Button, Image       |
| `LV_EVENT_VALUE_CHANGED` | ค่าเปลี่ยน              | Slider, Switch, Arc |
| `LV_EVENT_PRESSED`       | กดค้าง                  | ทุก Object          |
| `LV_EVENT_LONG_PRESSED`  | กดค้างนาน (>400ms)      | ทุก Object          |
| `LV_EVENT_RELEASED`      | ปล่อย                   | ทุก Object          |

#### WHY: ทำไม Event-Driven สำคัญ?

* ไม่ต้อง Polling สถานะ (ประหยัด CPU)
* แยก Logic ออกจาก UI (Maintainable)
* รองรับ Multiple Events บน Object เดียว

#### HOW: ส่ง User Data ผ่าน Event

```c
/* ส่ง GPIO Port/Pin ผ่าน User Data */
typedef struct {
    GPIO_PRT_Type *port;
    uint32_t pin;
} gpio_info_t;

static gpio_info_t led1_info = {
    .port = CYBSP_USER_LED1_PORT,
    .pin = CYBSP_USER_LED1_PIN
};

static void led_toggle_cb(lv_event_t * e)
{
    gpio_info_t *gpio = (gpio_info_t *)lv_event_get_user_data(e);

    if(lv_event_get_code(e) == LV_EVENT_CLICKED) {
        Cy_GPIO_Inv(gpio->port, gpio->pin);
    }
}

void setup_led_button(void)
{
    lv_obj_t * btn = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn, led_toggle_cb, LV_EVENT_CLICKED, &led1_info);
}
```

#### CAUTION: ข้อควรระวัง Event

* User Data ต้อง valid ตลอด lifetime ของ Object
* ห้ามใช้ local variable เป็น User Data (จะหาย)
* หลาย Event อาจเกิดพร้อมกัน (ต้องกรองด้วย code)

#### Functions สำหรับการวางตำแหน่ง (LVGL v9.2 API)

```c
/*==========================================================================
 * 1. lv_obj_set_pos() - กำหนดตำแหน่งแบบ Absolute (จาก parent's content area)
 *    ใช้เมื่อ: ต้องการวางตำแหน่งที่แน่นอน เช่น grid layout
 *==========================================================================*/
lv_obj_set_pos(obj, x, y);        /* x, y เป็น pixels จาก top-left ของ parent */

/*==========================================================================
 * 2. lv_obj_set_x() / lv_obj_set_y() - กำหนดทีละแกน
 *    ใช้เมื่อ: ต้องการปรับแกนเดียว
 *==========================================================================*/
lv_obj_set_x(obj, 100);           /* ตั้ง x เป็น 100px */
lv_obj_set_y(obj, 50);            /* ตั้ง y เป็น 50px */

/*==========================================================================
 * 3. lv_obj_align() - จัดตำแหน่งตาม alignment พร้อม offset
 *    ใช้เมื่อ: วาง widget โดยอ้างอิงจาก parent (แนะนำ)
 *==========================================================================*/
lv_obj_align(obj, LV_ALIGN_CENTER, x_offset, y_offset);
/*                    ↑                ↑          ↑
            ประเภท alignment        เลื่อน X     เลื่อน Y

    ตัวอย่าง:
    - LV_ALIGN_CENTER, 0, 0     → กลางจอพอดี
    - LV_ALIGN_CENTER, 0, -50   → กลางจอ แต่ขยับขึ้น 50px
    - LV_ALIGN_TOP_MID, 0, 20   → กลางบน แต่ขยับลง 20px (สำหรับ title)
*/

/*==========================================================================
 * 4. lv_obj_align_to() - จัดตำแหน่งโดยอ้างอิงจาก object อื่น
 *    ใช้เมื่อ: วาง widget สัมพันธ์กับ widget อื่น
 *==========================================================================*/
lv_obj_align_to(obj, base_obj, LV_ALIGN_OUT_BOTTOM_MID, x_offset, y_offset);
/*               ↑      ↑              ↑                   ↑          ↑
              widget   อ้างอิง    วางใต้ base_obj           เลื่อน X     เลื่อน Y */

/*==========================================================================
 * 5. lv_obj_center() - วางกลาง parent อย่างง่าย
 *    ใช้เมื่อ: ต้องการวางกลางโดยไม่มี offset
 *==========================================================================*/
lv_obj_center(obj);               /* เทียบเท่า lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0) */
```

## Functions สำหรับการกำหนดขนาด (Size Functions)

```c
/*==========================================================================
 * 1. lv_obj_set_size() - กำหนดทั้ง width และ height พร้อมกัน
 *==========================================================================*/
lv_obj_set_size(obj, width, height);

/* ค่าพิเศษที่ใช้ได้:
   - pixels (เช่น 200, 100)           → ขนาดคงที่
   - lv_pct(50)                       → 50% ของ parent
   - LV_SIZE_CONTENT                  → ขยายตาม content ข้างใน */

/*==========================================================================
 * 2. lv_obj_set_width() / lv_obj_set_height() - กำหนดทีละด้าน
 *==========================================================================*/
lv_obj_set_width(obj, 200);       /* กว้าง 200px */
lv_obj_set_height(obj, 100);      /* สูง 100px */
lv_obj_set_width(obj, lv_pct(80)); /* กว้าง 80% ของ parent */

/*==========================================================================
 * 3. lv_obj_set_style_pad_*() - กำหนด padding (ช่องว่างภายใน)
 *    ใช้เมื่อ: ต้องการขยายขนาด widget โดยอัตโนมัติ
 *==========================================================================*/
lv_obj_set_style_pad_hor(btn, 30, 0);  /* padding ซ้าย-ขวา 30px */
lv_obj_set_style_pad_ver(btn, 15, 0);  /* padding บน-ล่าง 15px */
/* หมายเหตุ: ดีกว่า lv_obj_set_size() สำหรับ Button เพราะขยายตาม text อัตโนมัติ */
```

#### Layout Pattern สำหรับ 480x320 Screen

**Pattern 1: Vertical Stacking (ใช้บ่อยที่สุด)**

```c
/* Title → Main Content → Description → Footer */
void create_vertical_layout(void)
{
    /* 1. Title - ชิดบน */
    lv_obj_t * title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "Example Title");
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 20);        /* y=20 จากขอบบน */

    /* 2. Main content - กลางจอ */
    lv_obj_t * widget = lv_led_create(lv_screen_active());
    lv_obj_align(widget, LV_ALIGN_CENTER, 0, -30);       /* y=-30 ขยับขึ้นเล็กน้อย */

    /* 3. Description - ล่างสุด */
    lv_obj_t * desc = lv_label_create(lv_screen_active());
    lv_label_set_text(desc, "Description text");
    lv_obj_align(desc, LV_ALIGN_BOTTOM_MID, 0, -50);     /* y=-50 จากขอบล่าง */
}
```

**Pattern 2: Multiple Widgets at CENTER with Y-offset**

```c
/* วาง widgets หลายตัวจากกลางจอ โดยใช้ y_offset */
void create_center_stacked_layout(void)
{
    /* LED - อยู่เหนือกลางจอ */
    lv_obj_t * led = lv_led_create(lv_screen_active());
    lv_obj_align(led, LV_ALIGN_CENTER, 0, -70);          /* ขึ้นไป 70px จากกลาง */

    /* Label - กลางจอพอดี */
    lv_obj_t * label = lv_label_create(lv_screen_active());
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);          /* กลางจอพอดี */

    /* Buttons - ล่างกลางจอ */
    lv_obj_t * btn1 = lv_button_create(lv_screen_active());
    lv_obj_align(btn1, LV_ALIGN_CENTER, -60, 50);        /* ซ้าย, ลง 50px */

    lv_obj_t * btn2 = lv_button_create(lv_screen_active());
    lv_obj_align(btn2, LV_ALIGN_CENTER, 60, 50);         /* ขวา, ลง 50px */

    /* Slider - ล่างสุดของกลุ่ม */
    lv_obj_t * slider = lv_slider_create(lv_screen_active());
    lv_obj_align(slider, LV_ALIGN_CENTER, 0, 110);       /* ลง 110px จากกลาง */
}
```

**Pattern 3: Container with Grid Layout**

```c
/* ใช้ container เมื่อมี widgets หลายตัวที่ต้องจัดเป็นกลุ่ม */
void create_container_grid_layout(void)
{
    /* Container - ขนาด 420x200 pixels */
    lv_obj_t * cont = lv_obj_create(lv_screen_active());
    lv_obj_set_size(cont, 420, 200);
    lv_obj_align(cont, LV_ALIGN_CENTER, 0, 10);
    lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);  /* ปิด scroll */

    /* วาง items ใน container ด้วย absolute position */
    for(int i = 0; i < 4; i++) {
        int x_pos = (i % 2) * 200 + 20;   /* 2 columns: x = 20 หรือ 220 */
        int y_pos = (i / 2) * 80 + 20;     /* 2 rows: y = 20 หรือ 100 */

        lv_obj_t * led = lv_led_create(cont);
        lv_obj_set_pos(led, x_pos, y_pos);
    }
}
```

## Widget Sizing Guidelines (แนะนำสำหรับ 800x480)

<table data-header-hidden><thead><tr><th width="100"></th><th></th><th></th></tr></thead><tbody><tr><td>Widget Type</td><td>Recommended Size (800x480)</td><td>Notes / Recommendations</td></tr><tr><td><strong>Title Label</strong></td><td>Auto (text length)</td><td>แนะนำ Font: <code>montserrat_32</code> หรือ <code>36</code> (เพื่อให้เด่นชัดบนความสูง 480px)</td></tr><tr><td><strong>Button</strong></td><td>Padding 50x25</td><td>ใช้ Padding แทนการ fix size เพื่อให้ปุ่มขยายตามขนาดตัวอักษร</td></tr><tr><td><strong>LED</strong></td><td>60x60 ถึง 120x120</td><td>ขนาด 60px เหมาะสำหรับสถานะทั่วไป, 120px สำหรับสถานะหลัก</td></tr><tr><td><strong>Switch</strong></td><td>100x50 ถึง 120x60</td><td>ปรับให้ใหญ่ขึ้นเพื่อให้กดง่าย (Touch Target ที่เหมาะสม)</td></tr><tr><td><strong>Slider</strong></td><td>Width 400-600</td><td>ความยาวควรอยู่ที่ประมาณ 50-75% ของความกว้างจอ</td></tr><tr><td><strong>Bar</strong></td><td>500x40</td><td>ปรับความหนา (Height) เป็น 40px เพื่อให้มองเห็นความก้าวหน้าชัดเจน</td></tr><tr><td><strong>Arc</strong></td><td>200x200 ถึง 350x350</td><td>ควรคงรูปทรงจัตุรัส (Square) เพื่อไม่ให้วงกลมเบี้ยว</td></tr><tr><td><strong>Chart</strong></td><td>700x400 max</td><td>ขยายให้เกือบเต็มพื้นที่หากเป็นหน้า Dashboard หลัก</td></tr><tr><td><strong>Container</strong></td><td>760x420 typical</td><td>เว้นขอบ (Margin) ด้านละ 20px เพื่อความสวยงาม</td></tr></tbody></table>

#### Common Background Colors (จาก Course Examples)

```c
/* Primary backgrounds */
#define BG_DARK_NAVY    0x003a57    /* Week3 Ex1: Hello World */
#define BG_DARK_BLUE    0x1a1a2e    /* Week3 Ex2,5: Button, Dashboard */
#define BG_DARKER       0x0f0f23    /* Week3 Ex3: LED Control */
#define BG_MIDNIGHT     0x16213e    /* Week3 Ex4, Week4: Switch, Chart */

/* Panel/Container backgrounds */
#define BG_PANEL        0x0f0f23    /* Container background */
#define BG_BORDER       0x444444    /* Container border */

/* Text colors */
#define TEXT_WHITE      0xFFFFFF    /* Main text */
#define TEXT_GRAY       0xAAAAAA    /* Description text */
#define TEXT_GREEN      0x00FF00    /* Status text */
```

#### CAUTION: ข้อควรระวังในการวาง Layout

1. **ห้ามวาง Widget นอกขอบจอ**: ค่า y ที่เกิน 319 หรือ x ที่เกิน 479 จะทำให้ widget หายไป
2. **ลำดับการสร้างสำคัญ**: Widget ที่สร้างทีหลังจะอยู่ด้านบน (z-order)
3. **align\_to ต้องทำหลัง base object ถูก position แล้ว**:

   ```c
   /* ✅ ถูก */
   lv_obj_align(slider, LV_ALIGN_CENTER, 0, 50);   /* position slider ก่อน */
   lv_obj_align_to(label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);  /* แล้วค่อย align label */

   /* ❌ ผิด */
   lv_obj_align_to(label, slider, ...);   /* slider ยังไม่มี position! */
   lv_obj_align(slider, ...);
   ```
4. **ใช้ padding แทน set\_size สำหรับ Button**:

   ```c
   /* ✅ ดี - text ไม่ถูก clip */
   lv_obj_set_style_pad_hor(btn, 30, 0);
   lv_obj_set_style_pad_ver(btn, 15, 0);

   /* ⚠️ ระวัง - อาจ clip text ถ้าขนาดเล็กเกินไป */
   lv_obj_set_size(btn, 80, 30);  /* ต้องแน่ใจว่า text พอดี */
   ```
5. **Container ควรปิด scroll flag**:

   ```c
   lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);  /* ป้องกัน content เลื่อนได้ */
   ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/gpio-to-hmi-display/lvgl-principles.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
