> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/sensor-to-hmi-display/ux-ui-workshops/slider-bar.md).

# Slider Bar

## Lab 1: Slider & Bar Widget (ADC Visualization)

***

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

#### Why? - ทำไมต้องเรียนรู้เรื่องนี้

* **ADC Visualization**: ค่า ADC 0-4095 เป็นตัวเลขดิบที่อ่านยาก จำเป็นต้องแปลงเป็นภาพที่เข้าใจง่าย
* **Slider vs Bar**: เรียนรู้ความแตกต่างระหว่าง widget ที่รับ input (Slider) กับ widget ที่แสดงผลอย่างเดียว (Bar)
* **Value Mapping**: ฝึกการแปลงค่า raw ADC เป็น percentage และ voltage ซึ่งเป็นพื้นฐานของ Sensor Data Processing
* **Industrial Application**: Bar chart และ slider เป็นส่วนสำคัญของ SCADA/HMI ในงานอุตสาหกรรม

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

1. **Slider Widget**: สร้าง slider พร้อม range, value, และ event callback
2. **Bar Widget**: สร้าง progress bar แบบ read-only สำหรับแสดงสถานะ
3. **ADC Mapping**: สูตรการแปลง ADC raw value เป็น percentage และ voltage
4. **Timer Update Pattern**: ใช้ `lv_timer_create()` อัพเดท UI แบบ periodic
5. **Event Callback**: จัดการ `LV_EVENT_VALUE_CHANGED` เพื่อตอบสนองต่อการเปลี่ยนแปลงค่า

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

1. สร้าง Slider แสดง ADC raw value (0-4095)
2. สร้าง Bar แสดง percentage (0-100%)
3. เพิ่ม Label แสดง Voltage คำนวณจาก ADC
4. ใช้ Timer จำลองค่า ADC ที่เปลี่ยนแปลงตามเวลา
5. เชื่อมต่อ Slider event กับ Bar update

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FtAeTUgoiwEGOWg3l5FK4%2Fweek4_ex1_slider_bar.gif?alt=media&amp;token=9010f5ef-4b26-4e04-bd58-0ed4e0ca0b18" alt=""><figcaption></figcaption></figure>

***

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

#### 2.1 Slider vs Bar Concept

```
┌─────────────────────────────────────────────────────────────┐
│                  SLIDER vs BAR CONCEPT                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   SLIDER (Interactive)                                      │
│   ┌──────────────────────────────────┐                      │
│   │   ══════════●══════              │  ← มี knob ลากได้      │
│   │          (draggable)             │                      │
│   └──────────────────────────────────┘                      │
│   Use Case: User adjusts setpoint, threshold                │
│                                                             │
│   BAR (Read-Only)                                           │
│   ┌──────────────────────────────────┐                      │
│   │   ████████████░░░░░░░░░░░░░░░░░  │  ← แสดงค่าอย่างเดียว    │
│   │           (no knob)              │                      │
│   └──────────────────────────────────┘                      │
│   Use Case: Show sensor value, progress, battery level      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 ADC Value Mapping Pipeline

```
┌─────────────────────────────────────────────────────────────┐
│                 ADC VALUE MAPPING PIPELINE                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ADC Raw Value (0-4095, 12-bit)                            │
│         │                                                   │
│         ├──────────────────────────────────┐                │
│         ▼                                  ▼                │
│   ┌─────────────────┐           ┌─────────────────┐         │
│   │ Slider Display  │           │  % = raw x 100  │         │
│   │  Range: 0-4095  │           │     ─────────   │         │
│   └─────────────────┘           │       4095      │         │
│                                 └────────┬────────┘         │
│                                          │                  │
│                                          ▼                  │
│                                 ┌─────────────────┐         │
│                                 │  Bar Display    │         │
│                                 │  Range: 0-100%  │         │
│                                 └─────────────────┘         │
│         │                                                   │
│         ▼                                                   │
│   ┌─────────────────┐                                       │
│   │  V = raw x 3.3  │                                       │
│   │     ─────────   │  Voltage calculation                  │
│   │       4095      │                                       │
│   └────────┬────────┘                                       │
│            ▼                                                │
│   ┌─────────────────┐                                       │
│   │  Label: "1.65V" │  แสดงค่า voltage                       │
│   └─────────────────┘                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.3 Timer Update Flow

```
┌─────────────────────────────────────────────────────────────┐
│                   TIMER UPDATE FLOW                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   lv_timer_create(timer_cb, 100ms)                          │
│         │                                                   │
│         ▼                                                   │
│   ┌─────────────────┐                                       │
│   │ Read ADC Value  │  ← simulate_adc_read() หรือ HW         │
│   └────────┬────────┘                                       │
│            │                                                │
│            ▼                                                │
│   ┌─────────────────┐                                       │
│   │ Update Slider   │  → lv_slider_set_value()              │
│   └────────┬────────┘                                       │
│            │                                                │
│            ▼                                                │
│   ┌─────────────────┐                                       │
│   │ Calculate %     │  → pct = raw * 100 / 4095             │
│   └────────┬────────┘                                       │
│            │                                                │
│            ▼                                                │
│   ┌─────────────────┐                                       │
│   │ Update Bar      │  → lv_bar_set_value()                 │
│   └────────┬────────┘                                       │
│            │                                                │
│            ▼                                                │
│   ┌─────────────────┐                                       │
│   │ Update Labels   │  → lv_label_set_text_fmt()            │
│   └─────────────────┘                                       │
│                                                             │
│   ◄──── ทำซ้ำทุก 100ms ────►                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

***

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

#### 3.1 Slider Functions

<table><thead><tr><th width="332.02557373046875">Function</th><th width="233.438232421875">Parameters</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_slider_create(parent)</code></td><td>parent: lv_obj_t*</td><td>สร้าง slider widget บน parent</td></tr><tr><td><code>lv_slider_set_range(obj, min, max)</code></td><td>min, max: int32_t</td><td>กำหนดช่วงค่า min-max</td></tr><tr><td><code>lv_slider_set_value(obj, val, anim)</code></td><td>val: int32_t, anim: lv_anim_enable_t</td><td>ตั้งค่า slider</td></tr><tr><td><code>lv_slider_get_value(obj)</code></td><td>-</td><td>อ่านค่าปัจจุบัน (return int32_t)</td></tr><tr><td><code>lv_slider_set_mode(obj, mode)</code></td><td>mode: lv_slider_mode_t</td><td>NORMAL / SYMMETRICAL / RANGE</td></tr></tbody></table>

#### 3.2 Bar Functions

<table><thead><tr><th width="353.63067626953125">Function</th><th>Parameters</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_bar_create(parent)</code></td><td>parent: lv_obj_t*</td><td>สร้าง bar widget (read-only)</td></tr><tr><td><code>lv_bar_set_range(obj, min, max)</code></td><td>min, max: int32_t</td><td>กำหนดช่วงค่า</td></tr><tr><td><code>lv_bar_set_value(obj, val, anim)</code></td><td>val: int32_t, anim: lv_anim_enable_t</td><td>ตั้งค่า bar</td></tr><tr><td><code>lv_bar_set_start_value(obj, val, anim)</code></td><td>val: int32_t</td><td>ตั้งค่าเริ่มต้น (สำหรับ range mode)</td></tr><tr><td><code>lv_bar_set_mode(obj, mode)</code></td><td>mode: lv_bar_mode_t</td><td>NORMAL / SYMMETRICAL / RANGE</td></tr></tbody></table>

#### 3.3 Common Styling

<table><thead><tr><th width="407.14984130859375">Function</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_obj_set_size(obj, w, h)</code></td><td>กำหนดขนาด width x height</td></tr><tr><td><code>lv_obj_set_style_bg_color(obj, color, part)</code></td><td>เปลี่ยนสีพื้นหลัง</td></tr><tr><td><code>lv_obj_set_style_bg_color(obj, color, LV_PART_INDICATOR)</code></td><td>เปลี่ยนสี indicator (ส่วนที่เติม)</td></tr><tr><td><code>lv_obj_set_style_anim_duration(obj, ms, 0)</code></td><td>กำหนดเวลา animation</td></tr><tr><td><code>lv_obj_add_event_cb(obj, cb, LV_EVENT_VALUE_CHANGED, NULL)</code></td><td>เพิ่ม callback เมื่อค่าเปลี่ยน</td></tr></tbody></table>

#### 3.4 ADC Calculation Formulas

```c
/* ADC Constants (PSoC Edge E84) */
#define ADC_MAX      4095    /* 12-bit resolution: 2^12 - 1 */
#define ADC_VREF_MV  3300    /* Reference voltage: 3.3V = 3300mV */

/* Percentage calculation */
int32_t pct = (adc_value * 100) / ADC_MAX;

/* Voltage calculation (float) */
float voltage = ((float)adc_value / ADC_MAX) * (ADC_VREF_MV / 1000.0f);

/* Voltage calculation (integer mV - no float needed) */
int32_t voltage_mv = (adc_value * ADC_VREF_MV) / ADC_MAX;
```

***

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

#### 4.1 Step 1: Global Variables และ Constants

```c
#include "lvgl.h"

/* ---- Constants ---- */
#define ADC_MAX      4095
#define ADC_VREF_MV  3300

/* ---- Global Widget Handles ---- */
static lv_obj_t * adc_slider;       /* Slider แสดง raw ADC */
static lv_obj_t * pct_bar;          /* Bar แสดง percentage */
static lv_obj_t * raw_label;        /* Label แสดง raw value */
static lv_obj_t * pct_label;        /* Label แสดง percentage */
static lv_obj_t * volt_label;       /* Label แสดง voltage */
static lv_timer_t * update_timer;   /* Timer สำหรับ auto-update */
```

#### 4.2 Step 2: Simulated ADC Read

```c
/*
 * simulate_adc_read() - จำลองค่า ADC ที่เปลี่ยนแปลง
 *
 * ใช้ sine wave + noise เพื่อให้ดูเหมือนสัญญาณจริง
 * ในการใช้งานจริง: แทนที่ด้วย aic_adc_read() หรือ HAL ADC API
 */
static uint16_t simulate_adc_read(void)
{
    static uint32_t tick = 0;
    tick++;

    /* Sine wave: center at 2048, amplitude 1500 */
    float angle = (float)tick * 0.05f;
    int32_t base = 2048 + (int32_t)(1500.0f * sinf(angle));

    /* Add small noise */
    int32_t noise = (lv_rand(0, 100) - 50);
    int32_t value = base + noise;

    /* Clamp to valid range */
    if (value < 0)    value = 0;
    if (value > 4095)  value = 4095;

    return (uint16_t)value;
}
```

#### 4.3 Step 3: Slider Event Callback

```c
/*
 * slider_event_cb() - เมื่อ user ลาก slider หรือค่าเปลี่ยนจาก timer
 *
 * ทำหน้าที่:
 * 1. อ่านค่า slider ปัจจุบัน
 * 2. คำนวณ percentage
 * 3. คำนวณ voltage
 * 4. อัพเดท bar และ labels ทั้งหมด
 */
static void slider_event_cb(lv_event_t * e)
{
    lv_obj_t * slider = lv_event_get_target(e);
    int32_t value = lv_slider_get_value(slider);

    /* คำนวณ percentage */
    int32_t pct = (value * 100) / ADC_MAX;

    /* คำนวณ voltage (mV) */
    int32_t voltage_mv = (value * ADC_VREF_MV) / ADC_MAX;

    /* อัพเดท Bar */
    lv_bar_set_value(pct_bar, pct, LV_ANIM_ON);

    /* อัพเดท Labels */
    lv_label_set_text_fmt(raw_label, "Raw: %d / %d", (int)value, ADC_MAX);
    lv_label_set_text_fmt(pct_label, "%d %%", (int)pct);
    lv_label_set_text_fmt(volt_label, "%d.%03d V",
                          (int)(voltage_mv / 1000),
                          (int)(voltage_mv % 1000));
}
```

#### 4.4 Step 4: Timer Callback สำหรับ Auto-Update

```c
/*
 * timer_cb() - ถูกเรียกทุก 100ms โดย LVGL timer system
 *
 * อ่านค่า ADC (จำลอง) แล้วอัพเดท slider
 * slider จะ trigger event callback อัตโนมัติ
 */
static void timer_cb(lv_timer_t * timer)
{
    (void)timer;

    /* อ่านค่า ADC (จำลอง หรือ hardware จริง) */
    uint16_t adc_value = simulate_adc_read();

    /* อัพเดท slider ค่า */
    lv_slider_set_value(adc_slider, adc_value, LV_ANIM_ON);

    /* ส่ง event เพื่อให้ callback ทำงาน */
    lv_obj_send_event(adc_slider, LV_EVENT_VALUE_CHANGED, NULL);
}
```

#### 4.5 Step 5: Main Function - สร้าง UI ทั้งหมด

```c
void part2_ex1_slider_bar(void)
{
    /* ---- Background สีเข้ม ---- */
    lv_obj_set_style_bg_color(lv_screen_active(),
                               lv_color_hex(0x1a1a2e), 0);

    /* ---- Title ---- */
    lv_obj_t * title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "ADC Visualization");
    lv_obj_set_style_text_color(title, lv_color_hex(0x00d4ff), 0);
    lv_obj_set_style_text_font(title, &lv_font_montserrat_24, 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);

    /* ---- Slider (ADC Raw Value) ---- */
    lv_obj_t * slider_label = lv_label_create(lv_screen_active());
    lv_label_set_text(slider_label, "ADC Raw Value (0-4095):");
    lv_obj_set_style_text_color(slider_label, lv_color_hex(0xcccccc), 0);
    lv_obj_align(slider_label, LV_ALIGN_TOP_LEFT, 20, 50);

    adc_slider = lv_slider_create(lv_screen_active());
    lv_obj_set_width(adc_slider, 280);
    lv_obj_align(adc_slider, LV_ALIGN_TOP_MID, 0, 75);
    lv_slider_set_range(adc_slider, 0, ADC_MAX);
    lv_slider_set_value(adc_slider, 2048, LV_ANIM_OFF);
    lv_obj_add_event_cb(adc_slider, slider_event_cb,
                         LV_EVENT_VALUE_CHANGED, NULL);

    /* ---- Raw Value Label ---- */
    raw_label = lv_label_create(lv_screen_active());
    lv_label_set_text(raw_label, "Raw: 2048 / 4095");
    lv_obj_set_style_text_color(raw_label, lv_color_white(), 0);
    lv_obj_align(raw_label, LV_ALIGN_TOP_MID, 0, 100);

    /* ---- Bar (Percentage) ---- */
    lv_obj_t * bar_label = lv_label_create(lv_screen_active());
    lv_label_set_text(bar_label, "Percentage:");
    lv_obj_set_style_text_color(bar_label, lv_color_hex(0xcccccc), 0);
    lv_obj_align(bar_label, LV_ALIGN_TOP_LEFT, 20, 130);

    pct_bar = lv_bar_create(lv_screen_active());
    lv_obj_set_size(pct_bar, 280, 25);
    lv_obj_align(pct_bar, LV_ALIGN_TOP_MID, 0, 155);
    lv_bar_set_range(pct_bar, 0, 100);
    lv_bar_set_value(pct_bar, 50, LV_ANIM_OFF);
    lv_obj_set_style_bg_color(pct_bar, lv_color_hex(0x333333), LV_PART_MAIN);
    lv_obj_set_style_bg_color(pct_bar,
                               lv_palette_main(LV_PALETTE_BLUE),
                               LV_PART_INDICATOR);

    /* ---- Percentage Label (บน Bar) ---- */
    pct_label = lv_label_create(lv_screen_active());
    lv_label_set_text(pct_label, "50 %");
    lv_obj_set_style_text_color(pct_label, lv_color_white(), 0);
    lv_obj_align_to(pct_label, pct_bar, LV_ALIGN_OUT_RIGHT_MID, 10, 0);

    /* ---- Voltage Display (ใหญ่ตรงกลาง) ---- */
    volt_label = lv_label_create(lv_screen_active());
    lv_label_set_text(volt_label, "1.650 V");
    lv_obj_set_style_text_color(volt_label, lv_color_hex(0x00ff88), 0);
    lv_obj_set_style_text_font(volt_label, &lv_font_montserrat_24, 0);
    lv_obj_align(volt_label, LV_ALIGN_CENTER, 0, 30);

    /* ---- Timer: Auto-update ทุก 100ms ---- */
    update_timer = lv_timer_create(timer_cb, 100, NULL);
}
```

#### 4.6 Data Flow Summary

```
simulate_adc_read()
        │
        ▼
  timer_cb() [ทุก 100ms]
        │
        ├── lv_slider_set_value()  → อัพเดท slider position
        │
        └── lv_obj_send_event()    → trigger callback
                    │
                    ▼
            slider_event_cb()
                    │
                    ├── คำนวณ pct, voltage
                    ├── lv_bar_set_value()      → อัพเดท bar
                    ├── lv_label_set_text_fmt()  → raw label
                    ├── lv_label_set_text_fmt()  → pct label
                    └── lv_label_set_text_fmt()  → volt label
```

***

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

#### 5.1 Slider vs Bar - เมื่อไหร่ใช้อะไร

<table><thead><tr><th width="204.36224365234375">Feature</th><th>Slider</th><th>Bar</th></tr></thead><tbody><tr><td>User Input</td><td>ใช่ (ลากได้)</td><td>ไม่ (read-only)</td></tr><tr><td>Knob</td><td>มี knob ลากได้</td><td>ไม่มี</td></tr><tr><td>Use Case</td><td>ปรับ setpoint, threshold</td><td>แสดง progress, level</td></tr><tr><td>Event</td><td>LV_EVENT_VALUE_CHANGED</td><td>ไม่มี (ตั้งค่าจากโค้ด)</td></tr><tr><td>CPU Usage</td><td>สูงกว่า (รับ touch)</td><td>ต่ำกว่า</td></tr><tr><td>ตัวอย่างใช้งาน</td><td>Volume control, Brightness</td><td>Battery, Loading, Fuel</td></tr></tbody></table>

#### 5.2 Animation Options

```c
/* LV_ANIM_ON - เปลี่ยนแบบ smooth (แนะนำสำหรับ UI) */
lv_bar_set_value(bar, 75, LV_ANIM_ON);

/* LV_ANIM_OFF - เปลี่ยนทันที (สำหรับ high-speed update) */
lv_bar_set_value(bar, 75, LV_ANIM_OFF);
```

> **Tip**: ถ้า update rate สูง (< 50ms) ให้ใช้ `LV_ANIM_OFF` เพื่อลด CPU load

#### 5.3 ทำ Slider เป็น Read-Only

```c
/* วิธีที่ 1: Disable slider (สีจะเปลี่ยนเป็นจาง) */
lv_obj_add_state(adc_slider, LV_STATE_DISABLED);

/* วิธีที่ 2: ลบ flag clickable (สียังเหมือนเดิม) */
lv_obj_remove_flag(adc_slider, LV_OBJ_FLAG_CLICKABLE);
```

#### 5.4 เปลี่ยนสี Indicator ตามค่า

```c
/* Pattern: Color Threshold - ใช้บ่อยมากในงาน Industrial */
void update_bar_color_by_value(lv_obj_t * bar, int32_t pct)
{
    lv_color_t color;

    if (pct <= 20) {
        color = lv_palette_main(LV_PALETTE_RED);      /* วิกฤต */
    } else if (pct <= 50) {
        color = lv_palette_main(LV_PALETTE_ORANGE);    /* ต่ำ */
    } else {
        color = lv_palette_main(LV_PALETTE_GREEN);     /* ปกติ */
    }

    lv_obj_set_style_bg_color(bar, color, LV_PART_INDICATOR);
}
```

#### 5.5 Integer Math vs Float

```c
/* ไม่แนะนำ: ใช้ float บน embedded (ช้า, ใช้ FPU) */
float voltage = ((float)adc_value / 4095.0f) * 3.3f;
printf("%.3f V", voltage);  /* ต้องมี float support ใน printf */

/* แนะนำ: ใช้ integer math + แยกหน่วย */
int32_t mv = (adc_value * 3300) / 4095;
lv_label_set_text_fmt(label, "%d.%03d V", mv / 1000, mv % 1000);
/* เร็วกว่า, ไม่ต้องใช้ FPU, ไม่ต้อง printf float */
```

#### 5.6 Memory Pattern: Static Variables

```c
/* Widget handles ต้องเป็น static หรือ global
 * เพราะ LVGL timer/callback เรียกใช้ทีหลัง */
static lv_obj_t * my_bar;       /* ถูกต้อง */
static lv_timer_t * my_timer;   /* ถูกต้อง */

/* ห้าม: local variable ใน function */
void bad_example(void) {
    lv_obj_t * bar = lv_bar_create(...);  /* bar หายเมื่อ function จบ */
    /* callback จะ crash เพราะ pointer ไม่ valid แล้ว */
}
```

***

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

#### Exercise 1: Battery Level Indicator

สร้าง Battery Level Indicator ที่ประกอบด้วย:

**Requirements:**

* Bar แสดงระดับแบตเตอรี่ (0-100%)
* Label แสดง percentage ตัวเลข
* **เปลี่ยนสี** ตามระดับ:
  * 0-20%: สีแดง (LV\_PALETTE\_RED) - ต่ำวิกฤต
  * 21-50%: สีส้ม (LV\_PALETTE\_ORANGE) - ต่ำ
  * 51-100%: สีเขียว (LV\_PALETTE\_GREEN) - ปกติ
* Timer จำลองค่าแบตเตอรี่ลดลงทุก 500ms (ลดครั้งละ 1%)
* เมื่อถึง 0% ให้รีเซ็ตเป็น 100% (จำลองการชาร์จ)

```
┌──────────────────────────────────────┐
│         Battery Monitor              │
│                                      │
│   ████████████████░░░░░░  73%        │
│   [████████████████      ]           │
│                                      │
│         Status: Normal               │
└──────────────────────────────────────┘
```

**Hints:**

* ใช้ `lv_bar_set_value()` อัพเดทค่า
* ใช้ `lv_obj_set_style_bg_color()` กับ `LV_PART_INDICATOR` เปลี่ยนสี
* สร้าง status label ที่เปลี่ยนข้อความตาม level

***

#### Exercise 2: Audio Mixer - Volume Control with Slider & Bar

สร้าง Audio Mixer ที่มี Slider ควบคุม Volume เชื่อมกับ Bar แสดงระดับ:

**Requirements:**

* Slider สำหรับปรับ Volume (0-100)
* Bar แสดง Volume Level แบบ real-time
* Label แสดงค่า Volume ปัจจุบัน
* **เพิ่ม Mute button**: กดแล้ว volume เป็น 0, กดอีกทีกลับค่าเดิม
* สี bar เปลี่ยนตาม volume:
  * 0: สีเทา (Muted)
  * 1-70: สีเขียว (Normal)
  * 71-90: สีเหลือง (Loud)
  * 91-100: สีแดง (Clipping!)

```
┌──────────────────────────────────────┐
│          Volume Mixer                │
│                                      │
│   VOL  ═══════════●═══  [65%]        │
│   LEVEL ████████████░░░░             │
│                                      │
│          [  MUTE  ]                  │
│                                      │
│         Status: Normal               │
└──────────────────────────────────────┘
```

**Hints:**

* ใช้ `lv_slider_get_value()` ใน event callback
* สร้าง `lv_button_create()` สำหรับ Mute button
* เก็บ `last_volume` ใน static variable สำหรับ mute/unmute

***

#### Exercise 3: Multi-Channel Energy Meter (ท้าทาย)

สร้าง Energy Meter แสดงกำลังไฟฟ้า 3 เฟส:

**Requirements:**

* Bar 3 อัน สำหรับ Phase A, B, C (0-1000 Watt)
* แต่ละ Phase มี label แสดง Watt
* Bar ด้านล่างแสดง Total Power (ผลรวม 3 Phase)
* Timer จำลองค่า power ที่เปลี่ยนแปลง
* Color coding: < 300W Green, 300-700W Yellow, > 700W Red

```
┌──────────────────────────────────────┐
│        3-Phase Energy Meter          │
│                                      │
│   Phase A  ████████░░░░  450W        │
│   Phase B  ██████░░░░░░  320W        │
│   Phase C  ████████████  780W        │
│   ─────────────────────────          │
│   TOTAL    ██████████░░  1550W       │
└──────────────────────────────────────┘
```

***

### 7. References

* [LVGL Slider](https://docs.lvgl.io/9.2/widgets/slider.html)
* [LVGL Bar](https://docs.lvgl.io/9.2/widgets/bar.html)
* [lv\_example\_slider\_1.c](https://github.com/lvgl/lvgl/blob/release/v9.2/examples/widgets/slider/lv_example_slider_1.c)

***
