> 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/multi-core-communication/event-bus.md).

# Event Bus

## Lab 2: Event Bus Integration (EventBus + Advanced IPC)

### Part 4 - IPC & Event Bus

***

### 1. โครงสร้างภาพรวม (Overview)

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

**Event-Driven Architecture** คือรูปแบบสถาปัตยกรรมที่ใช้ในระบบ Industrial, SCADA, IoT ทุกระดับ:

* **Loose Coupling**: ลด dependency ระหว่างโมดูล ทำให้ขยายระบบได้ง่าย
* **Publish-Subscribe**: หลายโมดูลสามารถ subscribe event เดียวกัน ไม่ต้องรู้จักกัน
* **Scalability**: เพิ่ม subscriber ได้โดยไม่ต้องแก้ publisher

ในระบบ Embedded จริง ไม่มี feature ใดทำงานอิสระ ทุกอย่างต้อง **บูรณาการ** เข้าด้วยกัน:

* **Smart Factory HMI**: หน้าจอเดียวต้องแสดง sensor data + ควบคุม actuator + แสดง event log + monitor สถานะระบบ
* **Medical Device**: Patient monitor ต้องรวม ECG + SpO2 + Blood Pressure + Alarm ทั้งหมด
* **Automotive Dashboard**: Instrument cluster ต้องรวม speed + RPM + fuel + warning lights
* **Building Management System**: HVAC control + lighting + security + energy monitoring ในระบบเดียว

Lab นี้เป็นแบบ **progressive learning** -- เริ่มจาก Part A ที่สร้าง Event Bus ด้วย simulated/IPC data แล้วก้าวสู่ Part B ที่บูรณาการ Event Bus + IPC + Logging + GPIO real hardware เข้าด้วยกัน

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

**Part A (Ex4 - Event Bus):**

1. **Event Bus Pattern**: Publish-Subscribe สำหรับ Embedded Systems
2. **Event Types**: EVENT\_IMU, EVENT\_ADC, EVENT\_BUTTON
3. **Multiple Subscribers**: หลาย handler ต่อ 1 event
4. **State Machine**: ออกแบบ state machine ที่ขับเคลื่อนด้วย event

**Part B (Ex8 - Advanced Integration):**

1. **System Integration**: รวม 4 subsystems (IPC, EventBus, Logging, Hardware)
2. **Layout Helpers**: ใช้ `aic_col_create()`, `aic_row_create()`, `aic_full_size()`, `aic_pad()` สร้าง UI อย่างรวดเร็ว
3. **Event Bus Pub/Sub**: Decouple sensor reads จาก UI updates ผ่าน publish/subscribe
4. **IPC Statistics Monitoring**: ติดตาม TX/RX/Error counts แบบ real-time
5. **Multi-Sensor Dashboard**: แสดง IMU + ADC + Button + LED ในหน้าเดียว
6. **Combined Init Pattern**: ลำดับการ initialize subsystems ที่ถูกต้อง

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

**Part A:** สร้าง Event Bus infrastructure (subscribe, publish, dispatch) + UI แสดง event log + statistics โดยรับข้อมูลจาก IPC callback ผ่าน flag-based pattern

**Part B:** สร้าง Dashboard บน CM55 ด้วย layout helpers ที่ประกอบด้วย LED row, Button state, IMU display, ADC display, Stats row -- โดยรวม IPC + EventBus + Logging + GPIO เข้าด้วยกัน

***

### 2. หลักการทางเทคนิค (Technical Principles)

#### 2.1 Event-Driven Architecture

```
+-----------------------------------------------------------------------+
|               Event-Driven Architecture Overview                      |
+-----------------------------------------------------------------------+
|                                                                       |
|  Traditional (Polling):                                               |
|  +----------------------------------------------+                     |
|  |  while(1) {                                  |                     |
|  |      if (button_pressed()) handle_btn();     | <- Wastes CPU       |
|  |      if (sensor_ready())  read_sensor();     |    checking things  |
|  |      if (timer_expired()) update_ui();       |    that haven't     |
|  |  }                                           |    happened yet     |
|  +----------------------------------------------+                     |
|                                                                       |
|  Event-Driven:                                                        |
|  +----------------------------------------------+                     |
|  |                 EVENT BUS                    |                     |
|  |                                              |                     |
|  |  Publishers              Subscribers         |                     |
|  |  ----------              -----------         |                     |
|  |  IPC Callback --+   +-- UI Handler           |                     |
|  |  Button ISR  ---+   +-- Log Handler          |                     |
|  |  Timer       ---+   +-- LED Handler          |                     |
|  |  Sensor      ---+   +-- State Machine        |                     |
|  |                                              |                     |
|  |  Publisher ไม่ต้องรู้ว่า Subscriber คือใคร         |                     |
|  |  Subscriber ไม่ต้องรู้ว่า Publisher คือใคร         |                     |
|  +----------------------------------------------+                     |
|                                                                       |
+-----------------------------------------------------------------------+
```

#### 2.2 Publish-Subscribe Flow

```
 Publisher                    Event Bus                 Subscribers
 ---------                   ---------                 -----------
    |                            |                         |
    | publish(EVENT_IMU, data)   |                         |
    | -------------------------> |                         |
    |                            | dispatch to all         |
    |                            | subscribers of          |
    |                            | EVENT_IMU               |
    |                            | ----------------------> | handler_1(data)
    |                            | ----------------------> | handler_2(data)
    |                            | ----------------------> | handler_3(data)
    |                            |                         |
    |                            |                         |
    | publish(EVENT_BUTTON, data)|                         |
    | -------------------------> |                         |
    |                            | ----------------------> | btn_handler(data)
    |                            |                         |
```

#### 2.3 Event Bus Architecture (Part A)

```
+-----------------------------------------------------------------------+
|                     Event Bus Architecture                            |
+-----------------------------------------------------------------------+
|                                                                       |
|  +-------------+                    +------------------------------+  |
|  | IPC Callback |   flag-based      |        EVENT BUS             |  |
|  | (IPC Task)   | ---------------->  |                             |  |
|  | imu_ready=1  |   imu_data        |  subscribers[EVENT_IMU][]    |  |
|  | btn_ready=1  |   btn_data        |  +-- [0] ui_chart_handler    |  |
|  +-------------+                    |  +-- [1] log_handler         |  |
|                                      |  +-- [2] threshold_handler  |  |
|  +-------------+   publish()         |  +-- [3] (empty)            |  |
|  | LVGL Timer   | ---------------->  |                             |  |
|  | (50ms poll)  |                    |  subscribers[EVENT_BUTTON][]|  |
|  | check flags  |                    |  +-- [0] led_toggle_handler |  |
|  | -> publish() |                    |  +-- [1] state_handler      |  |
|  +-------------+                    |  +-- [2] log_handler         |  |
|                                      |                             |  |
|  +-------------+   publish()         |  subscribers[EVENT_ADC][]   |  |
|  | Timer        | ---------------->  |  +-- [0] gauge_handler      |  |
|  | (1s tick)    |                    |  +-- [1] threshold_handler  |  |
|  | EVENT_TIMER  |                    |                             |  |
|  +-------------+                    +------------------------------+  |
|                                                                       |
|  KEY: ทุกอย่างทำงานใน LVGL context (thread-safe)                        |
|       IPC callback ใช้ flag-based pattern ก่อน publish                  |
|                                                                       |
+-----------------------------------------------------------------------+
```

#### 2.4 Integration Architecture (Part B)

```
+===================================================================+
|           ADVANCED FEATURES - INTEGRATION ARCHITECTURE            |
+===================================================================+
|                                                                   |
|  CM55 Core (LVGL + Application Logic)                             |
|  =============================================                    |
|                                                                   |
|  +----------------+  +----------------+  +------------------+     |
|  |   IPC Layer    |  |  Event Bus     |  |  Logging System  |     |
|  |                |  |                |  |                  |     |
|  | cm55_ipc_*()   |  | aic_event_*()  |  | aic_log_init()   |     |
|  | set_led()      |  | subscribe()    |  | CM55_LOGI()      |     |
|  | get_stats()    |  | publish_imu()  |  | CM55_LOGW()      |     |
|  | is_init()      |  | publish_adc()  |  | CM55_LOGE()      |     |
|  +-------+--------+  +-------+--------+  +--------+---------+     |
|          |                    |                     |             |
|          v                    v                     v             |
|  +------------------------------------------------------------+   |
|  |               LVGL UI Dashboard                            |   |
|  |                                                            |   |
|  | [Title] Part 4 Ex8: Advanced                               |   |
|  | [Subtitle] IPC + EventBus + Logging + HW                   |   |
|  |                                                            |   |
|  | [LED Row]  [*R][L1]  [*G][L2]  [*B][L3]     <- IPC send    |   |
|  |                                                            |   |
|  | [Button]   BTN1: Released/Pressed            <- GPIO read  |   |
|  |                                                            |   |
|  | [IMU]      Accel (EventBus)                  <- EventBus   |   |
|  |            X: +0.05  Y: -0.12  Z: +9.81                    |   |
|  |                                                            |   |
|  | [ADC]      ADC: 2048 (1.65V)                 <- EventBus   |   |
|  |                                                            |   |
|  | [Stats]    Events: 42  IPC TX:10 RX:8 Err:0  <- Monitor    |   |
|  +------------------------------------------------------------+   |
|                                                                   |
|  ========================== IPC PIPE ==========================   |
|                                                                   |
|  CM33-NS Core (Hardware + Sensors)                                |
|  =============================================                    |
|  +----------+ +----------+ +----------+ +----------+              |
|  | LED GPIO | |  Button  | |   IMU    | |   ADC    |              |
|  | Handler  | |  Poller  | |  Reader  | |  Reader  |              |
|  +----------+ +----------+ +----------+ +----------+              |
|                                                                   |
+===================================================================+
```

#### 2.5 Initialization Sequence (Part B -- Critical!)

```
+===================================================================+
|              INITIALIZATION SEQUENCE (CRITICAL ORDER)             |
+===================================================================+
|                                                                   |
|  +-------------------+                                            |
|  |   Start Ex8       |                                            |
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   aic_gpio_init() ต้องมาก่อน                 |
|  | 1. GPIO Init      |   --> return bool (ready/not ready)        |
|  |  aic_gpio_init()  |   --> ถ้า false: ข้าม HW functions           | 
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   aic_sensors_init() ต้องมาก่อน              |
|  | 2. Sensors Init   |   aic_adc_read() เสมอ!                     |
|  | aic_sensors_init()|                                            |
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   Event Bus ต้อง init ก่อน subscribe         |
|  | 3. Event Bus Init |                                            |
|  |  aic_event_init() |                                            |
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   Logging ใช้ได้ทันทีหลัง init                  |
|  | 4. Logging Init   |                                            |
|  |  aic_log_init()   |                                            |
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   สร้าง UI objects                          |
|  | 5. Build UI       |   (ต้องสร้างก่อน subscribe)                   |
|  |  Layout helpers   |                                            |
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   ตอนนี้ UI objects พร้อมแล้ว                  |
|  | 6. Subscribe      |   จึง subscribe event ได้                    |
|  |  aic_event_sub()  |                                            |
|  +--------+----------+                                            |
|           |                                                       |
|           v                                                       |
|  +-------------------+   Timer เริ่มทำงาน                           |
|  | 7. Create Timer   |   --> ระบบเริ่มทำงานจริง                      |
|  | lv_timer_create() |                                            |
|  +-------------------+                                            |
|                                                                   |
+===================================================================+
```

***

### 3. Part A: Event Bus (Ex4 - Simulated + IPC Data)

Part A สอนการสร้าง Event Bus infrastructure ตั้งแต่ต้น -- ออกแบบ event types, subscriber system, publish/dispatch mechanism แล้วทดสอบด้วย IPC data + timer events

#### 3.1 Event Types

```c
/*******************************************************************************
 * Event Type Definitions
 ******************************************************************************/
typedef enum {
    EVENT_NONE = 0,

    /* Sensor Events */
    EVENT_IMU,              /* IMU data received */
    EVENT_ADC,              /* ADC data received */
    EVENT_TEMP,             /* Temperature data */

    /* Input Events */
    EVENT_BUTTON,           /* Button press/release */
    EVENT_TOUCH,            /* Touch screen event */

    /* System Events */
    EVENT_IPC_CONNECTED,    /* IPC pipe established */
    EVENT_IPC_ERROR,        /* IPC communication error */
    EVENT_TIMER,            /* Periodic timer tick */

    /* Application Events */
    EVENT_THRESHOLD,        /* Sensor value exceeded threshold */
    EVENT_STATE_CHANGE,     /* State machine transition */

    EVENT_TYPE_COUNT        /* จำนวน event types ทั้งหมด */
} event_type_t;
```

#### 3.2 Event Data Structure

```c
/*******************************************************************************
 * Event Data
 ******************************************************************************/
typedef struct {
    event_type_t type;          /* ประเภท event */
    uint32_t     timestamp;     /* เวลาที่เกิด event (ms) */
    int32_t      value;         /* ค่าตัวเลข */
    void         *data;         /* ข้อมูลเพิ่มเติม (optional) */
} event_t;

/* Event Names (สำหรับ display) */
static const char *event_names[] = {
    "NONE", "IMU", "ADC", "TEMP",
    "BUTTON", "TOUCH",
    "IPC_OK", "IPC_ERR", "TIMER",
    "THRESH", "STATE"
};
```

#### 3.3 Event Bus Core

```c
/*******************************************************************************
 * Event Bus Infrastructure
 ******************************************************************************/
#define MAX_SUBSCRIBERS_PER_EVENT  4
#define EVENT_QUEUE_SIZE          16

/* Subscriber callback type */
typedef void (*event_handler_t)(const event_t *event, void *user_data);

/* Subscriber entry */
typedef struct {
    event_handler_t handler;
    void           *user_data;
    bool            active;
} subscriber_t;

/* Event Bus */
static subscriber_t subscribers[EVENT_TYPE_COUNT][MAX_SUBSCRIBERS_PER_EVENT];
static uint32_t event_counts[EVENT_TYPE_COUNT];     /* จำนวน events ต่อ type */
static uint32_t total_events = 0;

/*******************************************************************************
 * Subscribe to an event type
 * return: subscriber index (0-3) or -1 on failure
 ******************************************************************************/
static int event_subscribe(event_type_t type, event_handler_t handler,
                           void *user_data)
{
    if (type >= EVENT_TYPE_COUNT || handler == NULL) return -1;

    for (int i = 0; i < MAX_SUBSCRIBERS_PER_EVENT; i++) {
        if (!subscribers[type][i].active) {
            subscribers[type][i].handler   = handler;
            subscribers[type][i].user_data = user_data;
            subscribers[type][i].active    = true;
            return i;
        }
    }
    return -1;  /* Full */
}

/*******************************************************************************
 * Unsubscribe from an event type
 ******************************************************************************/
static void event_unsubscribe(event_type_t type, int index)
{
    if (type < EVENT_TYPE_COUNT && index >= 0
        && index < MAX_SUBSCRIBERS_PER_EVENT) {
        subscribers[type][index].active = false;
    }
}

/*******************************************************************************
 * Publish an event (dispatch to all subscribers)
 *
 * NOTE: เรียกจาก LVGL context เท่านั้น!
 * ถ้าต้อง publish จาก IPC callback ให้ใช้ flag-based pattern
 ******************************************************************************/
static void event_publish(event_type_t type, int32_t value, void *data)
{
    if (type >= EVENT_TYPE_COUNT) return;

    event_t event = {
        .type      = type,
        .timestamp = lv_tick_get(),
        .value     = value,
        .data      = data
    };

    event_counts[type]++;
    total_events++;

    /* Dispatch to all active subscribers */
    for (int i = 0; i < MAX_SUBSCRIBERS_PER_EVENT; i++) {
        if (subscribers[type][i].active) {
            subscribers[type][i].handler(&event,
                subscribers[type][i].user_data);
        }
    }
}

/*******************************************************************************
 * Initialize Event Bus
 ******************************************************************************/
static void event_bus_init(void)
{
    memset(subscribers, 0, sizeof(subscribers));
    memset(event_counts, 0, sizeof(event_counts));
    total_events = 0;
}
```

#### 3.4 Step-by-Step Implementation

**Step 1: ประกาศ Variables**

```c
/* part4_ex4_event_bus.c */
#include "lvgl.h"
#include "ipc/cm55_ipc_pipe.h"

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>

/*******************************************************************************
 * LVGL Objects
 ******************************************************************************/
static lv_obj_t *label_title      = NULL;
static lv_obj_t *label_state      = NULL;
static lv_obj_t *label_event_log  = NULL;
static lv_obj_t *label_stats      = NULL;
static lv_obj_t *led_status       = NULL;
static lv_obj_t *chart_events     = NULL;

/* Chart series (event frequency per type) */
static lv_chart_series_t *ser_freq = NULL;

/*******************************************************************************
 * Flag-Based Pattern (IPC -> Event Bus bridge)
 ******************************************************************************/
static volatile bool     imu_event_pending    = false;
static volatile bool     button_event_pending = false;
static volatile bool     adc_event_pending    = false;
static volatile int32_t  pending_imu_value    = 0;
static volatile int32_t  pending_btn_value    = 0;
static volatile int32_t  pending_adc_value    = 0;

/*******************************************************************************
 * Event Log (ring buffer)
 ******************************************************************************/
#define EVENT_LOG_SIZE  10
#define EVENT_LOG_BUF   1024

static char event_log_entries[EVENT_LOG_SIZE][64];
static uint32_t event_log_idx = 0;
static char event_log_display[EVENT_LOG_BUF];
```

**Step 2: IPC Callback (Bridge to Event Bus)**

```c
/*******************************************************************************
 * IPC Receive Callback
 *
 * CRITICAL: ทำงานใน IPC Task - ห้ามเรียก event_publish() โดยตรง!
 * ตั้ง flag เพื่อให้ LVGL timer publish event แทน
 ******************************************************************************/
static void ipc_event_callback(const ipc_msg_t *msg, void *user_data)
{
    (void)user_data;

    switch (msg->cmd) {
        case IPC_CMD_IMU_DATA: {
            const ipc_imu_data_t *imu = (const ipc_imu_data_t *)msg->data;
            /* ส่งค่า magnitude ของ accel */
            pending_imu_value = (int32_t)imu->accel_x;
            imu_event_pending = true;
            break;
        }

        case IPC_CMD_BUTTON_EVENT: {
            pending_btn_value = (int32_t)msg->value;  /* 1=pressed, 0=released */
            button_event_pending = true;
            break;
        }

        case IPC_CMD_ADC_DATA: {
            const ipc_adc_data_t *adc = (const ipc_adc_data_t *)msg->data;
            pending_adc_value = (int32_t)adc->adc_ch0;
            adc_event_pending = true;
            break;
        }

        default:
            break;
    }
}
```

**Step 3: Event Handlers (Subscribers)**

```c
/*******************************************************************************
 * Event Log Handler - จดบันทึกทุก event
 ******************************************************************************/
static void event_log_handler(const event_t *event, void *user_data)
{
    (void)user_data;

    uint32_t ts = event->timestamp / 1000;
    snprintf(event_log_entries[event_log_idx % EVENT_LOG_SIZE],
        64, "%02u:%02u:%02u [%s] val=%d",
        (unsigned int)((ts / 3600) % 24),
        (unsigned int)((ts / 60) % 60),
        (unsigned int)(ts % 60),
        event_names[event->type],
        (int)event->value);
    event_log_idx++;

    /* สร้าง display string */
    event_log_display[0] = '\0';
    int pos = 0;
    uint32_t total = (event_log_idx < EVENT_LOG_SIZE)
        ? event_log_idx : EVENT_LOG_SIZE;
    uint32_t start = (event_log_idx < EVENT_LOG_SIZE)
        ? 0 : (event_log_idx % EVENT_LOG_SIZE);

    for (uint32_t i = 0; i < total; i++) {
        uint32_t idx = (start + i) % EVENT_LOG_SIZE;
        int w = snprintf(event_log_display + pos,
            EVENT_LOG_BUF - pos, "%s\n", event_log_entries[idx]);
        if (w > 0) pos += w;
    }

    lv_label_set_text(label_event_log, event_log_display);
}

/*******************************************************************************
 * UI Stats Handler - อัปเดตสถิติ
 ******************************************************************************/
static void event_stats_handler(const event_t *event, void *user_data)
{
    (void)event;
    (void)user_data;

    lv_label_set_text_fmt(label_stats,
        "Total:%u  IMU:%u  ADC:%u  BTN:%u  TMR:%u",
        (unsigned int)total_events,
        (unsigned int)event_counts[EVENT_IMU],
        (unsigned int)event_counts[EVENT_ADC],
        (unsigned int)event_counts[EVENT_BUTTON],
        (unsigned int)event_counts[EVENT_TIMER]);
}

/*******************************************************************************
 * LED Handler - กระพริบ LED เมื่อมี event
 ******************************************************************************/
static void event_led_handler(const event_t *event, void *user_data)
{
    (void)user_data;

    switch (event->type) {
        case EVENT_IMU:
            lv_led_set_color(led_status,
                lv_palette_main(LV_PALETTE_BLUE));
            break;
        case EVENT_BUTTON:
            lv_led_set_color(led_status,
                lv_palette_main(LV_PALETTE_GREEN));
            break;
        case EVENT_ADC:
            lv_led_set_color(led_status,
                lv_palette_main(LV_PALETTE_YELLOW));
            break;
        case EVENT_THRESHOLD:
            lv_led_set_color(led_status,
                lv_palette_main(LV_PALETTE_RED));
            break;
        default:
            lv_led_set_color(led_status,
                lv_palette_main(LV_PALETTE_GREY));
            break;
    }
    lv_led_on(led_status);
}

/*******************************************************************************
 * Threshold Handler - ตรวจจับค่าเกิน threshold
 ******************************************************************************/
#define IMU_THRESHOLD  5000   /* ค่า accelerometer ที่ถือว่าสูง */

static void event_threshold_handler(const event_t *event, void *user_data)
{
    (void)user_data;

    if (event->type == EVENT_IMU) {
        int32_t abs_val = (event->value >= 0) ? event->value : -(event->value);
        if (abs_val > IMU_THRESHOLD) {
            /* Publish threshold event */
            event_publish(EVENT_THRESHOLD, event->value, NULL);
            CM55_LOGW("Threshold exceeded! IMU=%d", (int)event->value);
        }
    }
}
```

**Step 4: Bridge Timer (Flag -> Event Bus)**

```c
/*******************************************************************************
 * Bridge Timer - แปลง IPC flags เป็น Event Bus events
 * ทำงานใน LVGL context จึง publish ได้อย่างปลอดภัย
 ******************************************************************************/
static void bridge_timer_cb(lv_timer_t *timer)
{
    (void)timer;

    /* ===== IMU Event ===== */
    if (imu_event_pending) {
        imu_event_pending = false;
        event_publish(EVENT_IMU, pending_imu_value, NULL);
    }

    /* ===== Button Event ===== */
    if (button_event_pending) {
        button_event_pending = false;
        event_publish(EVENT_BUTTON, pending_btn_value, NULL);
    }

    /* ===== ADC Event ===== */
    if (adc_event_pending) {
        adc_event_pending = false;
        event_publish(EVENT_ADC, pending_adc_value, NULL);
    }
}

/*******************************************************************************
 * Periodic Timer - publish EVENT_TIMER ทุก 5 วินาที
 ******************************************************************************/
static void periodic_timer_cb(lv_timer_t *timer)
{
    (void)timer;
    uint32_t uptime = lv_tick_get() / 1000;
    event_publish(EVENT_TIMER, (int32_t)uptime, NULL);
}

/*******************************************************************************
 * Sensor Request Timer - ร้องขอ sensor data จาก CM33
 ******************************************************************************/
static void sensor_poll_timer_cb(lv_timer_t *timer)
{
    (void)timer;
    if (!cm55_ipc_is_init()) return;
    cm55_ipc_request_sensor(IPC_CMD_IMU_DATA);
}
```

**Step 5: สร้าง UI Layout + Main Entry**

```c
/*******************************************************************************
 * Main Entry Point
 ******************************************************************************/
void part4_ex4_event_bus(void)
{
    /* ===== Initialize Event Bus ===== */
    event_bus_init();

    /* ===== ล้างหน้าจอ ===== */
    lv_obj_t *scr = lv_screen_active();
    lv_obj_clean(scr);
    lv_obj_set_style_bg_color(scr, lv_color_hex(0x0d1117), 0);

    /* ===== Title ===== */
    label_title = lv_label_create(scr);
    lv_label_set_text(label_title, "Event Bus System");
    lv_obj_set_style_text_font(label_title, &lv_font_montserrat_20, 0);
    lv_obj_set_style_text_color(label_title, lv_color_hex(0xd2a8ff), 0);
    lv_obj_align(label_title, LV_ALIGN_TOP_MID, 0, 5);

    /* ===== State Label ===== */
    label_state = lv_label_create(scr);
    lv_label_set_text(label_state, "State: IDLE");
    lv_obj_set_style_text_font(label_state, &lv_font_montserrat_16, 0);
    lv_obj_set_style_text_color(label_state, lv_color_hex(0x7ee787), 0);
    lv_obj_align(label_state, LV_ALIGN_TOP_LEFT, 10, 30);

    /* ===== LED Indicator ===== */
    led_status = lv_led_create(scr);
    lv_obj_set_size(led_status, 24, 24);
    lv_obj_align(led_status, LV_ALIGN_TOP_RIGHT, -10, 30);
    lv_led_set_color(led_status, lv_palette_main(LV_PALETTE_GREY));
    lv_led_off(led_status);

    /* ===== Stats Label ===== */
    label_stats = lv_label_create(scr);
    lv_label_set_text(label_stats, "Total:0  IMU:0  ADC:0  BTN:0  TMR:0");
    lv_obj_set_style_text_font(label_stats, &lv_font_montserrat_12, 0);
    lv_obj_set_style_text_color(label_stats, lv_color_hex(0x8b949e), 0);
    lv_obj_align(label_stats, LV_ALIGN_TOP_MID, 0, 55);

    /* ===== Event Log (Scrollable Container) ===== */
    lv_obj_t *log_panel = lv_obj_create(scr);
    lv_obj_set_size(log_panel, 300, 130);
    lv_obj_align(log_panel, LV_ALIGN_BOTTOM_MID, 0, -25);
    lv_obj_set_style_bg_color(log_panel, lv_color_hex(0x010409), 0);
    lv_obj_set_style_border_color(log_panel, lv_color_hex(0x30363d), 0);
    lv_obj_set_style_pad_all(log_panel, 5, 0);

    label_event_log = lv_label_create(log_panel);
    lv_label_set_text(label_event_log, "(waiting for events...)");
    lv_obj_set_width(label_event_log, 280);
    lv_label_set_long_mode(label_event_log, LV_LABEL_LONG_WRAP);
    lv_obj_set_style_text_font(label_event_log, &lv_font_montserrat_12, 0);
    lv_obj_set_style_text_color(label_event_log, lv_color_hex(0xc9d1d9), 0);

    /* ===== Subscribe Events ===== */
    /* Log handler - รับทุก event type */
    event_subscribe(EVENT_IMU,       event_log_handler, NULL);
    event_subscribe(EVENT_ADC,       event_log_handler, NULL);
    event_subscribe(EVENT_BUTTON,    event_log_handler, NULL);
    event_subscribe(EVENT_TIMER,     event_log_handler, NULL);
    event_subscribe(EVENT_THRESHOLD, event_log_handler, NULL);

    /* Stats handler */
    event_subscribe(EVENT_IMU,       event_stats_handler, NULL);
    event_subscribe(EVENT_ADC,       event_stats_handler, NULL);
    event_subscribe(EVENT_BUTTON,    event_stats_handler, NULL);
    event_subscribe(EVENT_TIMER,     event_stats_handler, NULL);

    /* LED handler */
    event_subscribe(EVENT_IMU,       event_led_handler, NULL);
    event_subscribe(EVENT_BUTTON,    event_led_handler, NULL);
    event_subscribe(EVENT_THRESHOLD, event_led_handler, NULL);

    /* Threshold handler */
    event_subscribe(EVENT_IMU, event_threshold_handler, NULL);

    /* ===== Register IPC Callback ===== */
    cm55_ipc_register_callback(ipc_event_callback, NULL);

    /* ===== Create Timers ===== */
    lv_timer_create(bridge_timer_cb, 50, NULL);         /* Flag -> Event */
    lv_timer_create(periodic_timer_cb, 5000, NULL);     /* Timer event */
    lv_timer_create(sensor_poll_timer_cb, 200, NULL);   /* Sensor poll */

    CM55_LOGI("Part4 Ex4: Event Bus initialized");
}
```

#### 3.5 Subscriber Registration Map

```
+------------------------------------------------------------------------+
|                    Subscriber Registration Map                         |
+------------------------------------------------------------------------+
|                                                                        |
|  Event Type       Subscriber [0]     [1]           [2]          [3]    |
|  ----------       --------------     ----          ----         ----   |
|  EVENT_IMU        log_handler        stats_handler led_handler  thresh |
|  EVENT_ADC        log_handler        stats_handler (empty)      (empty)|
|  EVENT_BUTTON     log_handler        stats_handler led_handler  (empty)|
|  EVENT_TIMER      log_handler        stats_handler (empty)      (empty)|
|  EVENT_THRESHOLD  log_handler        led_handler   (empty)      (empty)|
|                                                                        |
|  Flow example (EVENT_IMU published):                                   |
|  1. event_publish(EVENT_IMU, value, NULL)                              |
|  2. -> event_counts[EVENT_IMU]++                                       |
|  3. -> log_handler(event)       <- บันทึก log                            |
|  4. -> stats_handler(event)     <- อัปเดตสถิติ                            |
|  5. -> led_handler(event)       <- เปลี่ยนสี LED                          |
|  6. -> threshold_handler(event) <- ตรวจค่าเกิน -> อาจ publish THRESHOLD  |
|                                                                        |
+------------------------------------------------------------------------+
```

***

### 4. Part B: Advanced IPC Integration (Ex8 - Real Hardware)

Part B ยกระดับจาก Part A โดยใช้ **AIC Library APIs** (`aic_event_*`, `aic_gpio_*`, layout helpers) แทน manual Event Bus -- รวม IPC + EventBus + Logging + GPIO real hardware เข้าด้วยกัน

#### 4.1 Layout Helpers (AIC Layout System)

| Function                      | Description                           | เทียบเท่า LVGL Manual                            |
| ----------------------------- | ------------------------------------- | ------------------------------------------------ |
| `aic_col_create(parent)`      | สร้าง container แนวตั้ง (Flex Column) | `lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN)` |
| `aic_row_create(parent)`      | สร้าง container แนวนอน (Flex Row)     | `lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW)`    |
| `aic_full_size(obj)`          | ขยาย obj เต็ม parent                  | `lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100))` |
| `aic_pad(obj, px)`            | ตั้ง padding ทุกด้าน                  | `lv_obj_set_style_pad_all(obj, px, 0)`           |
| `aic_apply_dark_theme(scr)`   | ตั้งค่า dark theme                    | Multiple style calls                             |
| `aic_create_footer(scr)`      | สร้าง footer bar                      | Custom footer creation                           |
| `aic_xyz_display_create(...)` | สร้าง XYZ display                     | 3 labels + header                                |

#### 4.2 Layout Helpers vs Manual Positioning

```c
/*******************************************************************************
 * BEFORE (Manual Positioning) - Lab ก่อนหน้า
 *
 * ข้อเสีย: ต้องคำนวณ pixel ทุกตำแหน่ง, แก้ไขยาก, ไม่ responsive
 ******************************************************************************/
lv_obj_t *panel = lv_obj_create(scr);
lv_obj_set_size(panel, 480, 272);
lv_obj_set_pos(panel, 0, 0);
lv_obj_set_style_bg_color(panel, lv_color_hex(0x0a0e27), 0);
lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLLABLE);

lv_obj_t *title = lv_label_create(panel);
lv_obj_set_pos(title, 10, 5);
lv_obj_t *btn1 = lv_button_create(panel);
lv_obj_set_pos(btn1, 10, 40);
lv_obj_t *btn2 = lv_button_create(panel);
lv_obj_set_pos(btn2, 100, 40);   /* ต้องคำนวณ offset! */

/*******************************************************************************
 * AFTER (Layout Helpers) - Lab นี้
 *
 * ข้อดี: อัตโนมัติ, responsive, เพิ่ม/ลบ widgets ง่าย
 ******************************************************************************/
lv_obj_t *cont = aic_col_create(scr);    /* แนวตั้ง auto-layout */
aic_full_size(cont);                      /* เต็มจอ */
aic_pad(cont, 12);                        /* ขอบ 12px ทุกด้าน */

lv_obj_t *title = lv_label_create(cont);  /* อยู่บนสุด auto */
lv_obj_t *row = aic_row_create(cont);     /* แถวถัดไป auto */
lv_obj_t *btn1 = lv_button_create(row);   /* ซ้ายสุดในแถว */
lv_obj_t *btn2 = lv_button_create(row);   /* ถัดไปทางขวา auto */
```

#### 4.3 Layout Structure ของ Part B

```
Screen (aic_apply_dark_theme)
  |
  +-- cont = aic_col_create(scr)         <- Column layout
  |     aic_full_size(cont)               <- Full screen
  |     aic_pad(cont, 12)                 <- 12px padding
  |     |
  |     +-- title label                   <- "Part 4 Ex8: Advanced"
  |     |
  |     +-- subtitle label                <- "IPC + EventBus + Logging + HW"
  |     |
  |     +-- led_row = aic_row_create()    <- Row layout for LEDs
  |     |     |
  |     |     +-- wrap[0] (80x60)         <- LED R + button L1
  |     |     +-- wrap[1] (80x60)         <- LED G + button L2
  |     |     +-- wrap[2] (80x60)         <- LED B + button L3
  |     |
  |     +-- btn_label                     <- "BTN1: Released"
  |     |
  |     +-- aic_xyz_display               <- "Accel (EventBus)"
  |     |     +-- X: +0.05                   XYZ labels
  |     |     +-- Y: -0.12
  |     |     +-- Z: +9.81
  |     |
  |     +-- adc_label                     <- "ADC: 2048 (1.65V)"
  |     |
  |     +-- stats_row = aic_row_create()  <- Row layout for stats
  |           +-- event_label             <- "Events: 42"
  |           +-- ipc_label               <- "IPC TX:10 RX:8 Err:0"
  |
  +-- aic_create_footer(scr)              <- Footer bar
```

#### 4.4 Data Flow Diagram

```
+===================================================================+
|                     DATA FLOW DIAGRAM                             |
+===================================================================+
|                                                                   |
|  (1) LED Control Flow:                                            |
|  ~~~~~~~~~~~~~~~~~~~~~~                                           |
|  User Tap Button --> LVGL event_cb --> toggle state               |
|                 --> aic_gpio_led_set() (direct HW)                |
|                 --> cm55_ipc_set_led() (send to CM33)             |
|                 --> CM55_LOGI() (log the action)                  |
|                                                                   |
|  (2) Button Read Flow:                                            |
|  ~~~~~~~~~~~~~~~~~~~~~~                                           |
|  Timer (100ms) --> aic_gpio_button_read_raw()                     |
|                --> compare with last state (edge detect)          |
|                --> update UI label                                |
|                --> CM55_LOGI() if pressed (log the event)         |
|                                                                   |
|  (3) IMU Data Flow (Event Bus):                                   |
|  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                 |
|  Timer (100ms) --> imu_shared_read_accel()                        |
|                --> aic_event_publish_imu()  ----+                 |
|                --> direct UI update (backup)    |                 |
|                                                  v                |
|                                      +-------------------+        |
|                                      | Event Bus         |        |
|                                      | AIC_EVENT_IMU     |        |
|                                      +-------------------+        |
|                                                  |                |
|                                                  v                |
|                                      ex8_imu_event_cb()           |
|                                      --> update IMU labels        |
|                                      --> ex8_event_count++        |
|                                                                   |
|  (4) ADC Data Flow (Event Bus):                                   |
|  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                 |
|  Timer (100ms) --> aic_adc_read()                                 |
|                --> aic_event_publish_adc() -----+                 |
|                --> direct UI update (backup)    |                 |
|                                                 v                 |
|                                      +-------------------+        |
|                                      | Event Bus         |        |
|                                      | AIC_EVENT_ADC     |        |
|                                      +-------------------+        |
|                                                  |                |
|                                                  v                |
|                                      ex8_adc_event_cb()           |
|                                      --> update ADC label         |
|                                      --> ex8_event_count++        |
|                                                                   |
|  (5) IPC Stats Flow:                                              |
|  ~~~~~~~~~~~~~~~~~~~~                                             |
|  Timer (100ms) --> cm55_ipc_get_stats(&tx, &rx, &errors)          |
|                --> update stats label                             |
|                                                                   |
+===================================================================+
```

#### 4.5 Event Bus Pub/Sub ใน Part B

```
+===================================================================+
|                EVENT BUS PUB/SUB IN PART B                        |
+===================================================================+
|                                                                   |
|  PUBLISHERS (ใน Timer Callback)                                   |
|  ==============================                                   |
|                                                                   |
|  ex8_timer_cb() (100ms)                                           |
|     |                                                             |
|     |-- imu_shared_read_accel(&ax, &ay, &az)                      |
|     |   |                                                         |
|     |   +-- aic_event_publish_imu(ax, ay, az, 0, 0, 0)            |
|     |       (publishes AIC_EVENT_IMU_UPDATE)                      |
|     |                                                             |
|     |-- aic_adc_read(AIC_ADC_CH0)                                 |
|     |   |                                                         |
|     |   +-- aic_event_publish_adc(0, raw, mv)                     |
|     |       (publishes AIC_EVENT_ADC_UPDATE)                      |
|     |                                                             |
|                                                                   |
|  SUBSCRIBERS (registered ใน part4_ex8_advanced)                   |
|  ==============================================                   |
|                                                                   |
|  AIC_EVENT_IMU_UPDATE --> ex8_imu_event_cb()                      |
|     +-- update X/Y/Z labels                                       |
|     +-- ex8_event_count++                                         |
|                                                                   |
|  AIC_EVENT_ADC_UPDATE --> ex8_adc_event_cb()                      |
|     +-- update ADC label (raw + voltage)                          |
|     +-- ex8_event_count++                                         |
|                                                                   |
+===================================================================+
```

#### 4.6 Static Variables

```c
/*******************************************************************************
 * Part 4 - Example 8: Advanced Features
 *
 * Combines IPC + Event Bus + Logging + Real Hardware.
 * Full-featured dashboard showing all Part 4 concepts.
 ******************************************************************************/

/* LVGL Widget References */
static lv_obj_t *ex8_led_widgets[3];   /* LED indicator widgets (R, G, B) */
static lv_obj_t *ex8_imu_labels[3];    /* IMU X/Y/Z value labels */
static lv_obj_t *ex8_adc_label;        /* ADC display label */
static lv_obj_t *ex8_btn_label;        /* Button state label */
static lv_obj_t *ex8_event_label;      /* Event counter label */
static lv_obj_t *ex8_ipc_label;        /* IPC stats label */
static lv_timer_t *ex8_timer;          /* Main update timer */

/* State Variables */
static bool ex8_led_states[3] = {false, false, false};  /* LED ON/OFF */
static bool ex8_gpio_ready = false;    /* GPIO init success? */
static bool ex8_btn1_last = false;     /* Last button state (for edge detect) */
static uint32_t ex8_event_count = 0;   /* Total Event Bus events received */
```

#### 4.7 Event Bus Callbacks (Subscribers)

```c
/*******************************************************************************
 * IMU Event Callback
 *
 * เรียกโดย Event Bus เมื่อมี AIC_EVENT_IMU_UPDATE
 * แปลง raw int16 (milli-g) กลับเป็น float แล้วแสดง
 ******************************************************************************/
static void ex8_imu_event_cb(aic_event_t event,
                              const aic_event_data_t *data,
                              void *user_data)
{
    (void)event;
    (void)user_data;

    if (data != NULL) {
        /* data->imu.ax/ay/az เป็น int16_t ในหน่วย milli-g */
        /* แปลงกลับเป็น g ก่อนแสดง */
        lv_label_set_text_fmt(ex8_imu_labels[0], "%+.2f",
            (double)(data->imu.ax / 1000.0f));
        lv_label_set_text_fmt(ex8_imu_labels[1], "%+.2f",
            (double)(data->imu.ay / 1000.0f));
        lv_label_set_text_fmt(ex8_imu_labels[2], "%+.2f",
            (double)(data->imu.az / 1000.0f));
    }
    ex8_event_count++;   /* นับจำนวน events ที่ได้รับ */
}

/*******************************************************************************
 * ADC Event Callback
 *
 * เรียกโดย Event Bus เมื่อมี AIC_EVENT_ADC_UPDATE
 * แสดง raw value + voltage
 ******************************************************************************/
static void ex8_adc_event_cb(aic_event_t event,
                              const aic_event_data_t *data,
                              void *user_data)
{
    (void)event;
    (void)user_data;

    if (data != NULL) {
        lv_label_set_text_fmt(ex8_adc_label, "ADC: %d (%.2fV)",
            data->adc.raw_value,
            (double)(data->adc.voltage_mv / 1000.0f));
    }
    ex8_event_count++;
}
```

#### 4.8 LED Toggle Callback (IPC + GPIO + Logging)

```c
/*******************************************************************************
 * LED Toggle Button Callback
 *
 * เมื่อ user tap ปุ่ม L1/L2/L3:
 * 1. Toggle internal state
 * 2. Update LVGL LED widget
 * 3. Control real GPIO LED (ถ้ามี)
 * 4. Send IPC command ไป CM33 (ถ้า IPC ready)
 * 5. Log the action
 *
 * user_data = LED index (0, 1, 2) ส่งมาตอน add_event_cb
 ******************************************************************************/
static void ex8_led_cb(lv_event_t *e)
{
    /* ดึง LED index จาก user_data */
    int id = (int)(intptr_t)lv_event_get_user_data(e);
    if (id < 0 || id > 2) return;

    /* (1) Toggle state */
    ex8_led_states[id] = !ex8_led_states[id];

    /* (2) Update LVGL LED widget */
    if (ex8_led_states[id]) {
        lv_led_on(ex8_led_widgets[id]);
    } else {
        lv_led_off(ex8_led_widgets[id]);
    }

    /* (3) Direct GPIO control (ถ้า init สำเร็จ) */
    if (ex8_gpio_ready) {
        aic_gpio_led_set((aic_led_t)id, ex8_led_states[id]);
    }

    /* (4) IPC + (5) Logging */
    if (cm55_ipc_is_init()) {
        cm55_ipc_set_led((uint8_t)id, ex8_led_states[id]);
        CM55_LOGI("LED%d -> %s", id,
            ex8_led_states[id] ? "ON" : "OFF");
    }
}
```

#### 4.9 Timer Callback (Heart of the System)

```c
/*******************************************************************************
 * Main Timer Callback (100ms interval)
 *
 * ทำ 4 อย่างทุก cycle:
 * 1. Poll button (edge detection)
 * 2. Read IMU + publish event
 * 3. Read ADC + publish event
 * 4. Update IPC statistics
 ******************************************************************************/
static void ex8_timer_cb(lv_timer_t *timer)
{
    (void)timer;

    /* ===== (1) Button Polling with Edge Detection ===== */
    if (ex8_gpio_ready) {
        bool pressed = aic_gpio_button_read_raw(AIC_BTN_USER);

        /* เฉพาะตอนสถานะเปลี่ยน (edge) เท่านั้น */
        if (pressed != ex8_btn1_last) {
            lv_label_set_text(ex8_btn_label,
                pressed ? "BTN1: PRESSED" : "BTN1: Released");
            lv_obj_set_style_text_color(ex8_btn_label,
                pressed ? AIC_COLOR_SUCCESS : AIC_COLOR_TEXT_DIM, 0);

            if (cm55_ipc_is_init() && pressed) {
                CM55_LOGI("BTN1 pressed");
            }
            ex8_btn1_last = pressed;  /* บันทึกสถานะล่าสุด */
        }
    }

    /* ===== (2) Read IMU + Publish via Event Bus ===== */
    float ax, ay, az;
    if (imu_shared_read_accel(&ax, &ay, &az)) {
        /* Publish ผ่าน Event Bus (subscribers จะได้รับ) */
        aic_event_publish_imu(
            (int16_t)(ax * 1000),    /* แปลง g -> milli-g */
            (int16_t)(ay * 1000),
            (int16_t)(az * 1000),
            0, 0, 0);               /* gyro (ไม่ใช้) */

        /* Direct update (backup path) */
        lv_label_set_text_fmt(ex8_imu_labels[0], "%+.2f", (double)ax);
        lv_label_set_text_fmt(ex8_imu_labels[1], "%+.2f", (double)ay);
        lv_label_set_text_fmt(ex8_imu_labels[2], "%+.2f", (double)az);
    }

    /* ===== (3) Read ADC + Publish via Event Bus ===== */
    uint16_t adc_raw = aic_adc_read(AIC_ADC_CH0);
    uint16_t mv = (uint16_t)(adc_raw * 3300 / 4095);  /* Raw -> millivolts */
    aic_event_publish_adc(0, adc_raw, mv);

    /* Direct update (backup path) */
    lv_label_set_text_fmt(ex8_adc_label, "ADC: %d (%.2fV)",
        adc_raw, (double)(mv / 1000.0f));

    /* ===== (4) Update Statistics ===== */
    lv_label_set_text_fmt(ex8_event_label, "Events: %u",
        (unsigned)ex8_event_count);

    uint32_t tx, rx, errors;
    cm55_ipc_get_stats(&tx, &rx, &errors);
    lv_label_set_text_fmt(ex8_ipc_label, "IPC TX:%u RX:%u Err:%u",
        (unsigned)tx, (unsigned)rx, (unsigned)errors);
}
```

#### 4.10 Main Function (Full UI Build)

```c
/*******************************************************************************
 * Main Entry: part4_ex8_advanced()
 *
 * Initialization order ต้องถูกต้อง!
 * GPIO -> Sensors -> EventBus -> Logging -> UI -> Subscribe -> Timer
 ******************************************************************************/
void part4_ex8_advanced(void)
{
    /* ===== Step 1-4: Initialize Subsystems ===== */
    ex8_gpio_ready = aic_gpio_init();   /* GPIO (return bool) */
    aic_sensors_init();                  /* Sensors (ต้องก่อน ADC read!) */
    aic_event_init();                    /* Event Bus */
    aic_log_init();                      /* Logging */

    /* ===== Step 5: Build UI ===== */
    lv_obj_t *scr = lv_screen_active();
    aic_apply_dark_theme(scr);

    /* Main column container */
    lv_obj_t *cont = aic_col_create(scr);
    aic_full_size(cont);
    aic_pad(cont, 12);

    /* Title + Subtitle */
    lv_obj_t *title = lv_label_create(cont);
    lv_label_set_text(title, "Part 4 Ex8: Advanced");
    lv_obj_set_style_text_color(title, AIC_COLOR_TEXT, 0);
    lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);

    lv_obj_t *subtitle = lv_label_create(cont);
    lv_label_set_text(subtitle, "IPC + EventBus + Logging + HW");
    lv_obj_set_style_text_color(subtitle, AIC_COLOR_TEXT_DIM, 0);
    lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_12, 0);

    /* === LED Row (aic_row_create) === */
    lv_obj_t *led_row = aic_row_create(cont);
    lv_color_t led_colors[] = {
        lv_color_hex(0xFF4444),   /* Red */
        lv_color_hex(0x44FF44),   /* Green */
        lv_color_hex(0x4444FF)    /* Blue */
    };
    const char *led_names[] = {"L1", "L2", "L3"};

    for (int i = 0; i < 3; i++) {
        /* Card wrapper for each LED + button pair */
        lv_obj_t *wrap = lv_obj_create(led_row);
        lv_obj_set_size(wrap, 80, 60);
        lv_obj_set_style_bg_color(wrap, AIC_COLOR_BG_CARD, 0);
        lv_obj_set_style_border_width(wrap, 0, 0);
        lv_obj_set_style_radius(wrap, 6, 0);
        lv_obj_set_flex_flow(wrap, LV_FLEX_FLOW_ROW);
        lv_obj_set_flex_align(wrap,
            LV_FLEX_ALIGN_CENTER,
            LV_FLEX_ALIGN_CENTER,
            LV_FLEX_ALIGN_CENTER);
        lv_obj_set_style_pad_all(wrap, 4, 0);
        lv_obj_remove_flag(wrap, LV_OBJ_FLAG_SCROLLABLE);

        /* LED widget */
        ex8_led_widgets[i] = lv_led_create(wrap);
        lv_led_set_color(ex8_led_widgets[i], led_colors[i]);
        lv_obj_set_size(ex8_led_widgets[i], 24, 24);
        lv_led_off(ex8_led_widgets[i]);

        /* Toggle button */
        lv_obj_t *btn = lv_button_create(wrap);
        lv_obj_set_size(btn, 40, 28);
        lv_obj_set_style_bg_color(btn, AIC_COLOR_PRIMARY, 0);
        lv_obj_add_event_cb(btn, ex8_led_cb, LV_EVENT_CLICKED,
                            (void *)(intptr_t)i);

        lv_obj_t *lbl = lv_label_create(btn);
        lv_label_set_text(lbl, led_names[i]);
        lv_obj_set_style_text_color(lbl, AIC_COLOR_TEXT, 0);
        lv_obj_center(lbl);
    }

    /* Button state label */
    ex8_btn_label = lv_label_create(cont);
    lv_label_set_text(ex8_btn_label, "BTN1: Released");
    lv_obj_set_style_text_color(ex8_btn_label, AIC_COLOR_TEXT_DIM, 0);

    /* IMU via Event Bus (aic_xyz_display helper) */
    aic_xyz_display_create(cont, "Accel (EventBus)", ex8_imu_labels);

    /* ADC via Event Bus */
    ex8_adc_label = lv_label_create(cont);
    lv_label_set_text(ex8_adc_label, "ADC: --- (-.--V)");
    lv_obj_set_style_text_color(ex8_adc_label, AIC_COLOR_TEXT, 0);

    /* Stats row */
    lv_obj_t *stats_row = aic_row_create(cont);

    ex8_event_label = lv_label_create(stats_row);
    lv_label_set_text(ex8_event_label, "Events: 0");
    lv_obj_set_style_text_color(ex8_event_label, AIC_COLOR_PRIMARY, 0);
    lv_obj_set_style_text_font(ex8_event_label, &lv_font_montserrat_12, 0);

    ex8_ipc_label = lv_label_create(stats_row);
    lv_label_set_text(ex8_ipc_label, "IPC TX:0 RX:0 Err:0");
    lv_obj_set_style_text_color(ex8_ipc_label, AIC_COLOR_TEXT_DIM, 0);
    lv_obj_set_style_text_font(ex8_ipc_label, &lv_font_montserrat_12, 0);

    aic_create_footer(scr);

    /* ===== Step 6: Subscribe to Events ===== */
    aic_event_subscribe(AIC_EVENT_IMU_UPDATE, ex8_imu_event_cb, NULL);
    aic_event_subscribe(AIC_EVENT_ADC_UPDATE, ex8_adc_event_cb, NULL);

    /* ===== Step 7: Start Timer ===== */
    ex8_timer = lv_timer_create(ex8_timer_cb, 100, NULL);

    /* Log startup */
    if (cm55_ipc_is_init()) {
        CM55_LOGI("Week6 Ex8: Advanced Features started");
    }
    printf("[Week6] Ex8: Advanced Features started\n");
}
```

***

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

#### 5.1 Integration Checklist Pattern

```
+===================================================================+
|         SUBSYSTEM INTEGRATION CHECKLIST                           |
+===================================================================+
|                                                                   |
|  [ ] 1. Init Order ถูกต้อง?                                         |
|        GPIO -> Sensors -> EventBus -> Logging -> UI -> Timer      |
|                                                                   |
|  [ ] 2. ทุก subsystem init สำเร็จ?                                  |
|        ตรวจ return value (เช่น ex8_gpio_ready)                     |
|        ถ้า fail: graceful degradation (ข้าม feature นั้น)             |
|                                                                   |
|  [ ] 3. Event Bus subscribe หลัง UI สร้างเสร็จ?                      |
|        subscribe ก่อน UI = crash (NULL labels)                     |
|                                                                   |
|  [ ] 4. Timer สร้างเป็นอันสุดท้าย?                                     |
|        Timer ทำงานทันที -> ทุกอย่างต้องพร้อมก่อน                         |
|                                                                   |
|  [ ] 5. NULL checks ครบ?                                          |
|        if (ex8_gpio_ready) { ... }                                |
|        if (cm55_ipc_is_init()) { ... }                            |
|        if (data != NULL) { ... }                                  |
|                                                                   |
|  [ ] 6. Thread safety?                                            |
|        ทุก UI update อยู่ใน LVGL context (timer/callback)            |
|        ไม่ call LVGL จาก IPC task โดยตรง                           |
|                                                                   |
+===================================================================+
```

#### 5.2 Graceful Degradation Pattern

```c
/*******************************************************************************
 * Graceful Degradation: ระบบทำงานได้แม้บาง subsystem ล้มเหลว
 ******************************************************************************/

/* GPIO ไม่พร้อม? ข้าม hardware functions */
ex8_gpio_ready = aic_gpio_init();

/* ใน timer: ตรวจก่อนใช้ */
if (ex8_gpio_ready) {
    bool pressed = aic_gpio_button_read_raw(AIC_BTN_USER);
    aic_gpio_led_set(id, state);
}

/* IPC ไม่พร้อม? ข้าม IPC commands */
if (cm55_ipc_is_init()) {
    cm55_ipc_set_led(id, state);
    CM55_LOGI("LED%d -> %s", id, state ? "ON" : "OFF");
}

/* IMU ไม่พร้อม? shared read return false */
float ax, ay, az;
if (imu_shared_read_accel(&ax, &ay, &az)) {
    /* อ่านได้: publish + update */
} else {
    /* อ่านไม่ได้: ไม่ crash, แค่ไม่ update */
}
```

#### 5.3 Button Edge Detection

```c
/*******************************************************************************
 * ตรวจจับ "เปลี่ยนสถานะ" ไม่ใช่ "สถานะปัจจุบัน"
 ******************************************************************************/
static bool btn1_last = false;

void timer_cb(lv_timer_t *t) {
    bool pressed = aic_gpio_button_read_raw(AIC_BTN_USER);
    if (pressed != btn1_last) {   /* สถานะเปลี่ยน! */
        if (pressed) {
            /* Rising edge: กด */
            CM55_LOGI("BTN1 pressed"); /* Log เฉพาะตอนกด */
        }
        btn1_last = pressed;
    }
    /* สถานะเหมือนเดิม -> ไม่ทำอะไร (ไม่ log ซ้ำ) */
}
```

#### 5.4 Event Count as System Health Metric

```c
/*******************************************************************************
 * ใช้ event count เป็น system health indicator
 ******************************************************************************/

/* ค่าที่ควรเห็น (ที่ 100ms timer):
 * - 10 IMU events/sec + 10 ADC events/sec = ~20 events/sec
 * - ถ้าน้อยกว่า: sensor อ่านไม่ได้ หรือ Event Bus มีปัญหา
 * - ถ้ามากกว่า: ปกติ (subscriber อื่นอาจ publish event เพิ่ม)
 */

static uint32_t ex8_event_count = 0;

/* ใน subscriber callback: */
ex8_event_count++;

/* ใน timer: แสดง */
lv_label_set_text_fmt(ex8_event_label, "Events: %u",
    (unsigned)ex8_event_count);
```

#### 5.5 Design Patterns ที่เกี่ยวข้อง

```
Event Bus (Lab นี้)
    |
    +-- Observer Pattern
    |   +-- One publisher -> Many subscribers
    |
    +-- Mediator Pattern
    |   +-- Event Bus เป็น mediator ระหว่าง modules
    |
    +-- State Pattern
    |   +-- State machine ขับเคลื่อนด้วย events
    |
    +-- Command Pattern
        +-- IPC commands เป็น command objects
```

***

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

#### Exercise 1: Event Counter Dashboard (Bar Chart) -- Part A

สร้าง dashboard แสดง bar chart ของจำนวน event แต่ละ type:

* แกน X: EVENT\_IMU, EVENT\_ADC, EVENT\_BUTTON, EVENT\_TIMER, EVENT\_THRESHOLD
* แกน Y: จำนวน events
* อัปเดตทุก 1 วินาที
* แสดง event rate (events/sec) สำหรับแต่ละ type

**Hints:**

```c
/* สร้าง Chart type BAR */
lv_obj_t *chart_bar = lv_chart_create(scr);
lv_chart_set_type(chart_bar, LV_CHART_TYPE_BAR);
lv_chart_set_point_count(chart_bar, 5);  /* 5 event types */

lv_chart_series_t *ser_bar = lv_chart_add_series(chart_bar,
    lv_palette_main(LV_PALETTE_CYAN), LV_CHART_AXIS_PRIMARY_Y);

/* อัปเดตใน timer */
void update_bar_chart(void) {
    lv_chart_set_value_by_id(chart_bar, ser_bar, 0, event_counts[EVENT_IMU]);
    lv_chart_set_value_by_id(chart_bar, ser_bar, 1, event_counts[EVENT_ADC]);
    lv_chart_set_value_by_id(chart_bar, ser_bar, 2, event_counts[EVENT_BUTTON]);
    lv_chart_set_value_by_id(chart_bar, ser_bar, 3, event_counts[EVENT_TIMER]);
    lv_chart_set_value_by_id(chart_bar, ser_bar, 4, event_counts[EVENT_THRESHOLD]);
    lv_chart_refresh(chart_bar);
}
```

#### Exercise 2: Event-Driven State Machine -- Part A

สร้าง state machine ที่มี 3 states:

* **IDLE**: ปกติ (สีเขียว) -- เมื่อได้ EVENT\_BUTTON --> ไป ACTIVE
* **ACTIVE**: กำลังทำงาน (สีเหลือง) -- เมื่อได้ EVENT\_THRESHOLD --> ไป ALERT
* **ALERT**: แจ้งเตือน (สีแดง กระพริบ) -- เมื่อได้ EVENT\_BUTTON --> กลับ IDLE

```
State Diagram:
                 EVENT_BUTTON
    +-------+ ----------------> +--------+
    | IDLE  |                   |ACTIVE  |
    |(green)| <---------------- |(yellow)|
    +-------+   EVENT_BUTTON   +---+-----+
        ^                          |
        |     EVENT_BUTTON         | EVENT_THRESHOLD
        |                          v
        |                    +--------+
        +------------------- | ALERT  |
                             | (red)  |
                             +--------+
```

**Hints:**

```c
typedef enum {
    STATE_IDLE = 0,
    STATE_ACTIVE,
    STATE_ALERT
} app_state_t;

static app_state_t current_state = STATE_IDLE;
static const char *state_names[] = { "IDLE", "ACTIVE", "ALERT" };
static const uint32_t state_colors[] = { 0x7ee787, 0xd29922, 0xf85149 };

static void state_machine_handler(const event_t *event, void *user_data);
```

#### Exercise 3: Sensor Threshold Alerts -- Part B

เพิ่มระบบ alert เมื่อค่า sensor เกิน threshold:

**Requirements:**

* เพิ่ม label "Alert: NONE" ใต้ ADC label
* ADC > 80% (raw > 3276): "Alert: HIGH VOLTAGE" สีแดง + LED1 on
* IMU |accel| > 15 m/s^2: "Alert: VIBRATION" สีส้ม + LED2 on
* ค่ากลับปกติ: "Alert: NONE" สีเขียว + LED off
* ส่ง `CM55_LOGW("ALERT: ...")` เมื่อ alert เปลี่ยนสถานะ (edge detect)

**Hints:**

* ใช้ `static bool alert_active = false;` สำหรับ edge detect (log เฉพาะตอนเปลี่ยน)
* Check thresholds ใน `ex8_timer_cb()` หลัง sensor read

```c
static lv_obj_t *ex8_alert_label;
static bool alert_high_voltage = false;

/* เพิ่มใน timer callback: */
bool adc_high = (adc_raw > 3276);
if (adc_high != alert_high_voltage) {
    alert_high_voltage = adc_high;
    if (adc_high) {
        lv_label_set_text(ex8_alert_label, "Alert: HIGH VOLTAGE");
        lv_obj_set_style_text_color(ex8_alert_label, AIC_COLOR_ERROR, 0);
        if (cm55_ipc_is_init()) CM55_LOGW("ALERT: HIGH VOLTAGE");
    } else {
        lv_label_set_text(ex8_alert_label, "Alert: NONE");
        lv_obj_set_style_text_color(ex8_alert_label, AIC_COLOR_SUCCESS, 0);
    }
}
```

#### Exercise 4: IPC Health Card -- Part B

เพิ่ม IPC statistics card:

**Requirements:**

* TX Rate / RX Rate: messages/sec (delta per second)
* Error Rate: % (errors / total \* 100)
* Status LED: green (OK) / red (error > 5%)
* Uptime: seconds since start
* ถ้า error > 5%: `CM55_LOGW("IPC health degraded")`

**Hints:**

* เก็บ `last_tx, last_rx` สำหรับ rate calculation
* สร้าง timer แยก 1 วินาที สำหรับ rate update

```c
static uint32_t rate_last_tx = 0, rate_last_rx = 0;

static void rate_timer_cb(lv_timer_t *timer) {
    uint32_t tx, rx, errors;
    cm55_ipc_get_stats(&tx, &rx, &errors);

    uint32_t tx_rate = tx - rate_last_tx;
    uint32_t rx_rate = rx - rate_last_rx;
    rate_last_tx = tx;
    rate_last_rx = rx;

    lv_label_set_text_fmt(tx_rate_label, "TX: %u/s", (unsigned)tx_rate);
    lv_label_set_text_fmt(rx_rate_label, "RX: %u/s", (unsigned)rx_rate);
}

/* สร้าง timer 1 วินาที */
lv_timer_create(rate_timer_cb, 1000, NULL);
```

***

### 7. Quick Reference

#### Event Bus API (Part A)

```c
event_bus_init();                                /* Initialize */
event_subscribe(EVENT_IMU, handler, user_data);  /* Subscribe */
event_unsubscribe(EVENT_IMU, index);             /* Unsubscribe */
event_publish(EVENT_IMU, value, data);           /* Publish */

typedef struct {
    event_type_t type;       /* EVENT_IMU, EVENT_BUTTON, ... */
    uint32_t     timestamp;  /* lv_tick_get() at publish time */
    int32_t      value;      /* Numeric payload */
    void         *data;      /* Pointer payload (optional) */
} event_t;

void my_handler(const event_t *event, void *user_data) {
    /* ทำงานใน LVGL context - เรียก lv_xxx() ได้ */
}

/* IPC -> Event Bus Bridge Pattern */
/* 1. IPC callback: ตั้ง flag (IPC Task) */
/* 2. Bridge timer: อ่าน flag -> event_publish() (LVGL Task) */
/* 3. Event Bus: dispatch to all subscribers */
```

#### AIC Event Bus API (Part B)

| Function                                        | Description             |
| ----------------------------------------------- | ----------------------- |
| `aic_event_init()`                              | Initialize Event Bus    |
| `aic_event_subscribe(event, cb, user_data)`     | Subscribe to event type |
| `aic_event_publish_imu(ax, ay, az, gx, gy, gz)` | Publish IMU data event  |
| `aic_event_publish_adc(ch, raw, mv)`            | Publish ADC data event  |

#### Layout Helper Functions (Part B)

| Function                                        | Description                   |
| ----------------------------------------------- | ----------------------------- |
| `aic_col_create(parent)`                        | สร้าง Flex Column container   |
| `aic_row_create(parent)`                        | สร้าง Flex Row container      |
| `aic_full_size(obj)`                            | ขยาย 100% x 100%              |
| `aic_pad(obj, px)`                              | ตั้ง padding ทุกด้าน          |
| `aic_apply_dark_theme(scr)`                     | ตั้ง dark background + colors |
| `aic_xyz_display_create(par, title, labels[3])` | สร้าง XYZ display panel       |
| `aic_create_footer(scr)`                        | สร้าง footer bar              |

#### IPC + Hardware Functions (Part B)

| Function                               | Description                         |
| -------------------------------------- | ----------------------------------- |
| `cm55_ipc_is_init()`                   | ตรวจว่า IPC พร้อมหรือยัง            |
| `cm55_ipc_set_led(id, state)`          | ส่งคำสั่ง LED ไป CM33               |
| `cm55_ipc_get_stats(&tx, &rx, &err)`   | ดึง IPC statistics                  |
| `CM55_LOGI(fmt, ...)`                  | Send INFO log to CM33 via IPC       |
| `aic_gpio_init()`                      | Init GPIO, return bool (success)    |
| `aic_gpio_led_set(led, state)`         | Set LED ON/OFF                      |
| `aic_gpio_button_read_raw(btn)`        | Read button state (true=pressed)    |
| `aic_sensors_init()`                   | Init sensor subsystem               |
| `imu_shared_read_accel(&ax, &ay, &az)` | Read accelerometer (float, g units) |
| `aic_adc_read(channel)`                | Read ADC (uint16\_t, 0-4095)        |

#### Architecture Principles

```
+===================================================================+
|           ARCHITECTURE PRINCIPLES                                 |
+===================================================================+
|                                                                   |
|  1. PROPER INIT ORDER                                             |
|     GPIO -> Sensors -> EventBus -> Logging -> UI -> Timer         |
|                                                                   |
|  2. GRACEFUL DEGRADATION                                          |
|     ถ้า subsystem fail -> ข้าม feature นั้น, ไม่ crash                 |
|                                                                   |
|  3. EVENT-DRIVEN DECOUPLING                                       |
|     Publisher ไม่รู้จัก subscriber (และกลับกัน)                         |
|     Timer -> publish -> Event Bus -> subscribers -> UI            |
|                                                                   |
|  4. LAYOUT HELPERS FOR PRODUCTIVITY                               |
|     Flex layout แทน manual pixel positioning                      |
|     aic_col + aic_row = responsive + maintainable                 |
|                                                                   |
+===================================================================+
```

#### Application ในงาน Industrial

| Use Case                | คำอธิบาย                                            |
| ----------------------- | --------------------------------------------------- |
| **SCADA System**        | Event-driven monitoring ของโรงงานทั้งหมด            |
| **PLC Programming**     | State machine สำหรับควบคุมกระบวนการผลิต             |
| **Alarm Management**    | จัดการ alarm ตามมาตรฐาน ISA-18.2                    |
| **Industrial IoT**      | Edge computing ที่ประมวลผล event ก่อนส่งขึ้น cloud  |
| **Automotive ECU**      | Event-driven architecture สำหรับ ADAS, Body Control |
| **Building Automation** | BACnet/BMS event management สำหรับ HVAC, Lighting   |
| **Medical Device**      | Monitor: vitals + pump control + alarm + audit log  |
