> 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/scale-temperature.md).

# Scale Temperature

## Lab 4: Scale Widget (Temperature Gauge)

***

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

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

* **Professional Gauge**: Scale widget สร้างเครื่องมือวัดที่ดูเหมือนเครื่องมือจริง มี tick marks และ labels
* **Color Zones**: แสดง warning zones ด้วยสี (blue=cold, green=normal, red=hot) ซึ่งเป็นมาตรฐานในอุตสาหกรรม
* **Needle Display**: เข็มชี้ค่าปัจจุบันเหมือน analog gauge ดั้งเดิม
* **Multiple Modes**: ใช้ได้ทั้งแบบ vertical (thermometer) และ round (pressure gauge)
* **Industry Standard**: Scale/Gauge เป็นองค์ประกอบหลักของ SCADA, HMI, และ BMS (Building Management System)

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

1. **Scale Widget**: สร้าง gauge ด้วย `lv_scale_create()`
2. **Tick Marks**: กำหนด total ticks, major tick interval, tick length
3. **Scale Sections**: เพิ่ม color zones (blue, green, red)
4. **Needle Line**: สร้างเข็มชี้ด้วย `lv_line_create()` บน scale
5. **Scale Modes**: HORIZONTAL, VERTICAL, ROUND\_INNER, ROUND\_OUTER

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

1. สร้าง Scale widget ในโหมด `LV_SCALE_MODE_ROUND_OUTER`
2. กำหนด tick count, major tick interval, label format
3. เพิ่ม 3 sections สำหรับ color zones
4. สร้าง needle line ชี้ค่าปัจจุบัน
5. ใช้ timer อัพเดทค่าจาก simulated sensor

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2Fu2yot7x2FCN461i55tUi%2Fweek4_ex4_scale_temperature.gif?alt=media&amp;token=368f965f-4985-4208-bb12-c5c613ab1886" alt=""><figcaption></figcaption></figure>

***

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

#### 2.1 Scale Anatomy

```
┌─────────────────────────────────────────────────────────────┐
│                    SCALE ANATOMY                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ROUND_OUTER Mode (Gauge):                                 │
│                                                             │
│              Labels (ตัวเลข)                                 │
│                 │                                           │
│                 ▼                                           │
│           0    10    20    30    40    50                   │
│          ╭─┬─┬─┬──┬─┬─┬──┬─┬─┬──┬─┬─╮                       │
│         ╱  │ │ │  │ │ │  │ │ │  │ │ │╲                      │
│        │   │ │ │  │ │ │  │ │ │  │ │ │ │                     │
│        │   │     │         │   │      │                     │
│        │  Minor  │  Major  │  Minor   │                     │
│        │  Ticks  │  Ticks  │  Ticks   │                     │
│        │         │         │          │                     │
│        │         │  NEEDLE │          │                     │
│        │         │    ╲    │          │                     │
│         ╲        │     ●──────────   ╱                      │
│          ╰───────┴──────────────────╯                       │
│                                                             │
│   Major Tick: ยาว, มี label ตัวเลข                            │
│   Minor Tick: สั้น, ไม่มี label                                 │
│   Needle: เข็มชี้ค่า (lv_line widget)                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 Scale Modes

```
┌─────────────────────────────────────────────────────────────┐
│                   SCALE MODES                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. LV_SCALE_MODE_HORIZONTAL (แนวนอน)                      │
│      0───────10───────20───────30───────40                  │
│      |||  ||||  ||||  ||||  ||||                            │
│                                                             │
│   2. LV_SCALE_MODE_VERTICAL (แนวตั้ง - thermometer)           │
│      40 ─┤                                                  │
│         ─┤                                                  │
│      30 ─┤                                                  │
│         ─┤                                                  │
│      20 ─┤                                                  │
│         ─┤                                                  │
│      10 ─┤                                                  │
│         ─┤                                                  │
│       0 ─┤                                                  │
│                                                             │
│   3. LV_SCALE_MODE_ROUND_OUTER (gauge - ticks ด้านนอก)       │
│           ╭──┬──╮                                           │
│          ╱ 0  50 ╲                                          │
│         │ ┤    ├  │                                         │
│          ╲ ┤  ├  ╱                                          │
│           ╰──┴──╯                                           │
│                                                             │
│   4. LV_SCALE_MODE_ROUND_INNER (gauge - ticks ด้านใน)        │
│           ╭──────╮                                          │
│          ╱  ├  ├  ╲                                         │
│         │  0    50 │                                        │
│          ╲  ├  ├  ╱                                         │
│           ╰──────╯                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.3 Color Sections (Temperature Zones)

```
┌─────────────────────────────────────────────────────────────┐
│                  COLOR SECTIONS                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Temperature Gauge (-10 to 50 degC):                       │
│                                                             │
│   Section 1: COLD (Blue)     -10 to 10 degC                 │
│   Section 2: NORMAL (Green)   10 to 35 degC                 │
│   Section 3: HOT (Red)        35 to 50 degC                 │
│                                                             │
│           -10    10     35    50                            │
│           ╭──────┬──────┬──────╮                            │
│          ╱ BLUE  │GREEN │ RED   ╲                           │
│         │ ██████ │██████│██████ │                           │
│         │  COLD  │NORMAL│ HOT   │                           │
│          ╲ ██████│██████│█████ ╱                            │
│           ╰──────┴──────┴─────╯                             │
│                                                             │
│   lv_scale_section_t * sec1, * sec2, * sec3;                │
│   sec1 = lv_scale_add_section(scale);                       │
│   lv_scale_section_set_range(sec1, -10, 10);                │
│   lv_scale_section_set_style(sec1, LV_PART_INDICATOR, &s);  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

***

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

#### 3.1 Scale Creation & Range

<table><thead><tr><th width="374.41546630859375">Function</th><th width="159.068115234375">Parameters</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_scale_create(parent)</code></td><td>parent: lv_obj_t*</td><td>สร้าง scale widget</td></tr><tr><td><code>lv_scale_set_mode(scale, mode)</code></td><td>mode: lv_scale_mode_t</td><td>HORIZONTAL/VERTICAL/ROUND_INNER/ROUND_OUTER</td></tr><tr><td><code>lv_scale_set_range(scale, min, max)</code></td><td>min, max: int32_t</td><td>กำหนดช่วงค่า</td></tr><tr><td><code>lv_scale_set_total_tick_count(scale, cnt)</code></td><td>cnt: uint32_t</td><td>จำนวน tick ทั้งหมด</td></tr><tr><td><code>lv_scale_set_major_tick_every(scale, nth)</code></td><td>nth: uint32_t</td><td>ทุกกี่ tick เป็น major</td></tr></tbody></table>

#### 3.2 Tick Configuration

<table><thead><tr><th width="445.6512451171875">Function</th><th width="125.538330078125">Parameters</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_scale_set_label_show(scale, show)</code></td><td>show: bool</td><td>แสดง/ซ่อน labels</td></tr><tr><td><code>lv_obj_set_style_length(scale, px, LV_PART_INDICATOR)</code></td><td>px: int32_t</td><td>ความยาว major tick</td></tr><tr><td><code>lv_obj_set_style_length(scale, px, LV_PART_ITEMS)</code></td><td>px: int32_t</td><td>ความยาว minor tick</td></tr></tbody></table>

#### 3.3 Round Scale Angles

<table><thead><tr><th width="337.757080078125">Function</th><th>Parameters</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_scale_set_rotation(scale, deg)</code></td><td>deg: int32_t</td><td>มุมเริ่มต้น (เช่น 135)</td></tr><tr><td><code>lv_scale_set_angle_range(scale, deg)</code></td><td>deg: uint32_t</td><td>sweep angle (เช่น 270)</td></tr></tbody></table>

#### 3.4 Scale Sections (Color Zones)

<table><thead><tr><th width="400.19677734375">Function</th><th width="182.9361572265625">Parameters</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_scale_add_section(scale)</code></td><td>-</td><td>สร้าง section ใหม่ (return pointer)</td></tr><tr><td><code>lv_scale_section_set_range(sec, min, max)</code></td><td>min, max: int32_t</td><td>กำหนดช่วงค่าของ section</td></tr><tr><td><code>lv_scale_section_set_style(sec, part, style)</code></td><td>part, style</td><td>กำหนด style สำหรับ section</td></tr></tbody></table>

***

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

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

```c
#include "lvgl.h"
#include <math.h>

/* ---- Widget Handles ---- */
static lv_obj_t * temp_scale;        /* Scale widget */
static lv_obj_t * needle_line;       /* Needle (lv_line) */
static lv_obj_t * value_label;       /* Center value label */
static lv_obj_t * status_label;      /* Status text */
static lv_timer_t * temp_timer;      /* Update timer */

/* ---- Needle points (ต้องเป็น static!) ---- */
static lv_point_precise_t needle_points[2];

/* ---- Section Styles ---- */
static lv_style_t style_cold;        /* Blue zone */
static lv_style_t style_normal;      /* Green zone */
static lv_style_t style_hot;         /* Red zone */

/* ---- Constants ---- */
#define TEMP_MIN        (-10)
#define TEMP_MAX        50
#define SCALE_SIZE      200           /* ขนาด scale (pixels) */
#define NEEDLE_LENGTH   75            /* ความยาวเข็ม */
```

#### 4.2 Step 2: Initialize Section Styles

```c
/*
 * init_section_styles() - สร้าง style สำหรับแต่ละ color zone
 *
 * ต้องเรียกก่อนสร้าง scale sections
 */
static void init_section_styles(void)
{
    /* Cold zone: Blue */
    lv_style_init(&style_cold);
    lv_style_set_arc_color(&style_cold,
                            lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_line_color(&style_cold,
                             lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_arc_width(&style_cold, 6);

    /* Normal zone: Green */
    lv_style_init(&style_normal);
    lv_style_set_arc_color(&style_normal,
                            lv_palette_main(LV_PALETTE_GREEN));
    lv_style_set_line_color(&style_normal,
                             lv_palette_main(LV_PALETTE_GREEN));
    lv_style_set_arc_width(&style_normal, 6);

    /* Hot zone: Red */
    lv_style_init(&style_hot);
    lv_style_set_arc_color(&style_hot,
                            lv_palette_main(LV_PALETTE_RED));
    lv_style_set_line_color(&style_hot,
                             lv_palette_main(LV_PALETTE_RED));
    lv_style_set_arc_width(&style_hot, 6);
}
```

#### 4.3 Step 3: Needle Update Function

```c
/*
 * update_needle_position() - คำนวณตำแหน่งเข็มจากค่า temperature
 *
 * มุมเข็ม = rotation + (value - min) / (max - min) * angle_range
 * rotation = 135, angle_range = 270
 */
static void update_needle_position(int32_t temp)
{
    /* Clamp to range */
    if (temp < TEMP_MIN) temp = TEMP_MIN;
    if (temp > TEMP_MAX) temp = TEMP_MAX;

    /* คำนวณ normalized position (0.0 to 1.0) */
    float normalized = (float)(temp - TEMP_MIN) /
                       (float)(TEMP_MAX - TEMP_MIN);

    /* คำนวณมุม (degrees) */
    float angle_deg = 135.0f + normalized * 270.0f;

    /* แปลง degrees to radians */
    float angle_rad = angle_deg * 3.14159f / 180.0f;

    /* จุดศูนย์กลางของ scale (relative to screen) */
    int32_t cx = 160;  /* ตรงกลางจอ */
    int32_t cy = 125;

    /* คำนวณ endpoint ของเข็ม */
    needle_points[0].x = cx;
    needle_points[0].y = cy;
    needle_points[1].x = cx + (int32_t)(NEEDLE_LENGTH * cosf(angle_rad));
    needle_points[1].y = cy + (int32_t)(NEEDLE_LENGTH * sinf(angle_rad));

    /* อัพเดท line widget */
    lv_line_set_points(needle_line, needle_points, 2);
}
```

#### 4.4 Step 4: Timer Callback

```c
/*
 * temp_timer_cb() - อัพเดทอุณหภูมิทุก 300ms
 */
static void temp_timer_cb(lv_timer_t * timer)
{
    (void)timer;

    /* จำลองอุณหภูมิ */
    static uint32_t tick = 0;
    tick++;

    float t = (float)tick * 0.02f;
    int32_t temp = 20 + (int32_t)(25.0f * sinf(t));
    temp += (lv_rand(0, 4) - 2);   /* noise +/- 2 */

    if (temp < TEMP_MIN) temp = TEMP_MIN;
    if (temp > TEMP_MAX)  temp = TEMP_MAX;

    /* อัพเดท needle */
    update_needle_position(temp);

    /* อัพเดท value label */
    lv_label_set_text_fmt(value_label, "%d degC", (int)temp);

    /* อัพเดทสีตัวเลขและ status */
    lv_color_t color;
    const char * status;

    if (temp <= 10) {
        color = lv_palette_main(LV_PALETTE_BLUE);
        status = "COLD";
    } else if (temp <= 35) {
        color = lv_palette_main(LV_PALETTE_GREEN);
        status = "NORMAL";
    } else {
        color = lv_palette_main(LV_PALETTE_RED);
        status = "HOT!";
    }

    lv_obj_set_style_text_color(value_label, color, 0);
    lv_label_set_text(status_label, status);
    lv_obj_set_style_text_color(status_label, color, 0);
}
```

#### 4.5 Step 5: Main Function - สร้าง Gauge UI

```c
void part2_ex4_scale_temperature(void)
{
    /* ---- Initialize styles ---- */
    init_section_styles();

    /* ---- Background ---- */
    lv_obj_set_style_bg_color(lv_screen_active(),
                               lv_color_hex(0x0f0f23), 0);

    /* ---- Title ---- */
    lv_obj_t * title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "Temperature Gauge");
    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, 5);

    /* ---- Scale Widget ---- */
    temp_scale = lv_scale_create(lv_screen_active());
    lv_obj_set_size(temp_scale, SCALE_SIZE, SCALE_SIZE);
    lv_obj_align(temp_scale, LV_ALIGN_CENTER, 0, 0);

    /* Round mode (gauge) */
    lv_scale_set_mode(temp_scale, LV_SCALE_MODE_ROUND_OUTER);
    lv_scale_set_range(temp_scale, TEMP_MIN, TEMP_MAX);

    /* Angle: 270-degree sweep เริ่มจากล่างซ้าย */
    lv_scale_set_rotation(temp_scale, 135);
    lv_scale_set_angle_range(temp_scale, 270);

    /* Tick configuration */
    lv_scale_set_total_tick_count(temp_scale, 31);    /* 31 ticks */
    lv_scale_set_major_tick_every(temp_scale, 5);     /* ทุก 5 ticks = major */
    lv_scale_set_label_show(temp_scale, true);

    /* Tick styling - major */
    lv_obj_set_style_length(temp_scale, 12, LV_PART_INDICATOR);
    lv_obj_set_style_line_width(temp_scale, 2, LV_PART_INDICATOR);
    lv_obj_set_style_line_color(temp_scale,
                                 lv_color_hex(0xcccccc), LV_PART_INDICATOR);

    /* Tick styling - minor */
    lv_obj_set_style_length(temp_scale, 6, LV_PART_ITEMS);
    lv_obj_set_style_line_width(temp_scale, 1, LV_PART_ITEMS);
    lv_obj_set_style_line_color(temp_scale,
                                 lv_color_hex(0x666666), LV_PART_ITEMS);

    /* Label styling */
    lv_obj_set_style_text_color(temp_scale,
                                 lv_color_hex(0xaaaaaa), LV_PART_INDICATOR);

    /* Main arc (background track) */
    lv_obj_set_style_arc_color(temp_scale,
                                lv_color_hex(0x333344), LV_PART_MAIN);
    lv_obj_set_style_arc_width(temp_scale, 4, LV_PART_MAIN);

    /* ---- Color Sections ---- */

    /* Section 1: Cold (-10 to 10) - Blue */
    lv_scale_section_t * sec_cold = lv_scale_add_section(temp_scale);
    lv_scale_section_set_range(sec_cold, TEMP_MIN, 10);
    lv_scale_section_set_style(sec_cold, LV_PART_MAIN, &style_cold);
    lv_scale_section_set_style(sec_cold, LV_PART_INDICATOR, &style_cold);
    lv_scale_section_set_style(sec_cold, LV_PART_ITEMS, &style_cold);

    /* Section 2: Normal (10 to 35) - Green */
    lv_scale_section_t * sec_normal = lv_scale_add_section(temp_scale);
    lv_scale_section_set_range(sec_normal, 10, 35);
    lv_scale_section_set_style(sec_normal, LV_PART_MAIN, &style_normal);
    lv_scale_section_set_style(sec_normal, LV_PART_INDICATOR, &style_normal);
    lv_scale_section_set_style(sec_normal, LV_PART_ITEMS, &style_normal);

    /* Section 3: Hot (35 to 50) - Red */
    lv_scale_section_t * sec_hot = lv_scale_add_section(temp_scale);
    lv_scale_section_set_range(sec_hot, 35, TEMP_MAX);
    lv_scale_section_set_style(sec_hot, LV_PART_MAIN, &style_hot);
    lv_scale_section_set_style(sec_hot, LV_PART_INDICATOR, &style_hot);
    lv_scale_section_set_style(sec_hot, LV_PART_ITEMS, &style_hot);

    /* ---- Needle Line ---- */
    needle_line = lv_line_create(lv_screen_active());
    lv_obj_set_style_line_width(needle_line, 3, 0);
    lv_obj_set_style_line_color(needle_line,
                                 lv_color_hex(0xff8800), 0);
    lv_obj_set_style_line_rounded(needle_line, true, 0);

    /* ตำแหน่งเริ่มต้น (20 degC) */
    update_needle_position(20);

    /* ---- Center pivot dot ---- */
    lv_obj_t * pivot = lv_obj_create(lv_screen_active());
    lv_obj_set_size(pivot, 10, 10);
    lv_obj_set_style_radius(pivot, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_color(pivot, lv_color_hex(0xff8800), 0);
    lv_obj_set_style_border_width(pivot, 0, 0);
    lv_obj_align(pivot, LV_ALIGN_CENTER, 0, 0);

    /* ---- Value Label ---- */
    value_label = lv_label_create(lv_screen_active());
    lv_label_set_text(value_label, "20 degC");
    lv_obj_set_style_text_color(value_label,
                                 lv_palette_main(LV_PALETTE_GREEN), 0);
    lv_obj_set_style_text_font(value_label, &lv_font_montserrat_24, 0);
    lv_obj_align(value_label, LV_ALIGN_CENTER, 0, 40);

    /* ---- Status Label ---- */
    status_label = lv_label_create(lv_screen_active());
    lv_label_set_text(status_label, "NORMAL");
    lv_obj_set_style_text_color(status_label,
                                 lv_palette_main(LV_PALETTE_GREEN), 0);
    lv_obj_align(status_label, LV_ALIGN_BOTTOM_MID, 0, -8);

    /* ---- Timer: อัพเดททุก 300ms ---- */
    temp_timer = lv_timer_create(temp_timer_cb, 300, NULL);
}
```

#### 4.6 UI Layout

```
┌──────────────────────────────────────┐
│         Temperature Gauge            │
│                                      │
│       -10    10     35    50         │
│        ╭──┬──┬──┬──┬──┬──╮           │
│       ╱BLUE │GREEN │ RED  ╲          │
│      │ ║║║║ │║║║║║ │║║║║║  │         │
│      │      │   ╱  │       │         │
│      │      │  ●───│       │         │
│       ╲ ║║║║│║║║║║ │║║║║  ╱          │
│        ╰──┴──┴──┴──┴──┴──╯           │
│                                      │
│            20 degC                   │
│            NORMAL                    │
└──────────────────────────────────────┘
```

***

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

#### 5.1 Scale Modes Comparison

| Mode         | Shape             | Use Case      | Example             |
| ------------ | ----------------- | ------------- | ------------------- |
| HORIZONTAL   | แนวนอน            | Ruler, level  | Signal strength bar |
| VERTICAL     | แนวตั้ง           | Thermometer   | Mercury thermometer |
| ROUND\_OUTER | วงกลม (ticks นอก) | Gauge มาตรฐาน | Speedometer         |
| ROUND\_INNER | วงกลม (ticks ใน)  | Gauge compact | Watch dial          |

#### 5.2 Tick Count Formula

```c
/*
 * การคำนวณ tick count:
 * - Range: min to max
 * - total_tick_count = (ตัวเลข major tick ทั้งหมด - 1) * minor_per_major + 1
 *
 * ตัวอย่าง: Range -10 to 50, major ทุก 10 degrees
 * - Major labels: -10, 0, 10, 20, 30, 40, 50 = 7 labels
 * - Minor between majors: 4 (ขีดเล็กระหว่าง major)
 * - major_tick_every = 5 (ทุก 5 minor ticks เป็น 1 major)
 * - total = (7-1) * 5 + 1 = 31 ticks
 */
lv_scale_set_total_tick_count(scale, 31);
lv_scale_set_major_tick_every(scale, 5);
```

#### 5.3 Needle Angle Calculation

```c
/*
 * สูตรคำนวณมุมเข็ม (สำหรับ 270-degree gauge, rotation 135):
 *
 * angle = start_rotation + (value - min) / (max - min) * sweep
 *
 * start_rotation = 135 degrees
 * sweep = 270 degrees
 *
 * ตัวอย่าง: temp = 20, range -10 to 50
 * normalized = (20 - (-10)) / (50 - (-10)) = 30/60 = 0.5
 * angle = 135 + 0.5 * 270 = 270 degrees
 * → เข็มชี้ตรงกลาง (12 o'clock position)
 */
```

#### 5.4 Section Style Pattern

```c
/* Pattern: สร้าง style แยกสำหรับแต่ละ section
 *
 * สำคัญ: style ต้องเป็น static หรือ global
 * เพราะ LVGL reference ไปที่ style pointer ตลอดเวลา
 */
static lv_style_t my_style;         /* ถูกต้อง: static */
lv_style_init(&my_style);
lv_style_set_arc_color(&my_style, lv_palette_main(LV_PALETTE_RED));
lv_style_set_line_color(&my_style, lv_palette_main(LV_PALETTE_RED));

/* ห้าม: local style ใน function */
void bad_example(void) {
    lv_style_t local_style;          /* ผิด: หายเมื่อ function จบ */
    lv_style_init(&local_style);     /* scale จะ crash ทีหลัง */
}
```

#### 5.5 Line Points ต้อง Static

```c
/* สำคัญมาก: lv_line ต้องการ static point array
 * เพราะ LVGL ไม่ copy ข้อมูล แค่เก็บ pointer */

static lv_point_precise_t points[2];    /* ถูกต้อง: static */
points[0].x = 100; points[0].y = 100;
points[1].x = 200; points[1].y = 50;
lv_line_set_points(line, points, 2);

/* ห้าม: local array */
void bad_needle(void) {
    lv_point_precise_t pts[2];     /* ผิด: หายเมื่อ function จบ */
    pts[0].x = 100; ...
    lv_line_set_points(line, pts, 2);  /* เข็มจะแสดงผิด/crash */
}
```

#### 5.6 Multiple Scales ซ้อนกัน

```c
/* สร้าง dual scale สำหรับ Temperature + Humidity */
lv_obj_t * outer = lv_scale_create(lv_screen_active());
lv_obj_set_size(outer, 200, 200);
lv_scale_set_mode(outer, LV_SCALE_MODE_ROUND_OUTER);
lv_scale_set_range(outer, 0, 50);    /* Temp degC */

lv_obj_t * inner = lv_scale_create(lv_screen_active());
lv_obj_set_size(inner, 160, 160);    /* เล็กกว่า */
lv_scale_set_mode(inner, LV_SCALE_MODE_ROUND_INNER);
lv_scale_set_range(inner, 0, 100);   /* Humidity % */

/* จัดให้อยู่ตรงกลางเดียวกัน */
lv_obj_align(outer, LV_ALIGN_CENTER, 0, 0);
lv_obj_align(inner, LV_ALIGN_CENTER, 0, 0);
```

***

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

#### Exercise 1: Pressure Gauge (0-100 PSI with 3 Color Zones)

สร้าง Pressure Gauge สำหรับ Industrial Monitoring:

**Requirements:**

* Scale mode: ROUND\_OUTER, 270-degree sweep
* Range: 0-100 PSI
* **3 Color zones:**
  * 0-30 PSI: สีเขียว (Normal operating pressure)
  * 31-70 PSI: สีเหลือง (Elevated pressure)
  * 71-100 PSI: สีแดง (Critical! Over-pressure warning)
* Needle ชี้ค่าปัจจุบัน
* Center label: "XX PSI"
* Status label: "Normal" / "Warning" / "CRITICAL!"
* Major tick ทุก 10 PSI (0, 10, 20, ..., 100)
* Timer จำลองค่า pressure ที่เปลี่ยนแปลง

```
┌──────────────────────────────────────┐
│        Pressure Gauge                │
│                                      │
│        0   20  40  60  80 100        │
│        ╭──┬──┬──┬──┬──┬──╮           │
│       ╱GREEN│YELLOW│ RED  ╲          │
│      │ ║║║║ │║║║║║ │║║║║║  │         │
│      │      │  ╱   │       │         │
│      │      │ ●────│       │         │
│       ╲ ║║║║│║║║║║ │║║║║  ╱          │
│        ╰──┴──┴──┴──┴──┴──╯           │
│                                      │
│           45 PSI                     │
│           Warning                    │
└──────────────────────────────────────┘
```

**Hints:**

* total\_tick\_count = 51 (0 ถึง 100, ทุก 2 PSI)
* major\_tick\_every = 5 (ทุก 10 PSI)
* ใช้ pattern จาก Section 4.3 สำหรับ needle calculation

***

#### Exercise 2: Combined Temperature + Humidity Display with Dual Scales

สร้างจอแสดงผล Temperature และ Humidity แบบ dual gauge:

**Requirements:**

* **Gauge 1** (ซ้าย): Temperature Scale
  * Mode: ROUND\_OUTER, size 130x130
  * Range: -10 to 50 degC
  * Sections: Blue (cold), Green (normal), Red (hot)
  * Needle + center value
* **Gauge 2** (ขวา): Humidity Scale
  * Mode: ROUND\_OUTER, size 130x130
  * Range: 0-100 %RH
  * Sections: Red (dry), Green (normal), Blue (humid)
  * Needle + center value
* Timer จำลองค่าที่เปลี่ยนสัมพันธ์กัน (temp สูง → humidity ต่ำ)
* Label ด้านล่างแสดง "Comfort Index" (Good/Fair/Poor)

```
┌──────────────────────────────────────┐
│   Temperature & Humidity Monitor     │
│                                      │
│   ╭──────╮           ╭──────╮        │
│  ╱  25    ╲         ╱  60    ╲       │
│ │  degC    │       │  %RH     │      │
│  ╲  ●──   ╱         ╲  ●──   ╱       │
│   ╰──────╯           ╰──────╯        │
│     TEMP               HUMID         │
│                                      │
│     Comfort Index: Good              │
└──────────────────────────────────────┘
```

**Hints:**

* ใช้ `lv_obj_align()` จัดวางซ้าย-ขวา
* แต่ละ gauge มี needle\_points แยกกัน (static array แยก)
* Comfort Index: Temp 20-26 + Humid 40-60 = "Good"

***

### 7. References

* [LVGL Scale](https://docs.lvgl.io/9.2/widgets/scale.html)
* [lv\_example\_scale\_4.c](https://github.com/lvgl/lvgl/blob/release/v9.2/examples/widgets/scale/lv_example_scale_4.c)

***
