SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches

Functions

bool ipc_sensorhub_init (void)
 การลงทะเบียน callback ของ pipe — ลำดับที่สองใน boot prologue
void ipc_sensorhub_snapshot (sensorhub_snapshot_t *snap)
 ต่อหนึ่ง tick มีผู้อ่านได้รายเดียว — การอ่านจะเคลียร์แฟล็ก changed
bool ipc_sensorhub_wifi_connected (void)
 สถานะ WiFi ที่ได้จากการวนถาม — ต้อง edge-detect ก่อนแตะ LVGL
bool ipc_sensorhub_ble_connected (void)
 คืน true เฉพาะขณะที่ NUS host อยู่ในสถานะ CONNECTED เท่านั้น; ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ
bool ipc_sensorhub_ntp_synced (void)
 เป็น gate ของการอ่าน RTC ทั้งหมด
bool ipc_sensorhub_get_time_str (char *buf, size_t buf_size)
 สตริงเวลาล่าสุดที่ CM33 ส่งมา; คืน false ก่อน NTP sync
void ipc_sensorhub_feed_capsense (const ipc_sensor_capsense_t *data)
 การป้อนตัวอย่างค่า CapSense ที่อ่านในเครื่อง — ต้องกำหนดค่าเริ่มต้นครบทุกฟิลด์ และใช้ sequence ร่วมกัน
void ipc_sensorhub_feed_pot (const ipc_sensor_pot_t *data)
 ค่าของ pot ที่ปรับเป็นค่ามาตรฐานแล้ว ไม่ใช่ค่านับจาก ADC; ต้องตรวจ pot_channels_ready() ก่อน
void ipc_sensorhub_feed_bmi270 (const ipc_sensor_bmi270_t *data)
 การป้อนตัวอย่างค่า IMU ที่อ่านในเครื่อง; ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ
void ipc_sensorhub_feed_bmm350 (const ipc_sensor_bmm350_t *data)
 การป้อนตัวอย่างค่าแมกนีโตมิเตอร์ที่อ่านในเครื่อง; ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ

Detailed Description

Header: ipc_sensorhub.h ตัวโค้ดที่ทำงานจริง: ipc_sensorhub.c ที่อยู่ใน archive (libbento_ipc.a) ฝั่ง ISR (interrupt service routine) เก็บตัวอย่างค่าล่าสุดจากเซนเซอร์ทุกตัวที่ CM33_NS ส่งมา ส่วนผู้อ่านรับค่าเป็น sensorhub_snapshot_t บนบอร์ดที่ CM55 เป็นเจ้าของบัสของเซนเซอร์ (Eva Kit และบอร์ดฐานของ TESAIoT Dev Kit) ฟังก์ชัน feed_* จะป้อนตัวอย่างค่าที่อ่านได้ในเครื่องบน CM55 เข้าสู่ที่เก็บ (store) เดียวกัน

variant ที่ใช้ได้
mtb-mpy และ mtb-only

Function Documentation

◆ ipc_sensorhub_init()

bool ipc_sensorhub_init ( void )

การลงทะเบียน callback ของ pipe — ลำดับที่สองใน boot prologue

Initialize IPC sensorhub receiver. Registers IPC pipe callback on CM55_IPC_SENSOR_CLIENT_ID. Must be called AFTER cm55_ipc_communication_setup().

Returns
true on success.
ข้อกำหนดการเรียกใช้
"Must be called AFTER \c cm55_ipc_communication_setup()" (ipc_sensorhub.h) display controller ที่อยู่ใน archive เรียกฟังก์ชันนี้เป็นลำดับที่สองใน boot prologue (ช่วงต้นของลำดับการบูต) ก่อน ipc_service_init() และก่อนการเริ่มการทำงานของ GFXSS เพื่อให้ IPC ของเซนเซอร์ยังอยู่รอดเมื่อจอแสดงผลล้มเหลว ลงทะเบียน callback ของ pipe บน CM55_IPC_SENSOR_CLIENT_ID ผู้ใช้ไลบรารี (consumer) ที่ใช้ display controller ที่ส่งมอบมาจะไม่เรียกฟังก์ชันนี้เลย
variant ที่ใช้ได้
mtb-mpy และ mtb-only
ที่มา
ยกมาจาก TESAIoT_KIT_PSE84_AI-Micropython-BentoClaw/proj_cm55/modules/lvgl_display/controller/tesaiot_display.c:223-238 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

◆ ipc_sensorhub_snapshot()

void ipc_sensorhub_snapshot ( sensorhub_snapshot_t * snap)

ต่อหนึ่ง tick มีผู้อ่านได้รายเดียว — การอ่านจะเคลียร์แฟล็ก changed

Take a snapshot of the latest sensor data. Clears the "changed" flags after reading. Safe to call from any task context.

Parameters
snapOutput snapshot struct.
ข้อกำหนดการเรียกใช้
ผู้เรียกเป็นเจ้าของ sensorhub_snapshot_t บน stack ของตนเอง และส่งที่อยู่ของมันเข้ามา header ระบุว่า "Safe to call from any task context" แต่ระบุด้วยว่า "Clears the 'changed' flags after reading" — ดังนั้นต่อหนึ่ง tick จึงมีผู้อ่านเรียกได้ เพียงรายเดียว พอดี นั่นคือเหตุผลที่ UI อ่าน snapshot ครั้งเดียวที่ 33 ms แล้วกระจายต่อผ่าน pm_render() แทนที่จะให้แต่ละหน้า (page) อ่าน snapshot ของตัวเองแยกกัน ผู้เรียกที่อยู่ใน archive อีก 2 รายซึ่งอยู่ในบริบทอื่นยืนยันข้ออ้างเรื่อง any-task นี้: AI inference task (ai_engine.c:795) และ IPC service task (ipc_service.c:729) คืนค่า void
variant ที่ใช้ได้
mtb-mpy และ mtb-only
static void sensorhub_timer_cb(lv_timer_t *timer)
{
(void)timer;
/* Take snapshot of all sensor data from IPC */
/* Dispatch render to current page (skips if animating) */
pm_render(&s_pm, &snap);
/* Update global topbar (time + WiFi) on current page */
if (!s_pm.animating) {
pm_update_topbar(&s_pm);
}
}

◆ ipc_sensorhub_wifi_connected()

bool ipc_sensorhub_wifi_connected ( void )

สถานะ WiFi ที่ได้จากการวนถาม — ต้อง edge-detect ก่อนแตะ LVGL

Returns true if CM33_NS reported WiFi connected via IPC_CMD_WIFI_STATE_PUSH

ข้อกำหนดการเรียกใช้
ได้มาจากการวนถาม (polling) — ไม่มี API แบบ callback อยู่เลย รูปแบบ edge-detect (ตรวจเฉพาะขอบที่สถานะเปลี่ยน) เป็นข้อบังคับ: ให้เทียบสถานะที่วนถามได้กับแฟล็ก LVGL ปัจจุบันของ widget แล้วแตะ LVGL เฉพาะเมื่อมีการเปลี่ยนสถานะจริงเท่านั้น การเรียก add_flag / clear_flag ทุก tick โดยไม่มีเงื่อนไขทำให้แถวการ์ดกะพริบจนมองเห็นได้ มีตัวกัน (guard) กรณี widget เป็น null เพราะหน้า (page) อาจถูกทำลายระหว่าง tick ทำงานที่การจำกัดอัตรา (throttle) ของ topbar ประมาณ 6 fps และถูกข้ามระหว่างที่มีแอนิเมชัน
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* ...context: inside pm_update_topbar() - GFX task context ... */
/* WiFi icon — only toggle visibility when state actually changes
* to prevent unnecessary LVGL invalidation (card row flicker). */
if (pm->wifi_lbl) {
bool connected = ipc_sensorhub_wifi_connected();
bool hidden = lv_obj_has_flag(pm->wifi_lbl, LV_OBJ_FLAG_HIDDEN);
if (connected && hidden) {
lv_obj_clear_flag(pm->wifi_lbl, LV_OBJ_FLAG_HIDDEN);
} else if (!connected && !hidden) {
lv_obj_add_flag(pm->wifi_lbl, LV_OBJ_FLAG_HIDDEN);
}
}

◆ ipc_sensorhub_ble_connected()

bool ipc_sensorhub_ble_connected ( void )

คืน true เฉพาะขณะที่ NUS host อยู่ในสถานะ CONNECTED เท่านั้น; ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ

Returns true if CM33_NS reported BLE NUS host CONNECTED via IPC_CMD_BLE_STATE_PUSH. Only true while a host is connected; advertising/idle return false.

ข้อกำหนดการเรียกใช้
คืนค่า true เฉพาะขณะที่ NUS host อยู่ในสถานะ CONNECTED เท่านั้น (IPC_CMD_BLE_STATE_PUSH) ส่วนการกระจายสัญญาณ (advertising) และสถานะว่างอ่านได้เป็น false ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบที่ใดเลย — ตัวอย่างที่เขียนขึ้นเองสร้างตามรูปแบบ edge-detect ของ wifi_connected : ทำงานบน GFX task เท่านั้น มีตัวกันกรณี widget เป็น null และแตะ LVGL เฉพาะเมื่อมีการเปลี่ยนแปลงเท่านั้น
variant ที่ใช้ได้
mtb-mpy และ mtb-only
ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
static lv_obj_t *s_ble_lbl; /* created by the page's create_cb,
* NULLed by its destroy_cb */
static void bento_ex_ipc_sensorhub_ble_connected(void)
{
if (s_ble_lbl == NULL) {
return; /* page gone — widget freed */
}
bool connected = ipc_sensorhub_ble_connected();
bool hidden = lv_obj_has_flag(s_ble_lbl, LV_OBJ_FLAG_HIDDEN);
/* Edge detect: touch LVGL only on a state CHANGE. */
if (connected && hidden) {
lv_obj_clear_flag(s_ble_lbl, LV_OBJ_FLAG_HIDDEN);
} else if (!connected && !hidden) {
lv_obj_add_flag(s_ble_lbl, LV_OBJ_FLAG_HIDDEN);
}
}

◆ ipc_sensorhub_ntp_synced()

bool ipc_sensorhub_ntp_synced ( void )

เป็น gate ของการอ่าน RTC ทั้งหมด

Returns true if CM33_NS has sent at least one IPC_CMD_TIME_PUSH (NTP synced)

Returns true after CM33_NS sends IPC_CMD_NTP_SYNCED (RTC is valid).

ข้อกำหนดการเรียกใช้
เป็น gate (ด่านกั้น) ของการอ่าน RTC ทั้งหมด : Cy_RTC_GetDateAndTime() ถูกเรียกเฉพาะหลังจาก CM33_NS ส่งการแจ้ง NTP-synced มาแล้วเท่านั้น การคืนค่า true เป็นเงื่อนไขจำเป็นแต่ยังไม่เพียงพอ — โค้ดที่ส่งมอบจริงยังตรวจช่วงค่าของ rtc.month และ rtc.dayOfWeek ก่อนนำไปใช้เป็นดัชนีของตารางชื่ออยู่ดี ให้สังเกต strcmp ที่อยู่ก่อน lv_label_set_text เพื่อไม่ให้ label ถูกทำให้เป็นโมฆะทุก tick (แถวการ์ดกะพริบ) จำกัดอัตรา (throttle) ที่ประมาณ 6 fps ภายใน pm_update_topbar และถูกข้ามระหว่างที่มีแอนิเมชัน
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* ...context: inside pm_update_topbar() - GFX task context ... */
/* RTC time display — only after NTP sync notification from CM33_NS */
if (pm->time_lbl && ipc_sensorhub_ntp_synced()) {
static const char * const dow[] = {
"", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static const char * const mon[] = {
"", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
cy_stc_rtc_config_t rtc;
Cy_RTC_GetDateAndTime(&rtc);
if (rtc.month >= 1 && rtc.month <= 12 &&
rtc.dayOfWeek >= 1 && rtc.dayOfWeek <= 7) {
char buf[32];
snprintf(buf, sizeof(buf), "%s %d %s %02d:%02d",
dow[rtc.dayOfWeek], (int)rtc.date,
mon[rtc.month], (int)rtc.hour, (int)rtc.min);
/* Only invalidate if text actually changed (prevents card row flicker) */
if (strcmp(lv_label_get_text(pm->time_lbl), buf) != 0) {
lv_label_set_text(pm->time_lbl, buf);
}

◆ ipc_sensorhub_get_time_str()

bool ipc_sensorhub_get_time_str ( char * buf,
size_t buf_size )

สตริงเวลาล่าสุดที่ CM33 ส่งมา; คืน false ก่อน NTP sync

Copy the latest time string from CM33_NS (e.g. "Mon 10 Mar 14:35"). Returns false if NTP not yet synced. buf must be >= 32 bytes.

ข้อกำหนดการเรียกใช้
คัดลอกสตริงเวลาล่าสุดที่ CM33_NS ส่งมา และคืนค่า false เมื่อ NTP ยังไม่ sync buf ต้องมีขนาดอย่างน้อย 32 ไบต์ ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ — topbar อ่าน RTC โดยตรงหลัง gate ntp_synced แทน ตัวอย่างที่เขียนขึ้นเองจับคู่ฟังก์ชันนี้เข้ากับ gate ดังกล่าว
variant ที่ใช้ได้
mtb-mpy และ mtb-only
ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
static void bento_ex_ipc_sensorhub_get_time_str(void)
{
/* Gate first: false before the first NTP push — necessary, and this
* call re-checks it anyway (returns false while unsynced). */
return; /* show nothing rather than garbage */
}
char now[32]; /* header contract: >= 32 bytes */
if (!ipc_sensorhub_get_time_str(now, sizeof(now))) {
return;
}
/* Drive a label only on change (flicker discipline, GFX task):
* if (strcmp(lv_label_get_text(lbl), now) != 0)
* lv_label_set_text(lbl, now);
*/
(void)now;
}

◆ ipc_sensorhub_feed_capsense()

void ipc_sensorhub_feed_capsense ( const ipc_sensor_capsense_t * data)

การป้อนตัวอย่างค่า CapSense ที่อ่านในเครื่อง — ต้องกำหนดค่าเริ่มต้นครบทุกฟิลด์ และใช้ sequence ร่วมกัน

ข้อกำหนดการเรียกใช้
ทุกฟิลด์ต้องถูกกำหนดค่าเริ่มต้น รวมถึง reserved = 0 และ sequence ต้องเพิ่มขึ้นแบบไม่ย้อนกลับจากตัวนับที่ใช้ร่วมกับการป้อนข้อมูลของ pot — หนึ่งช่วงเลข sequence ต่อผู้ผลิตข้อมูล (producer) หนึ่งราย ผู้ผลิตข้อมูลเป็นผู้เขียนรายเดียวบน GFX task นั่นคือสิ่งที่ทำให้ฟิลด์แคชขนาดหนึ่งไบต์ถูกอ่านจากเส้นทาง snapshot ของ IPC_CMD_CONTROLS_STATE ฝั่ง CM33 ได้โดยไม่ต้องใช้ lock คืนค่า void ส่วนค่า bool ของ wrapper รายงานผลการอ่าน I2C ไม่ใช่ผลของการป้อนข้อมูล
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* ...context: inside capsense_read_and_feed() - GFX task context ... */
ipc_sensor_capsense_t d;
d.btn0_pressed = (btn0_code != s_caps_idle_btn0) ? 1 : 0;
d.btn1_pressed = (btn1_code != s_caps_idle_btn1) ? 1 : 0;
d.slider = buf[2];
d.reserved = 0;
d.sequence = s_seq++;
/* Cache the decoded state for the CM33 IPC_CMD_CONTROLS_STATE snapshot
* (byte-size fields — single-writer GFX task, torn reads impossible). */
s_caps_btn0 = d.btn0_pressed;
s_caps_btn1 = d.btn1_pressed;
s_caps_slider = d.slider;
s_caps_live = true;
return true;

◆ ipc_sensorhub_feed_pot()

void ipc_sensorhub_feed_pot ( const ipc_sensor_pot_t * data)

ค่าของ pot ที่ปรับเป็นค่ามาตรฐานแล้ว ไม่ใช่ค่านับจาก ADC; ต้องตรวจ pot_channels_ready() ก่อน

ข้อกำหนดการเรียกใช้
API นี้ส่งค่าที่ ปรับเป็นค่ามาตรฐานแล้ว (normalised) ไม่ใช่ค่านับจาก ADC: raw ถูกปรับสเกลไปเต็มช่วงของ uint16 และ percent_x10 อยู่ในหน่วยต่อพัน ทั้งคู่คำนวณจาก POT_ADC_MAX ทั้งฟังก์ชัน feed นี้อยู่ภายใน BSP_HAS_POTENTIOMETER ก่อนการป้อนข้อมูลทุกครั้ง โค้ดที่ส่งมอบจริงจะเรียก pot_channels_ready() (cm55_sensor_poll.c:189-198) : ผลการอ่าน SAR ที่เป็น 0 ไม่ใช่หลักฐานว่าช่องสัญญาณยังทำงานอยู่ จึงมีการตรวจบิตสถานะประจำช่องก่อน ใช้ตัวนับ s_seq ตัวเดียวกันร่วมกับ capsense
variant ที่ใช้ได้
mtb-mpy และ mtb-only
static bool pot_read_and_feed(void)
{
for (uint8_t i = 0; i < QWA309_POT_COUNT; i++) {
s_pot_raw[i] = pot_read_channel(i);
}
/* Feed VR1 (channel 0) to the single-value pot IPC so the Controls page
* and the pot MicroPython snapshot light up unchanged. */
uint16_t adc0 = s_pot_raw[0];
ipc_sensor_pot_t d;
d.raw = (uint16_t)((uint32_t)adc0 * 65535U / POT_ADC_MAX);
d.percent_x10 = (uint16_t)((uint32_t)adc0 * 1000U / POT_ADC_MAX);
d.sequence = s_seq++;
return true;
}

◆ ipc_sensorhub_feed_bmi270()

void ipc_sensorhub_feed_bmi270 ( const ipc_sensor_bmi270_t * data)

การป้อนตัวอย่างค่า IMU ที่อ่านในเครื่อง; ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ

ข้อกำหนดการเรียกใช้
ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบที่ใดเลย ตัวอย่างที่เขียนขึ้นเองยึดวินัยของ feed_capsense ทุกประการ: กำหนดค่าเริ่มต้นให้ struct ครบทุกฟิลด์ ให้ sequence เพิ่มขึ้นแบบไม่ย้อนกลับจากตัวนับของผู้ผลิตข้อมูลเอง และทำงานใน GFX-task context แบบผู้เขียนรายเดียว
variant ที่ใช้ได้
mtb-mpy และ mtb-only
ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
static uint16_t s_imu_seq; /* ONE sequence space for this
* producer — shared by the bmi270
* and bmm350 feeds below */
static void bento_ex_ipc_sensorhub_feed_bmi270(void)
{
/* Raw values as read from the sensor on the CM55-owned bus. */
int16_t ax = 0, ay = 0, az = 4096; /* +1 g at ±8g scale */
int16_t gx = 0, gy = 0, gz = 0;
ipc_sensor_bmi270_t d; /* full struct init — every field */
d.ax = ax;
d.ay = ay;
d.az = az;
d.gx = gx;
d.gy = gy;
d.gz = gz;
d.sequence = s_imu_seq++;
}

◆ ipc_sensorhub_feed_bmm350()

void ipc_sensorhub_feed_bmm350 ( const ipc_sensor_bmm350_t * data)

การป้อนตัวอย่างค่าแมกนีโตมิเตอร์ที่อ่านในเครื่อง; ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ

ข้อกำหนดการเรียกใช้
ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบที่ใดเลย ข้อกำหนดเดียวกับ feed_bmi270 : กำหนดค่าครบทุกฟิลด์ รักษาวินัยของ sequence และมีผู้ผลิตข้อมูลรายเดียว
variant ที่ใช้ได้
mtb-mpy และ mtb-only
ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
static void bento_ex_ipc_sensorhub_feed_bmm350(void)
{
ipc_sensor_bmm350_t d; /* full struct init — every field */
d.mx_x100 = 2150; /* 21.50 uT */
d.my_x100 = -430; /* -4.30 uT */
d.mz_x100 = 4890; /* 48.90 uT */
d.heading_x10 = 923; /* 92.3 degrees */
d.sequence = s_imu_seq++; /* same producer sequence space */
}