SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
Loading...
Searching...
No Matches

Functions

bool ipc_sensorhub_init (void)
 Pipe-callback registration — second in the boot prologue.
void ipc_sensorhub_snapshot (sensorhub_snapshot_t *snap)
 One consumer per tick — reading clears the changed flags.
bool ipc_sensorhub_wifi_connected (void)
 Polled WiFi state — edge-detect before touching LVGL.
bool ipc_sensorhub_ble_connected (void)
 True only while a NUS host is CONNECTED; no shipped caller.
bool ipc_sensorhub_ntp_synced (void)
 The gate on reading the RTC at all.
bool ipc_sensorhub_get_time_str (char *buf, size_t buf_size)
 Latest CM33-pushed time string; false before NTP sync.
void ipc_sensorhub_feed_capsense (const ipc_sensor_capsense_t *data)
 Local CapSense sample injection — every field initialised, shared sequence.
void ipc_sensorhub_feed_pot (const ipc_sensor_pot_t *data)
 Normalised pot values, not ADC counts; check pot_channels_ready() first.
void ipc_sensorhub_feed_bmi270 (const ipc_sensor_bmi270_t *data)
 Local IMU sample injection; no shipped caller.
void ipc_sensorhub_feed_bmm350 (const ipc_sensor_bmm350_t *data)
 Local magnetometer sample injection; no shipped caller.

Detailed Description

Header: ipc_sensorhub.h. Implementation: archived ipc_sensorhub.c (libbento_ipc.a). The ISR half stores the latest sample from every sensor CM33_NS pushes; readers take a sensorhub_snapshot_t. On boards where CM55 owns the sensor bus (Eva Kit, TESAIoT Dev Kit base board) the feed_* functions inject samples read locally on CM55 into the same store.

Variant
mtb-mpy and mtb-only

Function Documentation

◆ ipc_sensorhub_init()

bool ipc_sensorhub_init ( void )

Pipe-callback registration — second in the 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.
Contract
"Must be called AFTER \c cm55_ipc_communication_setup()" (ipc_sensorhub.h). The archived display controller calls it second in the boot prologue, before ipc_service_init() and before GFXSS bring-up, so sensor IPC survives a display failure. Registers the pipe callback on CM55_IPC_SENSOR_CLIENT_ID. Consumers using the shipped display controller never call it.
Variant
mtb-mpy and mtb-only
Origin
Lifted from TESAIoT_KIT_PSE84_AI-Micropython-BentoClaw/proj_cm55/modules/lvgl_display/controller/tesaiot_display.c:223-238 (compiled into the prebuilt archive; not shipped as source).

◆ ipc_sensorhub_snapshot()

void ipc_sensorhub_snapshot ( sensorhub_snapshot_t * snap)

One consumer per tick — reading clears the changed flags.

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.
Contract
The caller owns a stack sensorhub_snapshot_t and passes its address. The header says "Safe to call from any task context" but also "Clears the 'changed' flags after reading" — so exactly one consumer per tick may call it. That is why the UI takes one snapshot at 33 ms and fans it out via pm_render() instead of each page snapshotting independently. Two archived callers in other contexts confirm the any-task claim: the AI inference task (ai_engine.c:795) and the IPC service task (ipc_service.c:729). Void.
Variant
mtb-mpy and 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 )

Polled WiFi state — edge-detect before touching LVGL.

Returns true if CM33_NS reported WiFi connected via IPC_CMD_WIFI_STATE_PUSH

Contract
Polled — no callback API exists. The edge-detect pattern is mandatory: compare the polled state against the widget's current LVGL flag and touch LVGL only on a real transition; unconditional add_flag / clear_flag every tick causes visible card-row flicker. Widget-null-guarded, because the page can be destroyed between ticks. Runs at the ~6 fps topbar throttle, skipped while animating.
Variant
mtb-mpy and 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 only while a NUS host is CONNECTED; no shipped caller.

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.

Contract
True only while a NUS host is CONNECTED (IPC_CMD_BLE_STATE_PUSH); advertising and idle read false. No shipped caller anywhere — the authored example models it on the wifi_connected edge-detect pattern: GFX-task only, widget null-guarded, LVGL touched only on a change.
Variant
mtb-mpy and mtb-only
Example (authored — no shipped 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 )

The gate on reading the RTC at all.

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

Contract
The gate on reading the RTC at all: Cy_RTC_GetDateAndTime() is called only after CM33_NS has sent the NTP-synced notification. A true return is necessary but not sufficient — the shipped code still range-validates rtc.month and rtc.dayOfWeek before indexing its name tables. Note the strcmp before lv_label_set_text so the label is not invalidated every tick (card-row flicker). ~6 fps throttle inside pm_update_topbar, skipped while animating.
Variant
mtb-mpy and 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 )

Latest CM33-pushed time string; false before 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.

Contract
Copies the latest time string pushed by CM33_NS; returns false when NTP has not synced. buf must be at least 32 bytes. No shipped caller — the topbar reads the RTC directly behind the ntp_synced gate instead. The authored example pairs it with that gate.
Variant
mtb-mpy and mtb-only
Example (authored — no shipped 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)

Local CapSense sample injection — every field initialised, shared sequence.

Contract
Every field must be initialised, including reserved = 0, and sequence must increment monotonically from a counter shared with the pot feed — one sequence space per producer. The producer is a single writer on the GFX task; that is what makes the byte-sized cached fields readable from the CM33 IPC_CMD_CONTROLS_STATE snapshot path without a lock. Void; the wrapper's bool reports the I2C read, not the feed.
Variant
mtb-mpy and 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)

Normalised pot values, not ADC counts; check pot_channels_ready() first.

Contract
The API carries normalised values, not ADC counts: raw rescaled to the full uint16 range and percent_x10 in per-mille, both from POT_ADC_MAX. The whole feed function sits inside BSP_HAS_POTENTIOMETER. Before any feed the shipped code runs pot_channels_ready() (cm55_sensor_poll.c:189-198): a SAR read result of 0 is not proof the channel is live, so the per-channel status bit is checked first. Same shared s_seq counter as capsense.
Variant
mtb-mpy and 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)

Local IMU sample injection; no shipped caller.

Contract
No shipped caller anywhere. The authored example follows the feed_capsense discipline exactly: full struct initialisation, monotonic sequence from the producer's own counter, single-writer GFX-task context.
Variant
mtb-mpy and mtb-only
Example (authored — no shipped 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)

Local magnetometer sample injection; no shipped caller.

Contract
No shipped caller anywhere. Same contract as feed_bmi270: every field set, sequence discipline, single producer.
Variant
mtb-mpy and mtb-only
Example (authored — no shipped 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 */
}