# GPIO Dashboard

## Lab 9: Hardware GPIO Dashboard

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

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

Dashboard ที่รวมทุก GPIO เป็นหัวใจของ Industrial HMI (Human-Machine Interface):

* **Complete Integration**: รวม LED + Button + ADC + PWM ไว้ในหน้าจอเดียว
* **Mixed I/O**: ควบคุม Output (LED) + อ่าน Input (Button, ADC) พร้อมกัน
* **Panel Layout**: จัดหน้าจอแบบ Panel สำหรับ Inputs/Outputs แยกกัน
* **Industrial Application**: คล้าย Control Panel ของเครื่องจักรในโรงงาน
* **Capstone Project**: รวมทุกสิ่งที่เรียนมาจาก Lab 6-8

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

1. **Multi-device Control**: ควบคุม 3 LED + PWM + อ่าน Button + ADC
2. **Panel Layout**: จัด UI เป็น Input Panel (ซ้าย) + Output Panel (ขวา)
3. **Mixed Polling**: Timer callback อ่านทั้ง Button และ ADC พร้อมกัน
4. **Cross-control**: ADC ควบคุม PWM (analog input -> analog output)
5. **Status Indicators**: แสดงสถานะ Hardware ทั้งหมดแบบ Real-time

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

1. Initialize ทุก Hardware: GPIO, PWM, Sensors
2. สร้าง Layout แบบ 2-panel: Input (ซ้าย) + Output (ขวา)
3. สร้าง Timer poll ทุก 50ms อ่าน Button + ADC
4. ใน timer callback อัพเดท UI + ควบคุม Hardware

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

***

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

#### 2.1 Dashboard Layout

```ini
┌─────────────────────────────────────────────────────────────┐
│              Week 3 Ex9: HW GPIO Dashboard                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  [Red LED]    Red    [Switch]    [All ON] [All OFF]         │
│  [Green LED]  Green  [Switch]                               │
│  [Blue LED]   Blue   (POT ctrl)                             │
│                                                             │
│ ┌─────────────────────┐     ┌─────────────────────┐         │
│ │   USER BTN2 (SW4)   │     │   POT -> Blue LED   │         │
│ │                     │     │                     │         │
│ │  ●    Released      │     │  ════════════════   │         │
│ │                     │     │        70%          │         │
│ └─────────────────────┘     └─────────────────────┘         │
│                     (C) 2026 AIC-EEC.com                    │
└─────────────────────────────────────────────────────────────┘

```

#### 2.2 Control Architecture

```ini
┌─────────────────────────────────────────────────────────────┐
│                  CONTROL ARCHITECTURE                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  User Controls (Switches)           Sensor Controls         │
│  ─────────────────────────         ─────────────────────    │
│  ┌────────────┐                    ┌────────────┐           │
│  │ Red Switch │──>aic_gpio_led_set │ Button SW4 │           │
│  │ Green Switch│  (toggle ON/OFF)  │ read state │           │
│  └────────────┘                    └────────────┘           │
│                                    ┌────────────┐           │
│  Batch Controls                    │ POT (ADC)  │           │
│  ─────────────────────────         │ read value │           │
│  ┌────────────┐                    └─────┬──────┘           │
│  │  All ON    │──>loop all LEDs         │                   │
│  │  All OFF   │                         ▼                   │
│  └────────────┘                    ┌────────────┐           │
│                                    │ Blue LED   │           │
│                                    │ PWM dimming│           │
│                                    └────────────┘           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

```

***

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

#### 3.1 Hardware API ทั้งหมดที่ใช้

<table><thead><tr><th width="311.3721923828125">Function</th><th width="266.21728515625">Description</th><th>Category</th></tr></thead><tbody><tr><td><code>aic_gpio_init()</code></td><td>Initialize GPIO</td><td>Setup</td></tr><tr><td><code>aic_gpio_pwm_init(AIC_LED_BLUE)</code></td><td>Initialize PWM</td><td>Setup</td></tr><tr><td><code>aic_sensors_init()</code></td><td>Initialize sensors/ADC</td><td>Setup</td></tr><tr><td><code>aic_gpio_led_set(led, state)</code></td><td>Control LED ON/OFF</td><td>Output</td></tr><tr><td><code>aic_gpio_pwm_set_duty(led, duty)</code></td><td>Set PWM duty 0-100</td><td>Output</td></tr><tr><td><code>aic_gpio_button_read_raw(btn)</code></td><td>Read button (0=pressed)</td><td>Input</td></tr><tr><td><code>aic_adc_read()</code></td><td>Read ADC 0-4095</td><td>Input</td></tr></tbody></table>

#### 3.2 LVGL Layout Functions

<table><thead><tr><th width="439.95172119140625">Function</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW)</code></td><td>จัดเรียงแนวนอน</td></tr><tr><td><code>lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN)</code></td><td>จัดเรียงแนวตั้ง</td></tr><tr><td><code>lv_obj_set_flex_align(obj, main, cross, track)</code></td><td>จัดตำแหน่ง Flex</td></tr><tr><td><code>lv_obj_set_size(obj, w, h)</code></td><td>กำหนดขนาด</td></tr><tr><td><code>LV_PCT(50)</code></td><td>ขนาด 50% ของ parent</td></tr></tbody></table>

#### 3.3 LVGL Widget Functions

| Function                                    | Description           |
| ------------------------------------------- | --------------------- |
| `lv_switch_create(parent)`                  | สร้าง Switch (toggle) |
| `lv_slider_create(parent)`                  | สร้าง Slider          |
| `lv_bar_create(parent)`                     | สร้าง Bar (progress)  |
| `lv_led_create(parent)`                     | สร้าง LED indicator   |
| `lv_button_create(parent)`                  | สร้าง Button          |
| `lv_label_create(parent)`                   | สร้าง Label           |
| `lv_obj_add_state(sw, LV_STATE_CHECKED)`    | ตั้ง Switch เป็น ON   |
| `lv_obj_remove_state(sw, LV_STATE_CHECKED)` | ตั้ง Switch เป็น OFF  |

***

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

#### 4.1 Full Dashboard Code

```c
#include "aic-eec.h"

/* ===== Global Variables: Input Panel ===== */
static lv_obj_t * btn_led_ind;       /* Button status LED */
static lv_obj_t * btn_status_label;  /* Button status text */
static lv_obj_t * adc_bar;           /* ADC progress bar */
static lv_obj_t * adc_percent_label; /* ADC percentage */
static lv_obj_t * adc_voltage_label; /* ADC voltage */

/* ===== Global Variables: Output Panel ===== */
static lv_obj_t * led_inds[3];       /* LED indicators R,G,B */
static lv_obj_t * led_switches[2];   /* Switches for Red, Green */
static lv_obj_t * blue_slider;       /* PWM slider for Blue */
static lv_obj_t * blue_duty_label;   /* Blue duty % */

/* ===== Global Variables: Status Bar ===== */
static lv_obj_t * status_label;

/* ===== Forward Declarations ===== */
static void poll_timer_cb(lv_timer_t * t);
static void red_switch_cb(lv_event_t * e);
static void green_switch_cb(lv_event_t * e);
static void blue_slider_cb(lv_event_t * e);
static void all_on_cb(lv_event_t * e);
static void all_off_cb(lv_event_t * e);

/* ============================================================ */
/*                     CALLBACK FUNCTIONS                       */
/* ============================================================ */

/* Timer callback: poll Button + ADC every 50ms */
static void poll_timer_cb(lv_timer_t * t)
{
    /* === [1] Read Button === */
    int btn_raw = aic_gpio_button_read_raw(AIC_BTN_USER);
    bool btn_pressed = (btn_raw == 0);

    if (btn_pressed) {
        lv_led_on(btn_led_ind);
        lv_label_set_text(btn_status_label, "PRESSED");
        lv_obj_set_style_text_color(btn_status_label,
            lv_palette_main(LV_PALETTE_RED), 0);
    } else {
        lv_led_off(btn_led_ind);
        lv_label_set_text(btn_status_label, "Released");
        lv_obj_set_style_text_color(btn_status_label,
            lv_palette_main(LV_PALETTE_GREEN), 0);
    }

    /* === [2] Read ADC === */
    uint16_t adc_raw = aic_adc_read();
    int percent = (int)((uint32_t)adc_raw * 100 / 4095);
    float voltage = (float)adc_raw * 3.3f / 4095.0f;

    lv_bar_set_value(adc_bar, percent, LV_ANIM_ON);
    lv_label_set_text_fmt(adc_percent_label, "ADC: %d (%d%%)",
                          (int)adc_raw, percent);
    lv_label_set_text_fmt(adc_voltage_label, "%.2f V", voltage);

    /* === [3] Update Status Bar === */
    lv_label_set_text_fmt(status_label,
        "BTN=%s | ADC=%d | Blue=%d%%",
        btn_pressed ? "PRESS" : "REL",
        (int)adc_raw,
        lv_slider_get_value(blue_slider));
}

/* Red LED Switch callback */
static void red_switch_cb(lv_event_t * e)
{
    if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
    lv_obj_t * sw = lv_event_get_target(e);
    bool on = lv_obj_has_state(sw, LV_STATE_CHECKED);
    aic_gpio_led_set(AIC_LED_RED, on);
    on ? lv_led_on(led_inds[0]) : lv_led_off(led_inds[0]);
}

/* Green LED Switch callback */
static void green_switch_cb(lv_event_t * e)
{
    if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
    lv_obj_t * sw = lv_event_get_target(e);
    bool on = lv_obj_has_state(sw, LV_STATE_CHECKED);
    aic_gpio_led_set(AIC_LED_GREEN, on);
    on ? lv_led_on(led_inds[1]) : lv_led_off(led_inds[1]);
}

/* Blue LED PWM Slider callback */
static void blue_slider_cb(lv_event_t * e)
{
    if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
    lv_obj_t * slider = lv_event_get_target(e);
    int duty = lv_slider_get_value(slider);

    aic_gpio_pwm_set_duty(AIC_LED_BLUE, duty);
    lv_led_set_brightness(led_inds[2], (uint8_t)(duty * 255 / 100));
    lv_label_set_text_fmt(blue_duty_label, "%d%%", duty);
}

/* All ON callback */
static void all_on_cb(lv_event_t * e)
{
    if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;

    /* Hardware */
    aic_gpio_led_set(AIC_LED_RED, true);
    aic_gpio_led_set(AIC_LED_GREEN, true);
    aic_gpio_pwm_set_duty(AIC_LED_BLUE, 100);

    /* UI: LEDs */
    lv_led_on(led_inds[0]);
    lv_led_on(led_inds[1]);
    lv_led_set_brightness(led_inds[2], 255);

    /* UI: Sync switches */
    lv_obj_add_state(led_switches[0], LV_STATE_CHECKED);
    lv_obj_add_state(led_switches[1], LV_STATE_CHECKED);
    lv_slider_set_value(blue_slider, 100, LV_ANIM_ON);
    lv_label_set_text(blue_duty_label, "100%");
}

/* All OFF callback */
static void all_off_cb(lv_event_t * e)
{
    if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;

    /* Hardware */
    aic_gpio_led_set(AIC_LED_RED, false);
    aic_gpio_led_set(AIC_LED_GREEN, false);
    aic_gpio_pwm_set_duty(AIC_LED_BLUE, 0);

    /* UI: LEDs */
    lv_led_off(led_inds[0]);
    lv_led_off(led_inds[1]);
    lv_led_set_brightness(led_inds[2], 0);

    /* UI: Sync switches */
    lv_obj_remove_state(led_switches[0], LV_STATE_CHECKED);
    lv_obj_remove_state(led_switches[1], LV_STATE_CHECKED);
    lv_slider_set_value(blue_slider, 0, LV_ANIM_ON);
    lv_label_set_text(blue_duty_label, "0%");
}

/* ============================================================ */
/*                  HELPER: Create Input Panel                  */
/* ============================================================ */
static lv_obj_t * create_input_panel(lv_obj_t * parent)
{
    lv_obj_t * panel = lv_obj_create(parent);
    lv_obj_set_size(panel, LV_PCT(48), LV_PCT(100));
    lv_obj_set_style_bg_color(panel, lv_color_hex(0x0f3460), 0);
    lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_all(panel, 8, 0);
    lv_obj_set_style_pad_row(panel, 6, 0);
    lv_obj_remove_flag(panel, LV_OBJ_FLAG_SCROLLABLE);

    /* Panel Title */
    lv_obj_t * lbl = lv_label_create(panel);
    lv_label_set_text(lbl, "-- INPUT --");
    lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFAA00), 0);

    /* Button Section */
    lv_obj_t * btn_lbl = lv_label_create(panel);
    lv_label_set_text(btn_lbl, "USER Button (SW2)");
    lv_obj_set_style_text_color(btn_lbl, lv_color_hex(0xCCCCCC), 0);

    lv_obj_t * btn_row = lv_obj_create(panel);
    lv_obj_set_size(btn_row, LV_PCT(100), 35);
    lv_obj_set_flex_flow(btn_row, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(btn_row, LV_FLEX_ALIGN_START,
                          LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_bg_opa(btn_row, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(btn_row, 0, 0);
    lv_obj_set_style_pad_all(btn_row, 0, 0);
    lv_obj_set_style_pad_column(btn_row, 10, 0);
    lv_obj_remove_flag(btn_row, LV_OBJ_FLAG_SCROLLABLE);

    btn_led_ind = lv_led_create(btn_row);
    lv_led_set_color(btn_led_ind, lv_palette_main(LV_PALETTE_CYAN));
    lv_obj_set_size(btn_led_ind, 25, 25);
    lv_led_off(btn_led_ind);

    btn_status_label = lv_label_create(btn_row);
    lv_label_set_text(btn_status_label, "Released");
    lv_obj_set_style_text_color(btn_status_label,
        lv_palette_main(LV_PALETTE_GREEN), 0);

    /* ADC Section */
    lv_obj_t * adc_lbl = lv_label_create(panel);
    lv_label_set_text(adc_lbl, "Potentiometer");
    lv_obj_set_style_text_color(adc_lbl, lv_color_hex(0xCCCCCC), 0);

    adc_bar = lv_bar_create(panel);
    lv_obj_set_size(adc_bar, LV_PCT(95), 15);
    lv_bar_set_range(adc_bar, 0, 100);
    lv_bar_set_value(adc_bar, 0, LV_ANIM_OFF);
    lv_obj_set_style_bg_color(adc_bar, lv_color_hex(0x333333), 0);
    lv_obj_set_style_bg_color(adc_bar,
        lv_palette_main(LV_PALETTE_CYAN), LV_PART_INDICATOR);

    adc_percent_label = lv_label_create(panel);
    lv_label_set_text(adc_percent_label, "ADC: 0 (0%)");
    lv_obj_set_style_text_color(adc_percent_label,
        lv_color_hex(0x00BFFF), 0);

    adc_voltage_label = lv_label_create(panel);
    lv_label_set_text(adc_voltage_label, "0.00 V");
    lv_obj_set_style_text_color(adc_voltage_label,
        lv_color_hex(0x00FF88), 0);

    return panel;
}

/* ============================================================ */
/*                  HELPER: Create Output Panel                 */
/* ============================================================ */
static lv_obj_t * create_output_panel(lv_obj_t * parent)
{
    lv_obj_t * panel = lv_obj_create(parent);
    lv_obj_set_size(panel, LV_PCT(48), LV_PCT(100));
    lv_obj_set_style_bg_color(panel, lv_color_hex(0x1a1a40), 0);
    lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_all(panel, 8, 0);
    lv_obj_set_style_pad_row(panel, 4, 0);
    lv_obj_remove_flag(panel, LV_OBJ_FLAG_SCROLLABLE);

    /* Panel Title */
    lv_obj_t * lbl = lv_label_create(panel);
    lv_label_set_text(lbl, "-- OUTPUT --");
    lv_obj_set_style_text_color(lbl, lv_color_hex(0xFF6600), 0);

    /* LED colors and names */
    lv_color_t colors[] = {
        lv_palette_main(LV_PALETTE_RED),
        lv_palette_main(LV_PALETTE_GREEN),
        lv_palette_main(LV_PALETTE_BLUE)
    };
    const char * names[] = {"Red", "Green", "Blue"};
    lv_event_cb_t cbs[] = {red_switch_cb, green_switch_cb, NULL};

    /* Red + Green rows (Switch control) */
    for (int i = 0; i < 2; i++) {
        lv_obj_t * row = lv_obj_create(panel);
        lv_obj_set_size(row, LV_PCT(100), 32);
        lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW);
        lv_obj_set_flex_align(row, LV_FLEX_ALIGN_START,
                              LV_FLEX_ALIGN_CENTER,
                              LV_FLEX_ALIGN_CENTER);
        lv_obj_set_style_bg_opa(row, LV_OPA_TRANSP, 0);
        lv_obj_set_style_border_width(row, 0, 0);
        lv_obj_set_style_pad_all(row, 0, 0);
        lv_obj_set_style_pad_column(row, 8, 0);
        lv_obj_remove_flag(row, LV_OBJ_FLAG_SCROLLABLE);

        led_inds[i] = lv_led_create(row);
        lv_led_set_color(led_inds[i], colors[i]);
        lv_obj_set_size(led_inds[i], 20, 20);
        lv_led_off(led_inds[i]);

        lv_obj_t * name = lv_label_create(row);
        lv_label_set_text(name, names[i]);
        lv_obj_set_style_text_color(name, lv_color_hex(0xCCCCCC), 0);

        led_switches[i] = lv_switch_create(row);
        lv_obj_add_event_cb(led_switches[i], cbs[i],
                            LV_EVENT_VALUE_CHANGED, NULL);
    }

    /* Blue row (Slider control) */
    lv_obj_t * blue_row = lv_obj_create(panel);
    lv_obj_set_size(blue_row, LV_PCT(100), 32);
    lv_obj_set_flex_flow(blue_row, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(blue_row, LV_FLEX_ALIGN_START,
                          LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_bg_opa(blue_row, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(blue_row, 0, 0);
    lv_obj_set_style_pad_all(blue_row, 0, 0);
    lv_obj_set_style_pad_column(blue_row, 6, 0);
    lv_obj_remove_flag(blue_row, LV_OBJ_FLAG_SCROLLABLE);

    led_inds[2] = lv_led_create(blue_row);
    lv_led_set_color(led_inds[2], colors[2]);
    lv_obj_set_size(led_inds[2], 20, 20);
    lv_led_off(led_inds[2]);

    blue_slider = lv_slider_create(blue_row);
    lv_obj_set_width(blue_slider, 100);
    lv_slider_set_range(blue_slider, 0, 100);
    lv_slider_set_value(blue_slider, 0, LV_ANIM_OFF);
    lv_obj_add_event_cb(blue_slider, blue_slider_cb,
                        LV_EVENT_VALUE_CHANGED, NULL);

    blue_duty_label = lv_label_create(blue_row);
    lv_label_set_text(blue_duty_label, "0%");
    lv_obj_set_style_text_color(blue_duty_label,
        lv_color_hex(0x00BFFF), 0);

    /* Batch Buttons */
    lv_obj_t * batch_row = lv_obj_create(panel);
    lv_obj_set_size(batch_row, LV_PCT(100), 35);
    lv_obj_set_flex_flow(batch_row, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(batch_row, LV_FLEX_ALIGN_CENTER,
                          LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_bg_opa(batch_row, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(batch_row, 0, 0);
    lv_obj_set_style_pad_column(batch_row, 8, 0);
    lv_obj_remove_flag(batch_row, LV_OBJ_FLAG_SCROLLABLE);

    lv_obj_t * btn_on = lv_button_create(batch_row);
    lv_obj_set_size(btn_on, 70, 28);
    lv_obj_set_style_bg_color(btn_on,
        lv_palette_main(LV_PALETTE_GREEN), 0);
    lv_obj_add_event_cb(btn_on, all_on_cb, LV_EVENT_CLICKED, NULL);
    lv_obj_t * lo = lv_label_create(btn_on);
    lv_label_set_text(lo, "All ON");
    lv_obj_center(lo);

    lv_obj_t * btn_off = lv_button_create(batch_row);
    lv_obj_set_size(btn_off, 70, 28);
    lv_obj_set_style_bg_color(btn_off,
        lv_palette_main(LV_PALETTE_RED), 0);
    lv_obj_add_event_cb(btn_off, all_off_cb, LV_EVENT_CLICKED, NULL);
    lv_obj_t * lf = lv_label_create(btn_off);
    lv_label_set_text(lf, "All OFF");
    lv_obj_center(lf);

    return panel;
}

/* ============================================================ */
/*                      MAIN FUNCTION                           */
/* ============================================================ */
void part1_ex9_hw_gpio_dashboard(void)
{
    /* ============================== */
    /*    HARDWARE INITIALIZATION     */
    /* ============================== */
    aic_gpio_init();                       /* [1] GPIO */
    aic_gpio_pwm_init(AIC_LED_BLUE);       /* [2] PWM */
    aic_sensors_init();                    /* [3] ADC */

    /* ============================== */
    /*         LVGL UI SETUP          */
    /* ============================== */
    lv_obj_t * scr = lv_screen_active();
    lv_obj_set_style_bg_color(scr, lv_color_hex(0x0a0a1a), 0);

    /* --- Title --- */
    lv_obj_t * title = lv_label_create(scr);
    lv_label_set_text(title, "Lab 9: HW GPIO Dashboard");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);

    /* --- Main Container (2 panels side by side) --- */
    lv_obj_t * main_cont = lv_obj_create(scr);
    lv_obj_set_size(main_cont, 470, 195);
    lv_obj_align(main_cont, LV_ALIGN_CENTER, 0, 5);
    lv_obj_set_flex_flow(main_cont, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(main_cont, LV_FLEX_ALIGN_SPACE_BETWEEN,
                          LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_bg_opa(main_cont, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(main_cont, 0, 0);
    lv_obj_set_style_pad_all(main_cont, 0, 0);
    lv_obj_set_style_pad_column(main_cont, 6, 0);
    lv_obj_remove_flag(main_cont, LV_OBJ_FLAG_SCROLLABLE);

    /* Create Panels */
    create_input_panel(main_cont);
    create_output_panel(main_cont);

    /* --- Status Bar --- */
    status_label = lv_label_create(scr);
    lv_label_set_text(status_label, "BTN=REL | ADC=0 | Blue=0%");
    lv_obj_set_style_text_color(status_label, lv_color_hex(0x888888), 0);
    lv_obj_align(status_label, LV_ALIGN_BOTTOM_MID, 0, -5);

    /* ============================== */
    /*    START POLLING TIMER         */
    /* ============================== */
    lv_timer_create(poll_timer_cb, 50, NULL);
}
```

#### 4.2 อธิบายโครงสร้างโค้ด

| ส่วน                    | หน้าที่                      | จำนวนบรรทัด |
| ----------------------- | ---------------------------- | ----------- |
| Global Variables        | เก็บ widget pointers ทั้งหมด | \~15        |
| `poll_timer_cb()`       | อ่าน Button + ADC ทุก 50ms   | \~25        |
| `red/green_switch_cb()` | ควบคุม Red/Green LED         | \~10 each   |
| `blue_slider_cb()`      | ควบคุม Blue LED PWM          | \~8         |
| `all_on/off_cb()`       | Batch control ทุก LED        | \~15 each   |
| `create_input_panel()`  | สร้าง UI ฝั่ง Input          | \~40        |
| `create_output_panel()` | สร้าง UI ฝั่ง Output         | \~60        |
| `part1_ex9_...()`       | Main function init + layout  | \~30        |

***

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

#### 5.1 Pattern: Panel-based Layout

```c
/* แบ่ง Dashboard เป็น panels */
lv_obj_t * main = lv_obj_create(scr);
lv_obj_set_flex_flow(main, LV_FLEX_FLOW_ROW);

/* Panel ซ้าย: Input (48% width) */
lv_obj_t * input = lv_obj_create(main);
lv_obj_set_size(input, LV_PCT(48), LV_PCT(100));

/* Panel ขวา: Output (48% width) */
lv_obj_t * output = lv_obj_create(main);
lv_obj_set_size(output, LV_PCT(48), LV_PCT(100));
```

#### 5.2 Pattern: Batch Control with UI Sync

```c
/* เมื่อกด All ON ต้อง sync ทั้ง Hardware + UI */
static void all_on_cb(lv_event_t * e)
{
    /* Hardware */
    aic_gpio_led_set(AIC_LED_RED, true);
    aic_gpio_led_set(AIC_LED_GREEN, true);

    /* UI: LED indicators */
    lv_led_on(led_red);
    lv_led_on(led_green);

    /* UI: Sync switches (important!) */
    lv_obj_add_state(sw_red, LV_STATE_CHECKED);
    lv_obj_add_state(sw_green, LV_STATE_CHECKED);
}
```

#### 5.3 Pattern: Mixed Poll + Event

```c
/* INPUT devices: ใช้ Timer Polling */
lv_timer_create(poll_cb, 50, NULL);  /* Button + ADC */

/* OUTPUT devices: ใช้ Event Callback */
lv_obj_add_event_cb(switch, cb, LV_EVENT_VALUE_CHANGED, NULL);

/* RULE: อ่าน Input = poll, เขียน Output = event */
```

#### 5.4 สิ่งที่ต้องระวัง

<table><thead><tr><th width="197.1605224609375">หัวข้อ</th><th>รายละเอียด</th></tr></thead><tbody><tr><td><strong>Init Order</strong></td><td><code>aic_gpio_init()</code> -> <code>aic_gpio_pwm_init()</code> -> <code>aic_sensors_init()</code></td></tr><tr><td><strong>UI Sync</strong></td><td>เมื่อ All ON/OFF ต้อง sync ทั้ง Hardware + Switch states + LED indicators</td></tr><tr><td><strong>Single Timer</strong></td><td>ใช้ timer เดียวอ่านทั้ง Button + ADC (ไม่ต้องแยก timer)</td></tr><tr><td><strong>LVGL Thread</strong></td><td>ทุก UI update ต้องอยู่ใน LVGL callback context</td></tr><tr><td><strong>PWM Blue Only</strong></td><td>Red/Green ใช้ Switch (ON/OFF), Blue ใช้ Slider (0-100%)</td></tr></tbody></table>

#### 5.5 Application ในอุตสาหกรรม

* **Factory Control Panel**: ควบคุมมอเตอร์ + อ่าน sensor + แสดงสถานะ
* **Building Management**: ควบคุมไฟ + อ่าน occupancy sensor + แสดง energy
* **Process Control**: ปรับ valve (slider) + อ่าน pressure (ADC) + alarm (LED)
* **Medical Device**: ตั้งค่า parameter + monitor patient vitals

***

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

#### แบบฝึกหัดที่ 1: ADC-controlled LED Brightness (Auto Mode)

**โจทย์**: เพิ่มโหมด "Auto" ที่ ADC Potentiometer ควบคุมความสว่าง Blue LED โดยอัตโนมัติ

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

* เพิ่ม Switch "Auto Mode" บน Output Panel
* เมื่อ Auto Mode = ON:
  * Blue Slider ถูก disable (สีจาง)
  * ค่า ADC (0-4095) ถูก map เป็น PWM duty (0-100)
  * Blue LED เปลี่ยนความสว่างตาม Potentiometer แบบ real-time
  * Slider เคลื่อนตามค่า ADC (read-only visual)
* เมื่อ Auto Mode = OFF:
  * Blue Slider กลับมาใช้งานได้ปกติ (Manual control)
* แสดงโหมดปัจจุบัน: "Auto: POT->Blue" หรือ "Manual"

**Hints**:

* ใช้ `static bool auto_mode = false;`
* `lv_obj_add_state(slider, LV_STATE_DISABLED)` / `lv_obj_remove_state()`
* ใน `poll_timer_cb`: ถ้า auto\_mode ให้ set PWM จาก ADC

#### แบบฝึกหัดที่ 2: Mini Alarm System

**โจทย์**: สร้างระบบ Alarm ย่อที่ใช้ Button arm/disarm และ ADC trigger

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

* **สถานะ Disarmed** (เริ่มต้น):
  * แสดง "DISARMED" สีเขียว
  * LED ดับหมด
  * กดปุ่ม USER -> เปลี่ยนเป็น Armed
* **สถานะ Armed**:
  * แสดง "ARMED" สีเหลือง
  * Green LED ติด (indicator ว่า armed)
  * ถ้า ADC > 80% -> เปลี่ยนเป็น Alarm Triggered
  * กดปุ่ม USER -> กลับเป็น Disarmed
* **สถานะ Alarm Triggered**:
  * แสดง "!! ALARM !!" สีแดง กะพริบ
  * Red LED กะพริบ (toggle ทุก 200ms ใน timer)
  * Blue LED สว่าง 100%
  * กดปุ่ม USER -> กลับเป็น Disarmed
* แสดง ADC value เป็น Bar graph ตลอดเวลา
* แสดง Threshold line บน Bar (เส้นที่ 80%)

**Hints**:

* ใช้ `enum { DISARMED, ARMED, TRIGGERED }` สำหรับ state machine
* Edge detection สำหรับ button press (เปลี่ยนสถานะเฉพาะจังหวะกด)
* Timer 50ms สำหรับ poll; ใช้ counter นับ tick สำหรับกะพริบ (4 ticks = 200ms)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/gpio-to-hmi-display/hardware-interfacing-workshops/gpio-dashboard.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.
