> 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/hardware-interfacing-workshops/scale-gauge.md).

# Scale Gauge

## Lab 10: Real Scale Gauge (Pitch Angle)

### Part 2 - Sensor Visualization

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

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

* **Real Gauge + Real Sensor**: นำ Scale widget จาก Lab 4 (simulation) มาใช้กับข้อมูล Pitch Angle จริงจาก IMU
* **Pitch Angle**: วัดการเอียงหน้า/หลังของบอร์ด ซึ่งใช้งานได้จริงในหลายแอปพลิเคชัน เช่น ระบบเตือนความลาดชัน
* **Complementary Filter**: เรียนรู้อัลกอริทึมที่รวม Accelerometer + Gyroscope เพื่อผลลัพธ์ที่แม่นยำ
* **Scale Widget Mastery**: เข้าใจการใช้ lv\_scale แบบ ROUND\_OUTER กับ needle line, color sections, custom labels
* **Roll vs Pitch**: เข้าใจความแตกต่างระหว่าง Roll (Lab 9) กับ Pitch (Lab 10)

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

1. **Tilt API (Pitch)**: ใช้ `aic_tilt_get_pitch()` และ `aic_tilt_get_pitch_percent()` อ่านมุมเอียงหน้า/หลัง
2. **Scale Widget**: สร้าง round gauge ด้วย ticks, labels, sections, needle
3. **Color Sections**: กำหนดโซนสี (75-100 = แดง) สำหรับ warning zone
4. **Needle Positioning**: ใช้ `lv_scale_set_line_needle_value()` ขยับเข็มตามค่า sensor
5. **Custom Labels**: แสดง "0", "25", "50", "75", "100" แทน default labels
6. **Style Layers**: จัดการ 3 layers ของ scale: MAIN (arc), INDICATOR (major ticks), ITEMS (minor ticks)

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

1. Initialize hardware ด้วย `aic_sensors_init()` + `aic_tilt_init(NULL)`
2. สร้าง Scale widget ในโหมด `LV_SCALE_MODE_ROUND_OUTER` ขนาด 200x200
3. กำหนด 21 ticks, major ทุก 5 ticks, range 0-100
4. เพิ่ม color section สำหรับ high zone (75-100 = สีแดง)
5. สร้าง needle line ด้วย `lv_line_create()` บน scale
6. Timer callback (100ms) เรียก `aic_tilt_update_from_imu()` แล้วอัพเดท needle + labels

<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 Pitch Angle Measurement

```
┌─────────────────────────────────────────────────────────────┐
│              PITCH ANGLE (Forward-Backward Tilt)            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Side View of Board:                                       │
│                                                             │
│       Pitch = 0°            Pitch = +45°                    │
│       (Level)              (Tilt Forward)                   │
│                                                             │
│       ─────────              ╱────────                      │
│       │ PSOC  │            ╱  PSOC  │                       │
│       │ EDGE  │          ╱    EDGE  │                       │
│       ─────────        ╱──────────                          │
│                                                             │
│   Formula: pitch = atan2(ax, az) × 180/π                    │
│                                                             │
│   Range:                                                    │
│     -90° (tilt backward) to +90° (tilt forward)             │
│     0° = level                                              │
│                                                             │
│   Note: Pitch range is smaller than Roll because            │
│         at ±90° the Z-axis becomes horizontal               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 Scale Widget Structure

```
┌─────────────────────────────────────────────────────────────┐
│                 SCALE WIDGET STRUCTURE                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                      LV_PART_MAIN                           │
│                   (Arc background)                          │
│                         ╱╲                                  │
│                        ╱  ╲                                 │
│                       ╱    ╲                                │
│               100    ╱      ╲    0                          │
│                ┃    │        │    ┃   LV_PART_INDICATOR     │
│             75 ┃    │   50   │    ┃ 25  (Major ticks)       │
│                ┃    │   ▲    │    ┃                         │
│                 ╲   │ NEEDLE │   ╱    LV_PART_ITEMS         │
│                  ╲  │        │  ╱      (Minor ticks)        │
│                   ╲ │        │ ╱                            │
│                    ╲│        │╱                             │
│                                                             │
│   Sections:                                                 │
│     Blue: 0-75 (Normal zone)                                │
│     Red: 75-100 (High alert zone)                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.3 UI Layout

```
┌─────────────────────────────────────────────────────────────┐
│       "Week 4 Ex10: Scale Gauge (Pitch Angle)"              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                   100    75                                 │
│                    ╲     │     ╱ 25                         │
│                     ╲    │    ╱                             │
│                      ╲   │50 ╱                              │
│                       ╲  │  ╱                               │
│                        ╲ ▲ ╱      ← Needle (orange)         │
│                         ─┴─                                 │
│                          0                                  │
│                                                             │
│                        "50"  ← Value Label                  │
│                                                             │
│                 Pitch: 0.0 deg ← Raw Angle Label            │
│                                                             │
│     "[Part II] Complementary Filter (Accel + Gyro)"         │
│     "Tilt board forward/backward to move the needle"        │
└─────────────────────────────────────────────────────────────┘
```

***

### 3. ฟังก์ชันสำคัญ

#### 3.1 Tilt API Functions

<table><thead><tr><th width="278.26141357421875">Function</th><th width="97.48370361328125">Return</th><th>Description</th></tr></thead><tbody><tr><td><code>aic_sensors_init()</code></td><td>void</td><td>Initialize sensor subsystem (เรียกครั้งเดียว)</td></tr><tr><td><code>aic_tilt_init(NULL)</code></td><td>bool</td><td>Initialize Complementary Filter (defaults: alpha=0.98, dt=0.1)</td></tr><tr><td><code>aic_tilt_update_from_imu()</code></td><td>bool</td><td>อัพเดท filter จาก BMI270 (true = สำเร็จ)</td></tr><tr><td><code>aic_tilt_get_pitch()</code></td><td>float</td><td>Pitch angle in degrees (-90 to +90)</td></tr><tr><td><code>aic_tilt_get_pitch_percent()</code></td><td>uint8_t</td><td>Pitch as percentage (0-100%)</td></tr><tr><td><code>aic_tilt_get_roll()</code></td><td>float</td><td>Roll angle in degrees (-180 to +180)</td></tr><tr><td><code>aic_tilt_get_roll_percent()</code></td><td>uint8_t</td><td>Roll as percentage (0-100%)</td></tr><tr><td><code>aic_tilt_reset()</code></td><td>void</td><td>Reset filter state to zero</td></tr><tr><td><code>aic_tilt_set_alpha(float)</code></td><td>void</td><td>เปลี่ยน filter coefficient (0.0-1.0)</td></tr></tbody></table>

#### 3.2 Scale Widget APIs

<table><thead><tr><th width="480.34234619140625">Function</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_scale_create(parent)</code></td><td>สร้าง Scale widget</td></tr><tr><td><code>lv_scale_set_mode(scale, LV_SCALE_MODE_ROUND_OUTER)</code></td><td>ตั้ง mode เป็น round gauge</td></tr><tr><td><code>lv_scale_set_total_tick_count(scale, 21)</code></td><td>จำนวน tick ทั้งหมด</td></tr><tr><td><code>lv_scale_set_major_tick_every(scale, 5)</code></td><td>Major tick ทุก N ticks</td></tr><tr><td><code>lv_scale_set_range(scale, 0, 100)</code></td><td>กำหนดช่วงค่า min-max</td></tr><tr><td><code>lv_scale_set_label_show(scale, true)</code></td><td>แสดง labels</td></tr><tr><td><code>lv_scale_set_text_src(scale, labels)</code></td><td>ใช้ custom labels</td></tr><tr><td><code>lv_scale_set_line_needle_value(scale, line, len, val)</code></td><td>ตั้งตำแหน่งเข็ม</td></tr><tr><td><code>lv_scale_add_section(scale)</code></td><td>เพิ่ม color section</td></tr><tr><td><code>lv_scale_section_set_range(section, min, max)</code></td><td>กำหนดช่วงของ section</td></tr><tr><td><code>lv_scale_section_set_style(section, part, style)</code></td><td>ตั้ง style ของ section</td></tr></tbody></table>

#### 3.3 Pitch Angle to Percentage Mapping

```c
/* Pitch: -90 to +90 degrees -> 0% to 100% */
/*   -90 deg = 0%   (tilted backward fully)  */
/*     0 deg = 50%  (level)                   */
/*   +90 deg = 100% (tilted forward fully)    */

/* Built-in API: */
uint8_t pct = aic_tilt_get_pitch_percent();

/* Manual calculation (for reference): */
float pitch = aic_tilt_get_pitch();  /* -90 to +90 */
uint8_t pct = (uint8_t)((pitch + 90.0f) * (100.0f / 180.0f));
```

#### 3.4 Include Files

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

/* Sensor & Tilt API headers (hardware only) */
#ifdef CYBSP_ENABLED
#include "aic-eec/aic-eec.h"
#include "aic-eec/sensors.h"
#include "aic-eec/tilt.h"
#include "../../shared/imu_shared.h"
#endif
```

***

### 4. โค้ดตัวอย่าง

#### 4.1 Global Variables

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

/* Sensor & Tilt API headers */
#ifdef CYBSP_ENABLED
#include "aic-eec/aic-eec.h"
#include "aic-eec/sensors.h"
#include "aic-eec/tilt.h"
#include "../../shared/imu_shared.h"
#endif

/* Scale gauge widgets */
static lv_obj_t * ex10_scale;
static lv_obj_t * ex10_value_label;
static lv_obj_t * ex10_raw_label;      /* Shows pitch angle in degrees */
static lv_obj_t * ex10_needle_line;
static lv_timer_t * ex10_timer;

static int32_t ex10_needle_length = 85; /* Needle length - almost reaching labels */
```

#### 4.2 Timer Callback - อ่านค่า Pitch จาก Complementary Filter

```c
static void ex10_timer_cb(lv_timer_t * timer)
{
    (void)timer;

    float pitch;
    uint8_t pct;

#ifdef CYBSP_ENABLED
    /* ===== HARDWARE: Update tilt from BMI270 ===== */
    if(!aic_tilt_update_from_imu()) {
        return;  /* IMU not ready - skip this cycle */
    }

    /* Get pitch angle and percentage */
    pitch = aic_tilt_get_pitch();          /* -90 to +90 degrees */
    pct   = aic_tilt_get_pitch_percent();  /* 0 to 100% */
#else
    /* ===== PC SIMULATOR: Simulated pitch ===== */
    static float t = 0.0f;
    t += 0.03f;
    pitch = 45.0f * sinf(t);              /* -45 to +45 degrees */
    pct   = (uint8_t)((pitch + 90.0f) * (100.0f / 180.0f));
#endif

    /* ===== Update value label (center of gauge) ===== */
    lv_label_set_text_fmt(ex10_value_label, "%d", (int)pct);

    /* ===== Update raw pitch angle label ===== */
    lv_label_set_text_fmt(ex10_raw_label, "Pitch: %.1f deg", (double)pitch);

    /* ===== Update needle position ===== */
    lv_scale_set_line_needle_value(ex10_scale, ex10_needle_line,
                                    ex10_needle_length, pct);
}
```

#### 4.3 Main Function - สร้าง UI ทั้งหมด

```c
void part2_ex10_real_scale_gauge(void)
{
    /* ===== HARDWARE INITIALIZATION ===== */
#ifdef CYBSP_ENABLED
    aic_sensors_init();
    aic_tilt_init(NULL);  /* Initialize Complementary Filter with defaults */
#endif

    /* ===== 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 2 Ex10: Scale Gauge (Pitch Angle)");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);

    /*=========================================================================
     * Create Scale - Following lv_example_scale_4 pattern
     *=========================================================================*/
    ex10_scale = lv_scale_create(lv_screen_active());
    lv_obj_set_size(ex10_scale, 200, 200);
    lv_scale_set_label_show(ex10_scale, true);
    lv_scale_set_mode(ex10_scale, LV_SCALE_MODE_ROUND_OUTER);
    lv_obj_align(ex10_scale, LV_ALIGN_CENTER, 0, 0);

    /* Range: 0-100 */
    lv_scale_set_total_tick_count(ex10_scale, 21);
    lv_scale_set_major_tick_every(ex10_scale, 5);

    /* Tick length styling */
    lv_obj_set_style_length(ex10_scale, 5, LV_PART_ITEMS);      /* Minor tick */
    lv_obj_set_style_length(ex10_scale, 10, LV_PART_INDICATOR); /* Major tick */
    lv_scale_set_range(ex10_scale, 0, 100);

    /* Custom labels for gauge */
    static const char * custom_labels[] = {"0", "25", "50", "75", "100", NULL};
    lv_scale_set_text_src(ex10_scale, custom_labels);

    /*=========================================================================
     * Indicator style (Major ticks) - Blue color
     *=========================================================================*/
    static lv_style_t ex10_indicator_style;
    lv_style_init(&ex10_indicator_style);

    /* Label style properties */
    lv_style_set_text_font(&ex10_indicator_style, LV_FONT_DEFAULT);
    lv_style_set_text_color(&ex10_indicator_style,
                             lv_palette_darken(LV_PALETTE_BLUE, 3));

    /* Major tick properties */
    lv_style_set_line_color(&ex10_indicator_style,
                             lv_palette_darken(LV_PALETTE_BLUE, 3));
    lv_style_set_width(&ex10_indicator_style, 10);       /* Tick length */
    lv_style_set_line_width(&ex10_indicator_style, 2);   /* Tick width */
    lv_obj_add_style(ex10_scale, &ex10_indicator_style, LV_PART_INDICATOR);

    /*=========================================================================
     * Minor ticks style - Lighter blue
     *=========================================================================*/
    static lv_style_t ex10_minor_ticks_style;
    lv_style_init(&ex10_minor_ticks_style);
    lv_style_set_line_color(&ex10_minor_ticks_style,
                             lv_palette_lighten(LV_PALETTE_BLUE, 2));
    lv_style_set_width(&ex10_minor_ticks_style, 5);      /* Tick length */
    lv_style_set_line_width(&ex10_minor_ticks_style, 2); /* Tick width */
    lv_obj_add_style(ex10_scale, &ex10_minor_ticks_style, LV_PART_ITEMS);

    /*=========================================================================
     * Main arc/line style
     *=========================================================================*/
    static lv_style_t ex10_main_line_style;
    lv_style_init(&ex10_main_line_style);
    lv_style_set_arc_color(&ex10_main_line_style,
                            lv_palette_darken(LV_PALETTE_BLUE, 3));
    lv_style_set_arc_width(&ex10_main_line_style, 3);
    lv_obj_add_style(ex10_scale, &ex10_main_line_style, LV_PART_MAIN);

    /*=========================================================================
     * Section: High zone (75-100) - Red color
     *=========================================================================*/
    lv_scale_section_t * high_section = lv_scale_add_section(ex10_scale);
    lv_scale_section_set_range(high_section, 75, 100);

    static lv_style_t ex10_high_section_main_style;
    lv_style_init(&ex10_high_section_main_style);
    lv_style_set_arc_color(&ex10_high_section_main_style,
                            lv_palette_main(LV_PALETTE_RED));
    lv_style_set_arc_width(&ex10_high_section_main_style, 3);
    lv_scale_section_set_style(high_section, LV_PART_MAIN,
                                &ex10_high_section_main_style);

    static lv_style_t ex10_high_section_indicator_style;
    lv_style_init(&ex10_high_section_indicator_style);
    lv_style_set_line_color(&ex10_high_section_indicator_style,
                             lv_palette_darken(LV_PALETTE_RED, 3));
    lv_style_set_text_color(&ex10_high_section_indicator_style,
                             lv_palette_darken(LV_PALETTE_RED, 3));
    lv_scale_section_set_style(high_section, LV_PART_INDICATOR,
                                &ex10_high_section_indicator_style);

    static lv_style_t ex10_high_section_items_style;
    lv_style_init(&ex10_high_section_items_style);
    lv_style_set_line_color(&ex10_high_section_items_style,
                             lv_palette_main(LV_PALETTE_RED));
    lv_scale_section_set_style(high_section, LV_PART_ITEMS,
                                &ex10_high_section_items_style);

    /*=========================================================================
     * Needle Line - Shows current value position
     *=========================================================================*/
    ex10_needle_line = lv_line_create(ex10_scale);

    /* Needle style - Orange color for visibility */
    static lv_style_t ex10_needle_style;
    lv_style_init(&ex10_needle_style);
    lv_style_set_line_width(&ex10_needle_style, 2);  /* Thin needle */
    lv_style_set_line_color(&ex10_needle_style,
                             lv_palette_main(LV_PALETTE_ORANGE));
    lv_style_set_line_rounded(&ex10_needle_style, true);
    lv_obj_add_style(ex10_needle_line, &ex10_needle_style, 0);

    /* Set initial needle position */
    lv_scale_set_line_needle_value(ex10_scale, ex10_needle_line,
                                    ex10_needle_length, 50);

    /*=========================================================================
     * Value label (center of gauge)
     *=========================================================================*/
    ex10_value_label = lv_label_create(lv_screen_active());
    lv_label_set_text(ex10_value_label, "50");
    lv_obj_set_style_text_color(ex10_value_label,
                                 lv_color_hex(0xFFFFFF), 0);
    lv_obj_set_style_text_font(ex10_value_label,
                                &lv_font_montserrat_24, 0);
    lv_obj_align(ex10_value_label, LV_ALIGN_CENTER, 0, 0);

    /*=========================================================================
     * Pitch angle label - shows actual degrees
     *=========================================================================*/
    ex10_raw_label = lv_label_create(lv_screen_active());
    lv_label_set_text(ex10_raw_label, "Pitch: 0.0 deg");
    lv_obj_set_style_text_color(ex10_raw_label,
                                 lv_color_hex(0x00FF00), 0);
    lv_obj_align(ex10_raw_label, LV_ALIGN_CENTER, 0, 120);

    /*=========================================================================
     * Description labels
     *=========================================================================*/
    lv_obj_t * desc = lv_label_create(lv_screen_active());
    lv_label_set_text(desc, "[Part II] Complementary Filter (Accel + Gyro)\n"
                           "Tilt board forward/backward to move the needle");
    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, -25);

    /*=========================================================================
     * Footer
     *=========================================================================*/
    aic_create_footer(lv_screen_active());

    /*=========================================================================
     * Timer for Tilt Analysis updates (100ms = 10 Hz)
     *=========================================================================*/
    ex10_timer = lv_timer_create(ex10_timer_cb, 100, NULL);

    printf("[Part2] Ex10: Scale Gauge (Pitch Angle) "
           "with Complementary Filter\r\n");
}
```

***

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

#### 5.1 Scale Style Layers - 3 ชั้นของ Style

Scale widget มี 3 ชั้นที่ต้องกำหนด style แยกกัน:

<table><thead><tr><th width="84.21588134765625">Layer</th><th width="187.95098876953125">Part</th><th>ควบคุม</th><th>Properties</th></tr></thead><tbody><tr><td>1</td><td><code>LV_PART_MAIN</code></td><td>เส้น arc หลัก</td><td><code>arc_color</code>, <code>arc_width</code></td></tr><tr><td>2</td><td><code>LV_PART_INDICATOR</code></td><td>Major ticks + Labels</td><td><code>line_color</code>, <code>text_color</code>, <code>width</code>, <code>line_width</code></td></tr><tr><td>3</td><td><code>LV_PART_ITEMS</code></td><td>Minor ticks</td><td><code>line_color</code>, <code>width</code>, <code>line_width</code></td></tr></tbody></table>

```c
/* ตัวอย่างย่อ: ตั้ง style ทั้ง 3 layers */
static lv_style_t main_s, ind_s, items_s;

lv_style_init(&main_s);
lv_style_set_arc_color(&main_s, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_arc_width(&main_s, 3);
lv_obj_add_style(scale, &main_s, LV_PART_MAIN);

lv_style_init(&ind_s);
lv_style_set_line_color(&ind_s, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_text_color(&ind_s, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_width(&ind_s, 10);        /* Major tick length */
lv_style_set_line_width(&ind_s, 2);    /* Major tick thickness */
lv_obj_add_style(scale, &ind_s, LV_PART_INDICATOR);

lv_style_init(&items_s);
lv_style_set_line_color(&items_s, lv_palette_lighten(LV_PALETTE_BLUE, 2));
lv_style_set_width(&items_s, 5);       /* Minor tick length */
lv_obj_add_style(scale, &items_s, LV_PART_ITEMS);
```

#### 5.2 Section Coloring - เพิ่มโซนสี

```c
/* Section เปลี่ยนสีของ arc, ticks, labels ในช่วงที่กำหนด */
/* ต้องตั้ง style ให้ครบทั้ง 3 parts: */
lv_scale_section_t * section = lv_scale_add_section(scale);
lv_scale_section_set_range(section, 75, 100);

lv_scale_section_set_style(section, LV_PART_MAIN,      &red_arc_style);
lv_scale_section_set_style(section, LV_PART_INDICATOR,  &red_tick_style);
lv_scale_section_set_style(section, LV_PART_ITEMS,      &red_minor_style);
/* สามารถเพิ่มหลาย sections: Green(0-25), Yellow(25-75), Red(75-100) */
```

#### 5.3 Needle Line vs Image Needle

```c
/* Line Needle (ใช้ใน Lab นี้) - ง่าย, ไม่ต้องมี image asset */
lv_obj_t * needle = lv_line_create(scale);
lv_scale_set_line_needle_value(scale, needle, 85, 50);
/* ข้อดี: ง่าย, RAM น้อย, ปรับ style ได้ */
/* ข้อจำกัด: เส้นตรงเท่านั้น, ไม่มีหัวลูกศร */

/* Image Needle (Advanced) - สวยกว่าแต่ต้องมี image */
/* lv_scale_set_image_needle_value(scale, img, 50); */
```

#### 5.4 Pitch vs Roll - เมื่อไหร่ใช้อันไหน

<table><thead><tr><th width="151.07745361328125">รายการ</th><th>Pitch (Lab 10)</th><th>Roll (Lab 9)</th></tr></thead><tbody><tr><td><strong>ทิศทาง</strong></td><td>เอียงหน้า/หลัง</td><td>เอียงซ้าย/ขวา</td></tr><tr><td><strong>Range</strong></td><td>-90 to +90 deg</td><td>-180 to +180 deg</td></tr><tr><td><strong>สูตร</strong></td><td>atan2(-ax, sqrt(ay^2+az^2))</td><td>atan2(ay, az)</td></tr><tr><td><strong>Use Case</strong></td><td>Slope, crane arm, nose up/down</td><td>Anti-tip, balance, bank L/R</td></tr><tr><td><strong>Widget</strong></td><td>Scale (lv_scale)</td><td>Arc (lv_arc)</td></tr></tbody></table>

#### 5.5 Custom Labels vs Auto Labels

```c
/* Auto labels: LVGL สร้างตัวเลขอัตโนมัติจาก range */
/* Custom labels: กำหนดเอง (ต้องเป็น static, ปิดด้วย NULL) */
static const char * labels[] = {"0", "25", "50", "75", "100", NULL};
lv_scale_set_text_src(scale, labels);
/* จำนวน labels = จำนวน major ticks (21 ticks / 5 = 5 labels + NULL) */
```

#### 5.6 Complementary Filter Tuning

```c
/* Default: alpha=0.98, dt=0.1 (เหมาะสำหรับส่วนใหญ่) */
aic_tilt_init(NULL);

/* Custom: ปรับ alpha ตามการใช้งาน */
aic_tilt_config_t config = { .alpha = 0.98f, .dt = 0.1f };
aic_tilt_init(&config);
/* alpha สูง (0.99): smooth แต่ drift มากขึ้น */
/* alpha ต่ำ (0.90): noise มากขึ้น แต่ stable กว่า */
```

***

### 6. แบบฝึกหัด

#### Exercise 1: Multi-Zone Scale Gauge

**โจทย์:** เพิ่ม color sections ให้ Scale gauge ครอบคลุมทั้ง 3 โซน:

**Requirements:**

* Low zone (0-25): สีเขียว - บอร์ดเอียงหลังมาก
* Normal zone (25-75): สีน้ำเงิน (default) - บอร์ดอยู่ในช่วงปกติ
* High zone (75-100): สีแดง - บอร์ดเอียงหน้ามาก
* เปลี่ยนสี value label ตาม zone ปัจจุบัน (เขียว/ขาว/แดง)
* เพิ่ม status label: "BACKWARD", "LEVEL", "FORWARD"

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

* Road Grade Warning, Anti-Rollover, Digital Level

**Hint:**

* สร้าง section เพิ่มสำหรับ low zone (0-25)
* ใน timer callback: ตรวจ `pct < 25`, `pct > 75` เพื่อเปลี่ยนสี label
* `lv_obj_set_style_text_color(label, color, 0)` เปลี่ยนสี label

***

#### Exercise 2: Dual Scale Dashboard (Roll + Pitch)

**โจทย์:** สร้าง dashboard แสดง 2 Scale gauges: Roll (ซ้าย) กับ Pitch (ขวา) พร้อมกัน

**Requirements:**

* Scale 2 อัน ขนาด 160x160 วางซ้าย-ขวา
* Scale ซ้าย: Roll angle (สี Cyan)
* Scale ขวา: Pitch angle (สี Orange)
* แต่ละอันมี needle line + value label
* เพิ่ม label กลาง: "Tilt Dashboard"
* Tilt magnitude label ด้านล่าง: "Tilt: XX.X deg"

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

* Flight Instrument: Artificial Horizon
* Crane Safety Monitor: ตรวจสอบความเอียง 2 แกน
* Platform Leveling: ปรับระดับแท่นรองรับอุปกรณ์

**Hint:**

* Scale ซ้าย: `lv_obj_align(scale, LV_ALIGN_CENTER, -110, -20)`
* Scale ขวา: `lv_obj_align(scale, LV_ALIGN_CENTER, 110, -20)`
* ขนาด 160x160 (เล็กกว่า 200x200 ของ Lab 10 เดิม)
* Tilt magnitude: `sqrtf(roll*roll + pitch*pitch)`
* ทั้งคู่ใช้ `aic_tilt_update_from_imu()` ครั้งเดียวใน timer

***

### 7. References

* [LVGL Scale](https://docs.lvgl.io/9.2/widgets/scale.html)
* [lv\_example\_scale\_4](https://github.com/lvgl/lvgl/blob/release/v9.2/examples/widgets/scale/lv_example_scale_4.c)
* [Complementary Filter Tutorial](https://www.pieter-jan.com/node/11)

***
