> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/sensor-to-hmi-display/hardware-interfacing-workshops/imu-display.md).

# IMU Display

## Lab 7: Real IMU Display

### Part 2 - Sensor Visualization

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

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

* **Real Data**: เปลี่ยนจาก simulation เป็นข้อมูลจาก sensor จริง (BMI270 Accelerometer)
* **IPC Architecture**: เข้าใจการส่งข้อมูลระหว่าง CM33 (sensor core) กับ CM55 (display core)
* **API Transition**: เรียนรู้วิธีแก้โค้ดจาก simulation เป็น hardware โดยแก้ไขน้อยที่สุด
* **Error Handling**: จัดการกรณี sensor ยังไม่พร้อมอ่านค่า

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

1. **Sensor API**: ใช้ `imu_shared_read_accel()` อ่านค่า accelerometer ผ่าน shared memory
2. **Hardware Init**: เรียก `aic_sensors_init()` ก่อนใช้ sensor API
3. **IPC Architecture**: CM33 อ่าน BMI270 ผ่าน I2C → เขียน Shared Memory → CM55 อ่านแสดงผล
4. **Error Handling**: ตรวจสอบ return value ก่อนใช้ข้อมูล
5. **Debug Throttling**: พิมพ์ debug message แบบ throttled ไม่ให้ console ท่วม

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

1. เพิ่ม `aic_sensors_init()` ก่อนสร้าง UI
2. ใน timer callback เปลี่ยนจาก `simulate_imu_accel()` เป็น `imu_shared_read_accel()`
3. เพิ่ม error check: ถ้า return false ให้ skip update
4. UI code เหมือนกับที่ทำบน PC Simulator (Lab 3)
5. เพิ่ม debug print แบบ throttled

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2F9Nw7gR2yHVoF1EoyyQDv%2Fweek4_ex3_chart_timeseries.gif?alt=media&amp;token=4fb3bc6f-f4d8-4c25-b46a-2518fa51f345" alt=""><figcaption></figcaption></figure>

***

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

#### 2.1 CM33-CM55 IPC Architecture

PSoC Edge E84 มี 2 cores ทำงานร่วมกัน: CM33 จัดการ hardware (sensor, GPIO, WiFi) และ CM55 จัดการ display (LVGL). การส่งข้อมูลระหว่าง core ใช้ Shared Memory.

```
┌─────────────────────────────────────────────────────────────┐
│               CM33-CM55 COMMUNICATION                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌──────────────────┐                 ┌──────────────────┐ │
│   │     CM33-NS      │                 │       CM55       │ │
│   │  (Sensor Core)   │                 │   (LVGL Core)    │ │
│   ├──────────────────┤                 ├──────────────────┤ │
│   │  BMI270 Driver   │                 │  LVGL Graphics   │ │
│   │  - I2C Comm      │                 │  - Chart Widget  │ │
│   │  - 100Hz polling │                 │  - Timer 100ms   │ │
│   │  - Raw Data Read │                 │  - Label Update  │ │
│   └────────┬─────────┘                 └────────▲─────────┘ │
│            │                                    │           │
│            │ Write                         Read │           │
│            ▼                                    │           │
│   ┌──────────────────────────────────────────────┐          │
│   │         Shared Memory (0x261C0000)           │          │
│   │  ┌──────────────────────────────────────┐    │          │
│   │  │ update_count | ax | ay | az | gx |...│    │          │
│   │  └──────────────────────────────────────┘    │          │
│   │  * CM33 writes continuously                  │          │
│   │  * CM55 reads on timer callback              │          │
│   └──────────────────────────────────────────────┘          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 Simulation vs Hardware - ความแตกต่าง

```
┌─────────────────────────────────────────────────────────────┐
│          PC SIMULATOR (Lab 3)                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Timer (100ms)                                             │
│       │                                                     │
│       ▼                                                     │
│   simulate_imu_accel(&ax, &ay, &az)  ← ค่าจำลอง sin/cos      │
│       │                                                     │
│       ▼                                                     │
│   lv_chart_set_next_value(chart, ser, ax * 100)             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│          HARDWARE (Lab 7 - นี่แหละ!)                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   aic_sensors_init()     ← เรียกครั้งเดียวตอนเริ่มต้น              │
│                                                             │
│   Timer (100ms)                                             │
│       │                                                     │
│       ▼                                                     │
│   imu_shared_read_accel(&ax, &ay, &az)  ← ค่าจาก BMI270      │
│       │                                                     │
│       ├── return false → return;  (sensor ยังไม่พร้อม)         │
│       │                                                     │
│       ▼  return true                                        │
│   lv_chart_set_next_value(chart, ser, ax * 100)             │
│                                                             │
│   สังเกต: UI code เหมือนกัน! แค่เปลี่ยน data source               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.3 UI Layout

```
┌─────────────────────────────────────────────────────────────┐
│      "Part 2 Ex7: Real IMU Display"                         │
├─────────────────────────────────────────────────────────────┤
│      "Accelerometer - REAL BMI270 DATA"                     │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                  LINE CHART (440x230)                 │  │
│  │                                                       │  │
│  │   3 Series:                                           │  │
│  │   --- X (Red)   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~          │  │
│  │   --- Y (Green) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~          │  │
│  │   --- Z (Blue)  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~          │  │
│  │                                                       │  │
│  │   50 data points                                      │  │
│  │   Y range: -1500 to +1500 (for +/-15 m/s2 x 100)      │  │
│  │                                                       │  │
│  └───────────────────────────────────────────────────────┘  │
│                                          X: +0.50           │
│                                          Y: -0.30           │
│                                          Z: +9.81           │
│      "[HW] Using imu_shared_read_accel()"                   │
└─────────────────────────────────────────────────────────────┘
```

***

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

#### 3.1 Sensor API Functions

<table><thead><tr><th width="341.6988525390625">Function</th><th width="244.5198974609375">Description</th><th>Return</th></tr></thead><tbody><tr><td><code>aic_sensors_init()</code></td><td>Initialize sensor subsystem (เรียกครั้งเดียว)</td><td>void</td></tr><tr><td><code>imu_shared_read_accel(&#x26;ax, &#x26;ay, &#x26;az)</code></td><td>อ่าน accelerometer ผ่าน shared memory</td><td>bool (true=valid)</td></tr><tr><td><code>imu_shared_read_gyro(&#x26;gx, &#x26;gy, &#x26;gz)</code></td><td>อ่าน gyroscope ผ่าน shared memory</td><td>bool (true=valid)</td></tr></tbody></table>

#### 3.2 API Signature

```c
/* อ่านค่า accelerometer จาก shared memory
 * ค่าที่ได้เป็น m/s^2
 * return: true = ข้อมูลใหม่พร้อมใช้, false = sensor ยังไม่พร้อม
 */
bool imu_shared_read_accel(float *ax, float *ay, float *az);
```

#### 3.3 Key Differences: Sim vs HW

<table><thead><tr><th width="195.07598876953125">รายการ</th><th>PC Simulator</th><th>Hardware (PSoC Edge)</th></tr></thead><tbody><tr><td>Init</td><td>ไม่ต้อง</td><td><code>aic_sensors_init()</code></td></tr><tr><td>Read</td><td><code>simulate_imu_accel()</code></td><td><code>imu_shared_read_accel()</code></td></tr><tr><td>Error</td><td>Always returns (ค่าจำลอง)</td><td>อาจ return false</td></tr><tr><td>Data Range</td><td>Predictable (sin/cos)</td><td>Real noise + gravity</td></tr><tr><td>Z-axis</td><td>~9.81 constant</td><td>~9.81 + noise</td></tr><tr><td>Build</td><td>CMake + GCC (PC)</td><td>ModusToolbox + GCC_ARM</td></tr></tbody></table>

#### 3.4 Include Files

```c
/* สำหรับ hardware build */
#include "aic-eec/aic-eec.h"       /* aic_sensors_init() */
#include "aic-eec/sensors.h"        /* sensor type definitions */
#include "../../shared/imu_shared.h" /* imu_shared_read_accel() */
```

***

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

#### 4.1 Global Variables

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

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

#define CHART_POINTS  50

/* Chart widgets */
static lv_obj_t * ex7_chart;
static lv_chart_series_t * ex7_ser_x;
static lv_chart_series_t * ex7_ser_y;
static lv_chart_series_t * ex7_ser_z;
static lv_obj_t * ex7_labels[3];
static lv_timer_t * ex7_timer;
```

#### 4.2 Timer Callback - อ่านค่า IMU จริง

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

    float ax, ay, az;

#ifdef CYBSP_ENABLED
    /* ===== HARDWARE: อ่านค่าจาก BMI270 จริง ===== */
    if(!imu_shared_read_accel(&ax, &ay, &az)) {
        return;  /* IMU ยังไม่พร้อม - ข้ามรอบนี้ */
    }
#else
    /* ===== PC SIMULATOR: ใช้ค่าจำลอง ===== */
    static float t = 0.0f;
    t += 0.05f;
    ax = 0.5f * sinf(t);
    ay = 0.3f * cosf(t * 0.7f);
    az = 9.81f + 0.2f * sinf(t * 2.0f);
#endif

    /* ===== Scale & Update Chart ===== */
    int32_t x_val = (int32_t)(ax * 100);
    int32_t y_val = (int32_t)(ay * 100);
    int32_t z_val = (int32_t)(az * 100);

    lv_chart_set_next_value(ex7_chart, ex7_ser_x, x_val);
    lv_chart_set_next_value(ex7_chart, ex7_ser_y, y_val);
    lv_chart_set_next_value(ex7_chart, ex7_ser_z, z_val);

    /* ===== Update Labels ===== */
    lv_label_set_text_fmt(ex7_labels[0], "X: %+.2f", (double)ax);
    lv_label_set_text_fmt(ex7_labels[1], "Y: %+.2f", (double)ay);
    lv_label_set_text_fmt(ex7_labels[2], "Z: %+.2f", (double)az);

    /* ===== Debug Print (throttled) ===== */
    static uint32_t count = 0;
    count++;
    if(count % 10 == 0) {  /* ทุก 1 วินาที (10 x 100ms) */
        printf("[IMU] X:%+.2f Y:%+.2f Z:%+.2f\r\n",
               (double)ax, (double)ay, (double)az);
    }
}
```

#### 4.3 Main Function

```c
void part2_ex7_real_imu_display(void)
{
    /* ===== HARDWARE INITIALIZATION ===== */
#ifdef CYBSP_ENABLED
    aic_sensors_init();  /* Initialize BMI270 IMU */
#endif

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

    /* ===== Title ===== */
    lv_obj_t * title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "Part 2 Ex7: Real IMU Display");
    lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
    lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);

    /* Subtitle - บอกว่าใช้ data จริง */
    lv_obj_t * subtitle = lv_label_create(lv_screen_active());
    lv_label_set_text(subtitle, "Accelerometer - REAL BMI270 DATA");
    lv_obj_set_style_text_color(subtitle, lv_color_hex(0x00FF88), 0);
    lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_14, 0);
    lv_obj_align(subtitle, LV_ALIGN_TOP_MID, 0, 25);

    /* ===== Chart ===== */
    ex7_chart = lv_chart_create(lv_screen_active());
    lv_obj_set_size(ex7_chart, 440, 230);
    lv_obj_align(ex7_chart, LV_ALIGN_CENTER, -10, 15);
    lv_chart_set_type(ex7_chart, LV_CHART_TYPE_LINE);
    lv_chart_set_point_count(ex7_chart, CHART_POINTS);

    /* Range: -1500 to 1500 (สำหรับ +/-15 m/s2 x 100) */
    lv_chart_set_range(ex7_chart, LV_CHART_AXIS_PRIMARY_Y, -1500, 1500);

    /* ซ่อน point indicator */
    lv_obj_set_style_size(ex7_chart, 0, 0, LV_PART_INDICATOR);
    lv_obj_set_style_line_width(ex7_chart, 2, LV_PART_ITEMS);
    lv_chart_set_div_line_count(ex7_chart, 5, 8);

    /* สีพื้นหลัง chart */
    lv_obj_set_style_bg_color(ex7_chart, lv_color_hex(0x1a1a2e), 0);
    lv_obj_set_style_bg_opa(ex7_chart, LV_OPA_COVER, 0);
    lv_obj_set_style_border_color(ex7_chart, lv_color_hex(0x333355), 0);

    /* ===== Series: X(Red), Y(Green), Z(Blue) ===== */
    lv_color_t colors[] = {
        lv_palette_main(LV_PALETTE_RED),
        lv_palette_main(LV_PALETTE_GREEN),
        lv_palette_main(LV_PALETTE_BLUE)
    };

    ex7_ser_x = lv_chart_add_series(ex7_chart, colors[0],
                                     LV_CHART_AXIS_PRIMARY_Y);
    ex7_ser_y = lv_chart_add_series(ex7_chart, colors[1],
                                     LV_CHART_AXIS_PRIMARY_Y);
    ex7_ser_z = lv_chart_add_series(ex7_chart, colors[2],
                                     LV_CHART_AXIS_PRIMARY_Y);

    /* ===== Value Labels ===== */
    const char * names[] = {"X:", "Y:", "Z:"};
    for(int i = 0; i < 3; i++) {
        ex7_labels[i] = lv_label_create(lv_screen_active());
        lv_label_set_text_fmt(ex7_labels[i], "%s 0.00", names[i]);
        lv_obj_set_style_text_color(ex7_labels[i], colors[i], 0);
        lv_obj_set_style_text_font(ex7_labels[i], &lv_font_montserrat_16, 0);
        lv_obj_align(ex7_labels[i], LV_ALIGN_RIGHT_MID, -5, -30 + i * 30);
    }

    /* ===== Footer Label ===== */
    lv_obj_t * footer = lv_label_create(lv_screen_active());
    lv_label_set_text(footer, "[HW] Using imu_shared_read_accel()");
    lv_obj_set_style_text_color(footer, lv_color_hex(0x888888), 0);
    lv_obj_set_style_text_font(footer, &lv_font_montserrat_12, 0);
    lv_obj_align(footer, LV_ALIGN_BOTTOM_MID, 0, -5);

    /* ===== Timer - Polling ทุก 50ms (20 Hz) ===== */
    ex7_timer = lv_timer_create(ex7_timer_cb, 50, NULL);
}
```

***

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

#### 5.1 Error Handling Pattern - ต้องตรวจสอบเสมอ

```c
/* Pattern: ตรวจ return value ก่อนใช้ข้อมูล */
float ax, ay, az;
if(!imu_shared_read_accel(&ax, &ay, &az)) {
    /* sensor ยังไม่พร้อม - 3 ทางเลือก: */

    /* ทางเลือก 1: Skip update */
    return;

    /* ทางเลือก 2: ใช้ค่าล่าสุด (ไม่ทำอะไร เพราะ labels ยังแสดงค่าเดิม) */

    /* ทางเลือก 3: แสดง status */
    lv_label_set_text(status_label, "Waiting for sensor...");
    return;
}
/* ถ้ามาถึงบรรทัดนี้ ax, ay, az ใช้ได้อย่างปลอดภัย */
```

#### 5.2 Debug Print Throttling

```c
/* Problem: printf ทุก 50ms = 20 บรรทัดต่อวินาที → console ท่วม */
/* Solution: พิมพ์ทุก N ครั้ง */

static uint32_t count = 0;
count++;

/* ทุก 1 วินาที (20 x 50ms = 1s) */
if(count % 20 == 0) {
    printf("[IMU] X:%+.2f Y:%+.2f Z:%+.2f\r\n",
           (double)ax, (double)ay, (double)az);
}

/* หรือใช้ modulo 10 สำหรับ 100ms timer → ทุก 1 วินาที */
```

#### 5.3 Shared Memory Debug

```c
/* ตรวจสอบว่า CM33 ส่งข้อมูลมาจริง */
#include "../../shared/imu_shared.h"

static void debug_ipc_status(void)
{
    /* ดู update count ของ CM33 */
    extern volatile imu_shared_data_t imu_shared;
    uint32_t cm33_count = imu_shared.update_count;

    static uint32_t last_count = 0;
    if(cm33_count == last_count) {
        printf("[WARN] CM33 not updating IMU data!\r\n");
    }
    last_count = cm33_count;
}
```

#### 5.4 Polling Rate Considerations

```c
/* เลือก polling rate ที่เหมาะสม */

/* 50ms (20 Hz) - แนะนำสำหรับ real-time display */
lv_timer_create(timer_cb, 50, NULL);
/* เหตุผล: BMI270 output rate ~100Hz, 20Hz display เพียงพอ */
/* CPU load ต่ำกว่า, LVGL ไม่ต้อง redraw บ่อย */

/* 100ms (10 Hz) - เหมาะสำหรับ slow-changing data */
lv_timer_create(timer_cb, 100, NULL);
/* ประหยัด CPU มากขึ้น แต่ motion อาจดูไม่ smooth */

/* 20ms (50 Hz) - สำหรับ high-speed motion tracking */
lv_timer_create(timer_cb, 20, NULL);
/* CPU load สูง, chart update เร็วมาก */
```

#### 5.5 Chart Range for Real IMU Data

```c
/* BMI270 accelerometer range: +/-16g (default +/-2g) */
/* 1g = 9.81 m/s^2 */

/* สำหรับ +/-2g range (default): */
/* Max value: 2 * 9.81 = 19.62 m/s^2 */
/* Scaled (x100): 1962 */
/* Chart range: -2000 to 2000 (ปลอดภัย) */

lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -2000, 2000);

/* สำหรับแสดง gravity (Z-axis ~981):  */
/* Range: -1500 to 1500 (เห็น Z oscillate around 981) */
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -1500, 1500);
```

***

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

#### Exercise 1: Tilt Angle Display (Arc Gauge)

**โจทย์:** คำนวณมุมเอียง (Tilt Angle) จากข้อมูล accelerometer และแสดงเป็น Arc gauge

**หลักการ:**

```
Roll  = atan2(ay, az) * 180 / PI    (มุมเอียงซ้าย-ขวา)
Pitch = atan2(-ax, sqrt(ay*ay + az*az)) * 180 / PI  (มุมเอียงหน้า-หลัง)
```

**Requirements:**

* เพิ่ม Arc gauge 2 วง: Roll และ Pitch
* Range: -90 ถึง +90 องศา (map เป็น 0-180 สำหรับ arc)
* แสดงค่ามุมเป็นตัวเลขใต้ arc
* สี: Roll = แดง, Pitch = น้ำเงิน

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

* ระบบ Anti-Tipping ของรถ Forklift ในโรงงาน
* ระบบเตือนเมื่อโครงสร้างอาคารเอียงเกินกำหนด
* Drone flight controller แสดงมุมบิน

**Hint:**

* ใช้ `atan2f()` จาก `<math.h>`
* `M_PI` อาจไม่มีใน embedded → ใช้ `3.14159265f`
* Arc range: `lv_arc_set_range(arc, 0, 180)` → ค่ากลาง 90 = ระดับ

***

#### Exercise 2: Motion Detection Alarm

**โจทย์:** สร้างระบบตรวจจับการสั่นสะเทือน (Vibration Detection) พร้อมระบบเตือน:

**Requirements:**

* คำนวณ magnitude: `mag = sqrt(ax^2 + ay^2 + az^2)`
* ตั้ง threshold 3 ระดับ:
  * Normal: mag = 9.5-10.1 (สีเขียว)
  * Warning: mag < 9.5 หรือ mag > 10.1 (สีเหลือง)
  * Alarm: mag < 8.0 หรือ mag > 12.0 (สีแดง กระพริบ)
* แสดง LED indicator บนหน้าจอที่เปลี่ยนสีตามระดับ
* เก็บจำนวน alarm events แสดงเป็นตัวเลข
* แสดง magnitude ปัจจุบันและค่า peak

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

* ระบบ Vibration Monitoring ของเครื่องจักรในโรงงาน (Predictive Maintenance)
* ระบบเตือนแผ่นดินไหวพื้นฐาน (Seismic Alert)
* ตรวจสอบความสมบูรณ์ของโครงสร้าง (Structural Health Monitoring)

**Hint:**

* `sqrtf()` จาก `<math.h>`
* LED กระพริบ: ใช้ `lv_led_toggle()` เมื่อ alarm
* Peak tracking: `if(mag > peak) peak = mag;`

***

### 7. References

* [LVGL Chart](https://docs.lvgl.io/9.2/widgets/chart.html)
* [PSoC Edge BMI270 IMU](https://www.infineon.com/cms/en/product/sensor/imu-sensor/)
* [Inter-Processor Communication](https://www.infineon.com/dgdl/Infineon-AN235302...)

***
