# Button

## Lab 2: Button with Click Counter

### 1. โครงสร้างภาพรวมของ Lab

#### Why?: วัตถุประสงค์การเรียนรู้

Button เป็น Widget พื้นฐานที่สำคัญที่สุดสำหรับการรับ Input จากผู้ใช้:

* **User Interaction**: Button เป็นวิธีหลักที่ผู้ใช้โต้ตอบกับ GUI
* **GPIO Control**: ใช้ควบคุม GPIO เช่น เปิด/ปิด LED, Motor
* **Action Trigger**: ใช้สั่งเริ่ม/หยุดการทำงานต่างๆ
* **Event-Driven**: เรียนรู้แนวคิด Event-driven Programming

#### What?: จะได้เรียนรู้อะไร

1. **Button Widget**: สร้างปุ่มกดด้วย `lv_button_create()`
2. **Event Callback**: ผูก callback function กับ event
3. **Event Filtering**: กรอง event ที่ต้องการด้วย `LV_EVENT_CLICKED`
4. **Static Variable**: เก็บค่า counter ข้าม function calls
5. **Child Widget**: เพิ่ม Label ลงใน Button

<table><thead><tr><th width="257.9808349609375">ฟังก์ชัน</th><th>หน้าที่</th></tr></thead><tbody><tr><td><code>lv_button_create()</code></td><td>สร้าง Button widget</td></tr><tr><td><code>lv_obj_add_event_cb()</code></td><td>ผูก Event callback function</td></tr><tr><td><code>lv_event_get_code()</code></td><td>ดึง event code ภายใน callback</td></tr><tr><td><code>lv_event_get_target()</code></td><td>ดึง object ที่ถูกกด</td></tr><tr><td><code>lv_event_get_user_data()</code></td><td>ดึง user data ที่ส่งมากับ event</td></tr><tr><td><code>LV_EVENT_CLICKED</code></td><td>Event เมื่อกดและปล่อยปุ่ม</td></tr></tbody></table>

#### How? - ทำอย่างไร

* สร้างปุ่มที่นับจำนวนครั้งที่กด และแสดงผลบน Label ภายในปุ่ม

<figure><img src="/files/7PM3raekzhJTsHLwg85c" alt=""><figcaption></figcaption></figure>

***

### 2. หลักการทำงานและ Flowchart

#### 2.1 Event-Driven Model

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

#### 2.2 Button Creation Pattern (LVGL Official)

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

{% hint style="warning" %}
**WARNING:**&#x20;

ห้ามใช้คำสั่ง `lv_obj_set_size()` ก่อนที่จะสร้าง label เพราะอาจจะทำให้ text ถูกตัด (clip) ออกไป
{% endhint %}

#### 2.3 Program Flowchart

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

***

### 3. ฟังก์ชันสำคัญ (API Reference)

#### 3.1 Button Creation & Style

<table><thead><tr><th width="387.7784423828125">ฟังก์ชัน</th><th>พารามิเตอร์</th><th>คำอธิบาย</th></tr></thead><tbody><tr><td><code>lv_button_create(parent)</code></td><td>parent object</td><td>สร้าง Button ใหม่</td></tr><tr><td><code>lv_obj_set_style_pad_hor(btn, px, part)</code></td><td>btn, pixels, 0</td><td>padding ซ้าย-ขวา (ทำให้ปุ่มกว้าง)</td></tr><tr><td><code>lv_obj_set_style_pad_ver(btn, px, part)</code></td><td>btn, pixels, 0</td><td>padding บน-ล่าง (ทำให้ปุ่มสูง)</td></tr><tr><td><code>lv_obj_set_style_bg_color(btn, color, part)</code></td><td>btn, lv_color_t, 0</td><td>สีพื้นหลังปุ่ม</td></tr><tr><td><code>lv_obj_set_style_radius(btn, r, part)</code></td><td>btn, pixels, 0</td><td>มุมมนของปุ่ม</td></tr></tbody></table>

#### 3.2 Event Functions

<table><thead><tr><th width="381.844482421875">ฟังก์ชัน</th><th width="197.4033203125">พารามิเตอร์</th><th>คำอธิบาย</th></tr></thead><tbody><tr><td><code>lv_obj_add_event_cb(obj, cb, filter, data)</code></td><td>obj, callback_fn, LV_EVENT_*, user_data</td><td>ลงทะเบียน event callback</td></tr><tr><td><code>lv_event_get_code(e)</code></td><td>lv_event_t *</td><td>ดึง event code</td></tr><tr><td><code>lv_event_get_target(e)</code></td><td>lv_event_t *</td><td>ดึง object ที่เกิด event</td></tr><tr><td><code>lv_event_get_user_data(e)</code></td><td>lv_event_t *</td><td>ดึง user data ที่ส่งมา</td></tr></tbody></table>

#### 3.3 Common Event Codes

<table><thead><tr><th width="214.788330078125">Event Code</th><th width="237.0440673828125">เมื่อไหร่</th><th>ใช้เมื่อ</th></tr></thead><tbody><tr><td><code>LV_EVENT_CLICKED</code></td><td>กดแล้วปล่อย (tap สมบูรณ์)</td><td>ใช้บ่อยที่สุด - สำหรับ action ทั่วไป</td></tr><tr><td><code>LV_EVENT_PRESSED</code></td><td>นิ้วแตะลง</td><td>ตอบสนองทันที ไม่รอปล่อย</td></tr><tr><td><code>LV_EVENT_RELEASED</code></td><td>ยกนิ้วขึ้น</td><td>ใช้คู่กับ PRESSED</td></tr><tr><td><code>LV_EVENT_LONG_PRESSED</code></td><td>กดค้างนาน (400ms)</td><td>สำหรับ action พิเศษ</td></tr><tr><td><code>LV_EVENT_VALUE_CHANGED</code></td><td>ค่าเปลี่ยน</td><td>สำหรับ Slider, Switch, Dropdown</td></tr></tbody></table>

#### 3.4 Label Functions ใน Button

<table><thead><tr><th width="352.71807861328125">ฟังก์ชัน</th><th>คำอธิบาย</th></tr></thead><tbody><tr><td><code>lv_label_create(btn)</code></td><td>สร้าง Label เป็น child ของ Button</td></tr><tr><td><code>lv_obj_center(label)</code></td><td>วาง Label ตรงกลาง Button</td></tr><tr><td><code>lv_obj_get_child(btn, 0)</code></td><td>ดึง child แรก (Label) จาก Button</td></tr><tr><td><code>lv_label_set_text_fmt(label, fmt, ...)</code></td><td>อัปเดตข้อความของ Label</td></tr></tbody></table>

***

### 4. โค้ดตัวอย่าง (Code Examples)

#### 4.1 โค้ดอ้างอิง: `part1_ex2_button_counter()`

จากไฟล์ `part1_examples.c`:

```c
/* Callback function - ประกาศ static นอก main function */
static void ex2_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 uint32_t cnt = 0;
        cnt++;

        /* Get the label child and update text */
        lv_obj_t * label = lv_obj_get_child(btn, 0);
        lv_label_set_text_fmt(label, "Clicked: %u", (unsigned int)cnt);

        printf("Button clicked %u times\r\n", (unsigned int)cnt);
    }
}

void part1_ex2_button_counter(void)
{
    /* Set background */
    lv_obj_set_style_bg_color(lv_screen_active(),
                              lv_color_hex(0x1a1a2e), LV_PART_MAIN);

    /* Title */
    lv_obj_t * title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "Part 1 - Example 2: Button Counter");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 20);

    /* Create button - following official LVGL pattern */
    lv_obj_t * btn = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn, ex2_btn_event_cb, LV_EVENT_CLICKED, NULL);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_style_pad_hor(btn, 30, 0);
    lv_obj_set_style_pad_ver(btn, 15, 0);

    /* Add label to button */
    lv_obj_t * btn_label = lv_label_create(btn);
    lv_label_set_text(btn_label, "Click Me!");
    lv_obj_center(btn_label);

    /* Description */
    lv_obj_t * desc = lv_label_create(lv_screen_active());
    lv_label_set_text(desc, "Learning: lv_button_create, lv_obj_add_event_cb\n"
                           "Pattern: Event callback with LV_EVENT_CLICKED");
    lv_obj_set_style_text_color(desc, lv_color_hex(0xAAAAAA), 0);
    lv_obj_set_style_text_align(desc, LV_TEXT_ALIGN_CENTER, 0);
    lv_obj_align(desc, LV_ALIGN_BOTTOM_MID, 0, -50);
}
```

**วิเคราะห์จุดสำคัญ**:

| จุด             | โค้ด                                 | ทำไม                         |
| --------------- | ------------------------------------ | ---------------------------- |
| Event filter    | `LV_EVENT_CLICKED` ใน `add_event_cb` | ลงทะเบียนเฉพาะ CLICKED       |
| Static counter  | `static uint32_t cnt = 0`            | เก็บค่าข้าม function call    |
| Get child label | `lv_obj_get_child(btn, 0)`           | ดึง Label จาก Button         |
| Update text     | `lv_label_set_text_fmt()`            | อัปเดตข้อความแบบ dynamic     |
| Padding         | `pad_hor(30), pad_ver(15)`           | ใช้แทน `set_size()`          |
| user\_data      | `NULL`                               | ไม่ส่ง data เพราะมีปุ่มเดียว |

#### 4.2 Button กับ User Data

```c
/* Callback ที่ใช้ user_data แยกการทำงาน */
static lv_obj_t * count_label;
static int32_t counter = 0;

static void inc_dec_cb(lv_event_t * e)
{
    int32_t * step = (int32_t *)lv_event_get_user_data(e);
    counter += *step;
    lv_label_set_text_fmt(count_label, "Count: %d", (int)counter);
}

void example_two_buttons(void)
{
    static int32_t step_up = 1;     /* ต้อง static เพราะ user_data เก็บ pointer */
    static int32_t step_down = -1;

    /* Display label */
    count_label = lv_label_create(lv_screen_active());
    lv_label_set_text(count_label, "Count: 0");
    lv_obj_set_style_text_color(count_label, lv_color_hex(0xFFFFFF), 0);
    lv_obj_set_style_text_font(count_label, &lv_font_montserrat_24, 0);
    lv_obj_align(count_label, LV_ALIGN_CENTER, 0, -40);

    /* UP Button */
    lv_obj_t * btn_up = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn_up, inc_dec_cb, LV_EVENT_CLICKED, &step_up);
    lv_obj_align(btn_up, LV_ALIGN_CENTER, -70, 30);
    lv_obj_set_style_pad_hor(btn_up, 25, 0);
    lv_obj_set_style_pad_ver(btn_up, 12, 0);
    lv_obj_set_style_bg_color(btn_up, lv_palette_main(LV_PALETTE_GREEN), 0);

    lv_obj_t * lbl_up = lv_label_create(btn_up);
    lv_label_set_text(lbl_up, "+1");
    lv_obj_center(lbl_up);

    /* DOWN Button */
    lv_obj_t * btn_down = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn_down, inc_dec_cb, LV_EVENT_CLICKED, &step_down);
    lv_obj_align(btn_down, LV_ALIGN_CENTER, 70, 30);
    lv_obj_set_style_pad_hor(btn_down, 25, 0);
    lv_obj_set_style_pad_ver(btn_down, 12, 0);
    lv_obj_set_style_bg_color(btn_down, lv_palette_main(LV_PALETTE_RED), 0);

    lv_obj_t * lbl_down = lv_label_create(btn_down);
    lv_label_set_text(lbl_down, "-1");
    lv_obj_center(lbl_down);
}
```

#### 4.3 Multiple Callbacks กับ Multiple Buttons

```c
/* แต่ละปุ่มมี callback แยก */
static void start_cb(lv_event_t * e) {
    (void)e;
    printf("Process STARTED\r\n");
    /* สั่งเริ่ม data collection, motor, etc. */
}

static void stop_cb(lv_event_t * e) {
    (void)e;
    printf("Process STOPPED\r\n");
    /* สั่งหยุดทำงาน */
}

/* ลงทะเบียนแยก callback */
lv_obj_add_event_cb(btn_start, start_cb, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(btn_stop, stop_cb, LV_EVENT_CLICKED, NULL);
```

#### 4.4 Button Style Customization

```c
/* ปุ่มแบบ Danger (สีแดง, มุมมน) */
lv_obj_t * btn_danger = lv_button_create(lv_screen_active());
lv_obj_set_style_bg_color(btn_danger, lv_palette_main(LV_PALETTE_RED), 0);
lv_obj_set_style_bg_color(btn_danger,
lv_palette_darken(LV_PALETTE_RED, 2), LV_STATE_PRESSED);  /* สีเข้มเมื่อกด */
lv_obj_set_style_radius(btn_danger, 10, 0);  /* มุมมน */
lv_obj_set_style_pad_hor(btn_danger, 40, 0);
lv_obj_set_style_pad_ver(btn_danger, 20, 0);
lv_obj_set_style_shadow_width(btn_danger, 10, 0);  /* เงา */
lv_obj_set_style_shadow_color(btn_danger,
lv_palette_main(LV_PALETTE_RED), 0);
```

***

### 5. องค์ความรู้และเทคนิค

#### 5.1 Event Filter: ทำไมต้อง Filter?

```c
/* วิธีผิด: ลงทะเบียน LV_EVENT_ALL แล้ว filter ภายใน */
lv_obj_add_event_cb(btn, cb, LV_EVENT_ALL, NULL);  /* ไม่แนะนำ! */

/* วิธีถูก: ลงทะเบียนเฉพาะ event ที่ต้องการ */
lv_obj_add_event_cb(btn, cb, LV_EVENT_CLICKED, NULL);  /* ดีกว่า */
```

> **เหตุผล**: LV\_EVENT\_ALL จะเรียก callback ทุกครั้งที่มี event ใดๆ เกิดขึ้น (หลายสิบครั้งต่อ touch เดียว) ทำให้สิ้นเปลือง CPU และอาจเกิดปัญหาที่ไม่คาดคิด

#### 5.2 ข้อผิดพลาดที่พบบ่อย

<table><thead><tr><th width="259.5504150390625">ข้อผิดพลาด</th><th width="246.5958251953125">สาเหตุ</th><th>วิธีแก้</th></tr></thead><tbody><tr><td>ปุ่มเล็กเกินไป</td><td>ใช้ <code>lv_obj_set_size()</code> กับ Button</td><td>ใช้ <code>lv_obj_set_style_pad_hor/ver()</code> แทน</td></tr><tr><td>Label หายจากปุ่ม</td><td>สร้าง Label ก่อน set_size</td><td>สร้าง Label หลังสุด แล้ว <code>lv_obj_center()</code></td></tr><tr><td>Callback ไม่ทำงาน</td><td>ลืม add_event_cb หรือ event code ผิด</td><td>ตรวจสอบ filter event</td></tr><tr><td>Counter กลับเป็น 0</td><td>ตัวแปร local ไม่ใช่ static</td><td>ประกาศ <code>static</code></td></tr><tr><td>user_data เป็น garbage</td><td>ส่ง address ของ local variable</td><td>ใช้ <code>static</code> variable หรือ global</td></tr><tr><td>กด 1 ครั้ง callback เรียกหลายครั้ง</td><td>ใช้ LV_EVENT_ALL</td><td>ใช้ LV_EVENT_CLICKED</td></tr></tbody></table>

#### 5.3 ขนาดปุ่มที่แนะนำสำหรับ Touch Screen

```
    Minimum touch target: 40x40 pixels
    Recommended padding:  hor=25-40, ver=12-20

    +-----------------------+
    |  pad_ver = 15         |
    |  +---------------+    |
    |  |  "Click Me!"  |    |   <-- Label ถูก center อัตโนมัติ
    |  +---------------+    |
    |  pad_hor = 30         |
    +-----------------------+

    ขนาดปุ่มโดยประมาณ:
    - เล็ก:   pad_hor=20, pad_ver=10  (~80x34 px)
    - กลาง:  pad_hor=30, pad_ver=15  (~120x44 px)
    - ใหญ่:   pad_hor=40, pad_ver=20  (~160x54 px)
```

#### 5.4 Static Variables vs. Global Variables

```c
/* วิธี 1: Static ภายใน Callback (เรียบง่าย, ใช้กับ counter) */
static void cb(lv_event_t * e) {
    static int count = 0;   /* เห็นได้เฉพาะใน function นี้ */
    count++;
}

/* วิธี 2: Static Global (ใช้เมื่อ callback ต้องอัปเดต widget อื่น) */
static lv_obj_t * my_label;     /* เข้าถึงได้ทั้งไฟล์ */
static void cb(lv_event_t * e) {
    lv_label_set_text(my_label, "Updated!");
}

/* วิธี 3: User Data (ใช้เมื่อ callback ถูก reuse) */
static void cb(lv_event_t * e) {
    lv_obj_t * label = (lv_obj_t *)lv_event_get_user_data(e);
    lv_label_set_text(label, "Updated!");
}
lv_obj_add_event_cb(btn, cb, LV_EVENT_CLICKED, my_label);
```

***

### 6. แบบฝึกหัด (Exercises)

#### Exercise 1: Increment/Decrement Counter - 10-15 นาที

**โจทย์**: สร้าง Counter ที่มีปุ่ม 2 ตัว สำหรับเพิ่ม (+1) และลด (-1) ค่า

**ข้อกำหนด**:

1. Background สี `0x1a1a2e`
2. Title "Production Counter" (font\_24, สีขาว, TOP\_MID)
3. Label แสดงค่า counter เริ่มที่ 0 (font\_24, สีขาว, CENTER, y=-30)
4. ปุ่ม "+" สีเขียว อยู่ทางซ้าย (CENTER, x=-80, y=+40)
5. ปุ่ม "-" สีแดง อยู่ทางขวา (CENTER, x=+80, y=+40)
6. ค่า counter ไม่ต่ำกว่า 0 (ถ้า counter == 0 แล้วกด "-" ไม่ลด)
7. แสดง format "Count: XX"

**Expected Output:**

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

**Hints:**

* ใช้ `static int` สำหรับ counter
* สร้าง global label pointer สำหรับแสดงค่า
* ใช้ user\_data ส่ง direction (+1 หรือ -1)

***

#### Exercise 2: Toggle Button (Medium)

สร้างปุ่มที่สลับระหว่าง "ON" และ "OFF" เมื่อกด พร้อมเปลี่ยนสีปุ่ม

**Expected Output:**

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

**Hints:**

* ใช้ `static bool state = false;`
* `lv_obj_set_style_bg_color()` เปลี่ยนสีปุ่ม

***

#### Exercise 3: Long Press Detection (Advanced)

สร้างปุ่มที่แยกการกดสั้นและกดค้าง:

* กดสั้น: แสดง "Short Press"
* กดค้าง 1 วินาที: แสดง "Long Press"

**Hints:**

* ใช้ `LV_EVENT_CLICKED` และ `LV_EVENT_LONG_PRESSED`
* สามารถ register หลาย callback ได้

***

### 7. References

* [LVGL Button Documentation](https://docs.lvgl.io/9.2/widgets/button.html)
* [LVGL Button Example](https://github.com/lvgl/lvgl/blob/release/v9.2/examples/widgets/button/lv_example_button_1.c)
* [LVGL Events Guide](https://docs.lvgl.io/9.2/overview/event.html)

***

**Next Lab:** Lab 3: LED Widget Control


---

# 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/ux-ui-workshops/label-and-button/button.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.
