# Arc Gauge

## Lab 9: Real Arc Gauge (Roll Angle)

### Part 2 - Sensor Visualization

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

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

* **Sensor Fusion**: เข้าใจวิธีการรวมข้อมูลจาก sensor 2 ตัว (Accelerometer + Gyroscope) เข้าด้วยกันเพื่อให้ได้ผลลัพธ์ที่ดีกว่าการใช้ sensor ตัวเดียว
* **Complementary Filter**: เรียนรู้ algorithm พื้นฐานที่ใช้จริงในอุตสาหกรรม (drone, robot, motion tracker)
* **Tilt API**: ใช้ module `aic_tilt` ที่ abstract ความซับซ้อนของ filter ให้เรียกใช้ง่าย
* **Arc Gauge + Real Data**: นำ Arc Gauge จาก Lab 2 มาแสดงข้อมูล Roll Angle จริงจาก IMU

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

1. **Complementary Filter**: หลักการรวม Accel (ช้าแต่ stable) กับ Gyro (เร็วแต่ drift)
2. **Tilt API**: ใช้ `aic_tilt_init()`, `aic_tilt_update_from_imu()`, `aic_tilt_get_roll()`, `aic_tilt_get_roll_percent()`
3. **Arc Gauge Integration**: แสดงมุมเอียง (Roll Angle) บน Arc widget แบบ real-time
4. **Percentage Mapping**: แปลงมุม (-90 to +90) เป็นเปอร์เซ็นต์ (0-100%) สำหรับ widget
5. **Hardware Init Pattern**: `aic_sensors_init()` + `aic_tilt_init(NULL)` ก่อนสร้าง UI

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

1. เรียก `aic_sensors_init()` เพื่อ initialize BMI270 IMU
2. เรียก `aic_tilt_init(NULL)` เพื่อ initialize Complementary Filter (ค่า default: alpha=0.98, dt=0.1s)
3. สร้าง Arc Gauge UI เหมือน Lab 2 (part2\_ex2\_arc\_gauge)
4. ใน timer callback (100ms): เรียก `aic_tilt_update_from_imu()` แล้วอ่าน `aic_tilt_get_roll()` + `aic_tilt_get_roll_percent()`
5. อัพเดท Arc value + labels ด้วยค่ามุมจริง

<figure><img src="/files/4H6WLe3wtPkZGOTSPt6j" alt=""><figcaption></figcaption></figure>

***

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

#### 2.1 Why Complementary Filter?

```
┌─────────────────────────────────────────────────────────────┐
│         SENSOR CHARACTERISTICS                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ACCELEROMETER:                                            │
│   ┌─────────────────────────────────────────┐               │
│   │  + Measures gravity direction           │               │
│   │  + Stable over time (no drift)          │               │
│   │  - Noisy (vibration sensitive)          │               │
│   │  - Slow response                        │               │
│   │  = Good for LOW frequency               │               │
│   └─────────────────────────────────────────┘               │
│                                                             │
│   GYROSCOPE:                                                │
│   ┌─────────────────────────────────────────┐               │
│   │  + Fast response                        │               │
│   │  + Smooth readings                      │               │
│   │  - Drifts over time (integration error) │               │
│   │  = Good for HIGH frequency              │               │
│   └─────────────────────────────────────────┘               │
│                                                             │
│   COMPLEMENTARY FILTER = Best of both!                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 Complementary Filter Formula

```
┌─────────────────────────────────────────────────────────────┐
│         COMPLEMENTARY FILTER                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   angle = α × (angle + gyro × dt) + (1-α) × accel_angle     │
│                                                             │
│   where:                                                    │
│     α = 0.98 (trust gyro 98%)                               │
│     dt = time delta (e.g., 0.01 sec at 100Hz)               │
│     gyro = angular velocity (rad/s → deg/s)                 │
│     accel_angle = atan2(ay, az) for Roll                    │
│                 = atan2(ax, az) for Pitch                   │
│                                                             │
│   ┌───────────────────────────────────────────────────────┐ │
│   │   HIGH PASS FILTER        LOW PASS FILTER             │ │
│   │   (Gyro: fast changes)    (Accel: slow/stable)        │ │
│   │                                                       │ │
│   │   0.98 × gyro_angle   +   0.02 × accel_angle          │ │
│   │        ↓                       ↓                      │ │
│   │   Quick response         Drift correction             │ │
│   └───────────────────────────────────────────────────────┘ │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.3 Roll Angle Measurement

```
┌─────────────────────────────────────────────────────────────┐
│              ROLL ANGLE (Left-Right Tilt)                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Top View of Board:                                        │
│                                                             │
│         Roll = 0°              Roll = +45°                  │
│       (Level)                (Tilt Right)                   │
│                                                             │
│       ┌─────────┐              ┌─────────┐                  │
│       │  PSOC   │              │  PSOC  ╲│                  │
│       │  EDGE   │              │  EDGE   ╲                  │
│       │  E84    │              │  E84    │                  │
│       └─────────┘              └─────────╱                  │
│                                         ╱                   │
│                                                             │
│   Formula: roll = atan2(ay, az) × 180/π                     │
│                                                             │
│   Range:                                                    │
│     -180° (tilt far left) to +180° (tilt far right)         │
│     0° = level                                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

***

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

#### 3.1 Tilt API Functions

<table><thead><tr><th width="261.890625">Function</th><th width="97.53125">Return</th><th>Description</th></tr></thead><tbody><tr><td><code>aic_tilt_init(config)</code></td><td>bool</td><td>Initialize Complementary Filter (NULL = defaults)</td></tr><tr><td><code>aic_tilt_update_from_imu()</code></td><td>bool</td><td>อ่าน Accel+Gyro แล้วอัพเดท filter (เรียกทุก 100ms)</td></tr><tr><td><code>aic_tilt_get_roll()</code></td><td>float</td><td>Roll angle (degrees, -180 to +180)</td></tr><tr><td><code>aic_tilt_get_pitch()</code></td><td>float</td><td>Pitch angle (degrees, -90 to +90)</td></tr><tr><td><code>aic_tilt_get_roll_percent()</code></td><td>uint8_t</td><td>Roll mapped to 0-100%</td></tr><tr><td><code>aic_tilt_get_pitch_percent()</code></td><td>uint8_t</td><td>Pitch mapped to 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(alpha)</code></td><td>void</td><td>ตั้งค่า filter coefficient (0.0-1.0)</td></tr><tr><td><code>aic_tilt_set_dt(dt)</code></td><td>void</td><td>ตั้งค่า sample period (seconds)</td></tr></tbody></table>

#### 3.2 Tilt Init API Signature

```c
/**
 * @brief Initialize the tilt analysis module
 *
 * @param config  Pointer to configuration (NULL for defaults):
 *                - alpha = 0.98 (98% gyro, 2% accel)
 *                - dt = 0.1s (100ms sample period)
 * @return true if successful
 */
bool aic_tilt_init(const aic_tilt_config_t *config);

/* Configuration structure */
typedef struct {
    float alpha;   /* Complementary filter coefficient (0.0-1.0) */
    float dt;      /* Sample period in seconds */
} aic_tilt_config_t;
```

#### 3.3 Tilt Update API Signature

```c
/**
 * @brief Convenience function: Update from IMU shared memory
 *
 * Reads accelerometer AND gyroscope from the sensors module,
 * then applies Complementary Filter to estimate tilt angles.
 *
 * Internally calls:
 *   aic_imu_read_accel() + aic_imu_read_gyro() + aic_tilt_update()
 *
 * @return true if update successful (both accel and gyro available)
 */
bool aic_tilt_update_from_imu(void);
```

#### 3.4 Key Differences: Lab 2 (Sim) vs Lab 9 (HW)

<table><thead><tr><th width="150.7578125">รายการ</th><th width="263.852294921875">Lab 2 (Simulator)</th><th>Lab 9 (Hardware)</th></tr></thead><tbody><tr><td>Init</td><td>ไม่ต้อง</td><td><code>aic_sensors_init()</code> + <code>aic_tilt_init(NULL)</code></td></tr><tr><td>Data Source</td><td>Counter (0-100 loop)</td><td>Complementary Filter (Accel+Gyro)</td></tr><tr><td>Update</td><td><code>value = (value + 1) % 101</code></td><td><code>aic_tilt_update_from_imu()</code></td></tr><tr><td>Value</td><td>Simulated percentage</td><td><code>aic_tilt_get_roll_percent()</code></td></tr><tr><td>Error Check</td><td>ไม่มี</td><td>ตรวจ return false จาก update</td></tr><tr><td>Data Range</td><td>0-100 fixed</td><td>-90 to +90 (mapped to 0-100)</td></tr><tr><td>Behavior</td><td>Linear sweep</td><td>Real physical tilt angle</td></tr></tbody></table>

#### 3.5 Include Files

```c
/* Tilt Analysis module */
#include "../aic-eec/aic-eec.h"       /* aic_sensors_init() */
#include "../aic-eec/sensors.h"        /* sensor type definitions */
#include "../aic-eec/tilt.h"           /* aic_tilt_init(), aic_tilt_get_roll() */
#include "../../shared/imu_shared.h"   /* imu_shared_read_accel/gyro() */
```

***

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

#### 4.1 Global Variables

```c
#include "part2_examples.h"
#include "../aic-eec/aic-eec.h"
#include "../aic-eec/sensors.h"
#include "../aic-eec/tilt.h"      /* Tilt Analysis with Complementary Filter */
#include "../../shared/imu_shared.h"
#include <stdio.h>
#include <math.h>

/* Static variables - same as Ex2 */
static lv_obj_t * ex9_arc;
static lv_obj_t * ex9_value_label;
static lv_obj_t * ex9_raw_label;
static lv_timer_t * ex9_timer;
```

#### 4.2 Timer Callback - Tilt Analysis (Complementary Filter)

```c
/* Timer callback - uses Tilt Analysis (Complementary Filter) */
static void ex9_timer_cb(lv_timer_t * timer)
{
    (void)timer;

    /* Update tilt estimation from IMU (accel + gyro fusion) */
    if(!aic_tilt_update_from_imu()) {
        return;  /* IMU not ready */
    }

    /* Get roll angle and percentage */
    float roll = aic_tilt_get_roll();
    uint8_t pct = aic_tilt_get_roll_percent();

    lv_arc_set_value(ex9_arc, pct);
    lv_label_set_text_fmt(ex9_value_label, "%d%%", (int)pct);
    lv_label_set_text_fmt(ex9_raw_label, "Roll: %.1f deg", (double)roll);
}
```

**ภายใน `aic_tilt_update_from_imu()` จะทำ 3 ขั้นตอน:** อ่าน Accel -> อ่าน Gyro -> คำนวณ Complementary Filter โดยอัตโนมัติ ถ้า sensor ตัวใดตัวหนึ่งไม่พร้อมจะ return false

#### 4.3 Main Function

```c
void part2_ex9_real_arc_gauge(void)
{
    /* ===== HARDWARE INITIALIZATION (Part II addition) ===== */
    aic_sensors_init();
    aic_tilt_init(NULL);  /* Initialize Tilt Analysis with defaults */

    /* ===== UI CODE FROM Ex2 (part2_ex2_arc_gauge) ===== */

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

    /* Title - Updated for Tilt */
    lv_obj_t * title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "Part 2 Ex9: Arc Gauge (Roll Angle)");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);

    /* Create Arc - same as Ex2 */
    ex9_arc = lv_arc_create(lv_screen_active());
    lv_obj_set_size(ex9_arc, 200, 200);
    lv_arc_set_rotation(ex9_arc, 135);
    lv_arc_set_bg_angles(ex9_arc, 0, 270);
    lv_arc_set_range(ex9_arc, 0, 100);
    lv_arc_set_value(ex9_arc, 50);
    lv_obj_center(ex9_arc);

    /* Style the arc - same as Ex2 */
    lv_obj_set_style_arc_width(ex9_arc, 20, LV_PART_MAIN);
    lv_obj_set_style_arc_width(ex9_arc, 20, LV_PART_INDICATOR);
    lv_obj_set_style_arc_color(ex9_arc,
                               lv_palette_main(LV_PALETTE_CYAN),
                               LV_PART_INDICATOR);

    /* Remove knob for read-only display */
    lv_obj_remove_style(ex9_arc, NULL, LV_PART_KNOB);
    lv_obj_remove_flag(ex9_arc, LV_OBJ_FLAG_CLICKABLE);

    /* Value label in center - same as Ex2 */
    ex9_value_label = lv_label_create(lv_screen_active());
    lv_label_set_text(ex9_value_label, "50%");
    lv_obj_set_style_text_color(ex9_value_label, lv_color_hex(0xFFFFFF), 0);
    lv_obj_set_style_text_font(ex9_value_label, &lv_font_montserrat_24, 0);
    lv_obj_center(ex9_value_label);

    /* Roll angle label */
    ex9_raw_label = lv_label_create(lv_screen_active());
    lv_label_set_text(ex9_raw_label, "Roll: 0.0 deg");
    lv_obj_set_style_text_color(ex9_raw_label, lv_color_hex(0x00FF00), 0);
    lv_obj_align(ex9_raw_label, LV_ALIGN_CENTER, 0, 130);

    /* Auto-update timer - uses Tilt Analysis */
    ex9_timer = lv_timer_create(ex9_timer_cb, 100, NULL);

    /* Description - Updated for Tilt */
    lv_obj_t * desc = lv_label_create(lv_screen_active());
    lv_label_set_text(desc, "[Part II] Complementary Filter (Accel + Gyro)\n"
                           "Tilt board left/right to change gauge");
    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, -30);

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

    printf("[Week4] Ex9: Arc Gauge (Roll Angle) with Complementary Filter\r\n");
}
```

***

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

#### 5.1 Complementary Filter - ทำไมต้องใช้?

ปัญหาของการใช้ sensor ตัวเดียว:

```
┌─────────────────────────────────────────────────────────────┐
│   ACCELEROMETER ONLY (atan2 of gravity vector)              │
│                                                             │
│   Problem 1: NOISY (สัญญาณรบกวน)                             │
│                                                             │
│   angle  ~~~~╱╲~~╱╲~~╱╲~~  ← ค่ากระเพื่อมตลอดเวลา              │
│          ───────────────── ← ค่าจริงที่ต้องการ                   │
│                                                             │
│   Problem 2: AFFECTED BY VIBRATION                          │
│   เมื่อบอร์ดสั่น accelerometer อ่านค่า linear acceleration         │
│   ผิดพลาดเพราะไม่ใช่เฉพาะ gravity                              │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│   GYROSCOPE ONLY (integration of angular velocity)          │
│                                                             │
│   Problem: DRIFT (ค่าเลื่อนไปเรื่อยๆ)                            │
│                                                             │
│   angle  ──────────╱                                        │
│          ─────────────── ← ค่าจริงที่ต้องการ                     │
│                   ▲                                         │
│                   │ drift ที่เพิ่มขึ้นเรื่อยๆ                       │
│                                                             │
│   เมื่อ integrate gyro rate: angle += gyro * dt               │
│   noise/bias เล็กๆ สะสมเป็น drift ใหญ่ขึ้นเรื่อยๆ                  │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│   COMPLEMENTARY FILTER (Best of Both Worlds!)               │
│                                                             │
│   angle  ──────────────── ← Smooth + No drift!              │
│          ──────────────── ← ค่าจริง                           │
│                                                             │
│   Gyro: ให้ fast response (98% weight)                       │
│   Accel: ให้ long-term stability, correct drift (2% weight)  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 5.2 Tilt API Usage Pattern

```c
/* Pattern 1: Basic usage (recommended for Lab 9) */
aic_sensors_init();
aic_tilt_init(NULL);  /* Default: alpha=0.98, dt=0.1s */

/* In timer callback (every 100ms): */
if(aic_tilt_update_from_imu()) {
    float roll = aic_tilt_get_roll();          /* -180..+180 deg */
    uint8_t pct = aic_tilt_get_roll_percent(); /* 0..100 */
    /* Update UI */
}
```

```c
/* Pattern 2: Custom configuration */
aic_tilt_config_t config = {
    .alpha = 0.95f,  /* Less gyro weight = more stable but slower */
    .dt = 0.05f      /* 50ms update rate */
};
aic_tilt_init(&config);

/* Pattern 3: Runtime adjustment */
aic_tilt_set_alpha(0.90f);  /* ลด gyro weight */
aic_tilt_set_dt(0.05f);     /* เปลี่ยนเป็น 50ms */
aic_tilt_reset();            /* Reset filter (เมื่อวางบอร์ดใหม่) */
```

#### 5.3 Alpha Parameter - ผลกระทบต่อการตอบสนอง

```c
/* alpha สูง (0.98-0.99): Fast response, อาจ drift เล็กน้อย */
/*   เหมาะสำหรับ: Motion tracking, game controller            */
aic_tilt_set_alpha(0.98f);

/* alpha ปานกลาง (0.90-0.95): Balanced */
/*   เหมาะสำหรับ: General purpose, tilt display               */
aic_tilt_set_alpha(0.93f);

/* alpha ต่ำ (0.80-0.90): Very stable, slow response */
/*   เหมาะสำหรับ: Static measurement, structural monitoring   */
aic_tilt_set_alpha(0.85f);

/* Comparison:
 *   alpha=0.98 → Gyro 98%, Accel 2%  → เร็วแต่อาจ drift
 *   alpha=0.90 → Gyro 90%, Accel 10% → สมดุล
 *   alpha=0.80 → Gyro 80%, Accel 20% → ช้าแต่ stable มาก
 */
```

#### 5.4 Complementary Filter - สูตรคำนวณภายใน (tilt.c)

```c
/* Step 1: Accel angle = atan2(gravity components) */
accel_roll  = atan2f(ay, az) * RAD_TO_DEG;
accel_pitch = atan2f(-ax, sqrtf(ay*ay + az*az)) * RAD_TO_DEG;

/* Step 2: Gyro integration = previous angle + rate * dt */
gyro_roll  = prev_roll  + (gx * RAD_TO_DEG) * dt;
gyro_pitch = prev_pitch + (gy * RAD_TO_DEG) * dt;

/* Step 3: Complementary Filter fusion */
roll  = 0.98f * gyro_roll  + 0.02f * accel_roll;
pitch = 0.98f * gyro_pitch + 0.02f * accel_pitch;

/* Step 4: Map to percentage for UI widget */
/* -90 deg = 0%,  0 deg = 50%,  +90 deg = 100% */
uint8_t pct = (uint8_t)((clamp(roll, -90, 90) + 90.0f) / 180.0f * 100.0f);
```

#### 5.5 Calibration Tips - เมื่อค่าไม่ตรง

```c
/* Tip 1: ตรวจสอบว่า board วางระดับแล้ว roll/pitch ควรใกล้ 0 */
/* ถ้าไม่เป็น 0 แสดงว่า sensor มี offset */

/* Tip 2: Reset filter หลังวางบอร์ดระดับ */
aic_tilt_reset();
/* filter จะ reinitialize จาก accelerometer ในรอบถัดไป */

/* Tip 3: ถ้า drift เยอะ ลอง alpha ต่ำลง */
aic_tilt_set_alpha(0.93f);  /* ให้ accel correct drift มากขึ้น */

/* Tip 4: ตรวจสอบ dt ให้ตรงกับ timer period */
/* ถ้า timer = 50ms แต่ dt = 0.1s → ผลคำนวณจะผิด */
aic_tilt_set_dt(0.05f);  /* ตรงกับ 50ms timer */
```

#### 5.6 Comparison: Tilt API vs Manual Calculation

```c
/* วิธี 1: ใช้ Tilt API (แนะนำ) */
aic_tilt_update_from_imu();
float roll = aic_tilt_get_roll();  /* Easy, correct, handles init/normalize */

/* วิธี 2: Accel only (noisy, no gyro fusion) */
float roll_accel = atan2f(ay, az) * 57.2957795f;

/* วิธี 3: Manual Complementary Filter */
static float roll = 0.0f;
roll = 0.98f * (roll + gx * 57.2957795f * 0.1f) + 0.02f * atan2f(ay, az) * 57.2957795f;
/* Must handle init, normalize, clamp manually */
```

***

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

#### Exercise 1: Dual Arc Gauge (Roll + Pitch)

**โจทย์:** สร้างหน้าจอที่แสดง Arc Gauge 2 วงสำหรับ Roll และ Pitch พร้อมกัน

**Requirements:**

* Arc 2 วง: Roll (ซ้าย, สี Cyan) และ Pitch (ขวา, สี Orange)
* ขนาด 150x150 pixels ต่อวง
* แต่ละวงมี percentage label ตรงกลาง + angle label ใต้วง
* ใช้ `aic_tilt_get_roll_percent()` และ `aic_tilt_get_pitch_percent()`
* เปลี่ยนสี indicator เมื่อเอียงเกิน 45 องศา (สีแดง)
* ใช้ timer 100ms เดียวอัพเดททั้ง 2 วง

**Layout:**

```
┌─────────────────────────────────────────────┐
│     "Dual Tilt Monitor"                     │
│                                             │
│    "Roll"             "Pitch"               │
│   ╭━━━━━╮            ╭━━━━━╮                │
│  ╱ CYAN  ╲          ╱ ORANGE╲               │
│  │  45%   │          │  62%  │              │
│  ╲       ╱          ╲       ╱               │
│   ╰━━━━━╯            ╰━━━━━╯                │
│  "-9.5 deg"         "+21.6 deg"             │
│                                             │
│     "[HW] Complementary Filter"             │
└─────────────────────────────────────────────┘
```

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

* **Drone Flight Controller**: แสดงมุม Roll (เอียงซ้าย-ขวา) และ Pitch (เอียงหน้า-หลัง) ของ quadcopter เพื่อให้ pilot เห็น attitude ของ drone แบบ real-time
* **เครน/รถยก (Crane/Forklift)**: ระบบ Anti-Tipping ที่แจ้งเตือนเมื่อรถเอียงเกินมุมปลอดภัย ป้องกันการพลิกคว่ำขณะยกของหนัก
* **Marine Vessel Monitoring**: แสดง Roll/Pitch ของเรือเพื่อตรวจสอบเสถียรภาพ

**Hint:**

* วาง Arc ด้วย `LV_ALIGN_CENTER` offset X = -90 (Roll) และ +90 (Pitch)
* เปลี่ยนสี: `lv_obj_set_style_arc_color(arc, color, LV_PART_INDICATOR)`
* ตรวจมุมเกิน: `if(fabsf(roll) > 45.0f) { /* change to red */ }`

***

#### Exercise 2: Tilt Alarm with History Chart

**โจทย์:** สร้างระบบตรวจจับมุมเอียงเกินกำหนดพร้อม Line Chart แสดงประวัติ:

**Requirements:**

* Arc Gauge แสดง Roll angle ปัจจุบัน (เหมือน Lab 9)
* Line Chart (350x120) ใต้ Arc แสดง history 50 จุดของ Roll angle
* ระบบเตือน 3 ระดับ:
  * Safe: |roll| < 15 deg (สีเขียว)
  * Warning: |roll| 15-30 deg (สีเหลือง)
  * Danger: |roll| > 30 deg (สีแดง + label กระพริบ)
* แสดง alarm count, max tilt angle ที่เคยวัดได้
* ปุ่ม "RESET" สำหรับ clear alarm count + tilt history

**Layout:**

```
┌─────────────────────────────────────────────┐
│     "Tilt Alarm Monitor"                    │
│                                             │
│        ╭━━━━━╮     Status: SAFE (green)     │
│       ╱       ╲    Alarms: 3                │
│       │  45%   │    Max: 28.5 deg           │
│       ╲       ╱                             │
│        ╰━━━━━╯                              │
│     "Roll: -5.2 deg"                        │
│                                             │
│   ┌─────────────────────────────────────┐   │
│   │    CHART: Roll History (50 pts)     │   │
│   │   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~      │   │
│   │   ---  15 deg threshold ---         │   │
│   └─────────────────────────────────────┘   │
│                              [RESET]        │
└─────────────────────────────────────────────┘
```

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

* **Structural Health Monitoring**: ติดตั้งบนเสา/อาคาร ตรวจจับความเอียงและแจ้งเตือนเมื่อเกินเกณฑ์ปลอดภัย เก็บข้อมูลย้อนหลังเพื่อวิเคราะห์แนวโน้ม
* **Industrial Equipment Leveling**: ตรวจสอบว่าเครื่องจักร CNC หรือเครื่องพิมพ์ 3D อยู่ระดับหรือไม่ แจ้งเตือนเมื่อพื้นทรุดตัว
* **Vehicle Roll-over Prevention**: ระบบ ESP/ESC ในรถยนต์ที่ตรวจจับมุมเอียงเพื่อป้องกันรถพลิกคว่ำ

**Hint:**

* Chart range: `-90` to `+90` (raw degrees, not percentage)
* `lv_chart_set_next_value(chart, ser, (int32_t)roll)`
* กระพริบ label: toggle visibility ใน timer `lv_obj_add_flag()/lv_obj_remove_flag(LV_OBJ_FLAG_HIDDEN)`
* Peak tracking: `if(fabsf(roll) > max_tilt) max_tilt = fabsf(roll);`

***

### 7. References

* [Complementary Filter Explained](https://www.pieter-jan.com/node/11)
* [LVGL Arc](https://docs.lvgl.io/9.2/widgets/arc.html)
* [BMI270 IMU Datasheet](https://www.bosch-sensortec.com/products/motion-sensors/imus/bmi270/)

***


---

# 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/sensor-to-hmi-display/hardware-interfacing-workshops/arc-gauge.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.
