> 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  |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/multi-core-communication/event-bus.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
