# 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="/files/LnVCMT8WhBTvurKcI4N7" 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...)

***


---

# Agent Instructions: Querying This Documentation

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

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

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/sensor-to-hmi-display/hardware-interfacing-workshops/imu-display.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
