|
SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
|
A compiled register. Every entry cites the line that proves it. Entries are numbered as in the design of record so chapters can refer to them by number.
CM55 has no console: CM33_NS owns the UART, and newlib stdio on CM55 would take a mutex that is never initialised and abort the core. proj_cm55/bento_cm55_stdio.c, which is open source, therefore defines printf and puts as weak no-ops. A printf in your own CM55 code prints nothing until you define your own printf (strong wins the link) or route the message over IPC to the CM33_NS console. libbento_edge_ai.a defines neither symbol (neither is in lib/edge_ai/api.txt).
CM55 treats it as an unconditional touch_disabled = false, cancelling another task's touch hold and producing OPTIGA_COMMS_ERROR (0x0102) on the shared bus (ipc_hsm_handler.c:1393-1405). Use the counted optiga_manager_touch_hold()/_release() pair.
It deliberately returns true when the manager was never initialised (declared in tesaiot_hsm_api.h). optiga_manager_lock() is the "is the manager up?" test; getting this backwards is the documented cause of several ungated-access defects.
Both requests are asynchronous, and a resume issued while a rate request is still in flight can cancel it: CM33 then pauses the sensor push instead of changing its rate. set_sensor_rate() already resumes the push. resume_sensor has no caller in shipped firmware.
active() reports the model that has finished loading, so it lags the request by a whole cold-init. The recorded symptom: select Radar, press Load, and the Motion model runs. Guard on ai_engine_requested().
It returns the most recent verdict from any model. In a set, a cough detected at 0.94 is followed by three "unlabelled" verdicts from the other members before a reader sees it — the detection is not late, it is gone. Use snapshot_model() per member.
There is no idempotent set form; always guard with ipc_lcd_is_panel_visible() (ipc_core row 2, page_playground.c:167-188). Console and widgets are mutually exclusive.
lv_screen_load_anim(auto_del) frees the page's objects; a later IPC handler writes into them (ipc_core row 3, page_playground.c:342-349). NULL both containers on destroy, in the same order as the bind.
"Safe to call from any task context" but "Clears the 'changed' flags after reading" (ipc_sensorhub.h:66-67) — exactly one consumer per tick. The UI takes one snapshot at 33 ms and fans out.
Resolve SSID → slot with wifi_saved_find() before any erase (wifi_connect_native.c:118-134); < 0 on miss.
wifi_creds_flush_if_dirty is called only at mpy_main.c:709 and :686 (REPL idle / exit). A program that never returns to the REPL never persists the credential; mtb-only writes immediately (sensor_auto_task.c:784-789 vs :828-854).
A retained bundle arrives on every connect; observed three times on 2026-08-07 silently redoing the previous run. trustm_current_correlation_id() == NULL means nothing is outstanding — discard (tesaiot_pu_ingest.c:752-768). At weak sites check the function pointer, then the string.
The GFX task runs at MAX−1; a wedged clock-stretching device with a block-forever PDL timeout starved every lower-priority CM55 task. Use bounded timeouts (2 ms) on disp_touch_i2c_controller_context (cm55_sensor_poll.c:49-53).
page_id_ordinal_assert.c:22-25; lib/ipc_core/PROVENANCE.txt records PAGE_ID_PLAYGROUND == 7. A mismatch links cleanly and compares the wrong page. Add at the end; never renumber, never re-guard.
The MQTT port derives from tls_mode (mqtt_client_config.c:105-118). Editing port in /.tesaiot_config changes nothing.
main.c:278-287. The heartbeat exists because the variant has no instrument "that is not also the murder weapon". Flash, then power-cycle; do not attach to a running board.
modwifi.c:261-285 and wifi_init.c:261-302 read-modify-write the file directly without wifi_creds_lock and bypass g_boot_wifi_creds; there is no file-level lock outside mtb-only's bs_lock() (J8 thin-evidence #6).
Six of the 18 tesaiot_hsm functions link only under ENABLE_OPTIGA_CLM=1 and are consumed as weak symbols: publish_csr, tesaiot_publish_protected_update, trustm_reset_state, trustm_current_correlation_id, trustm_requested_target_oid, trustm_requested_anchor_oid (A.5 preamble; refusal string at ipc_hsm_handler.c:2158-2161).
The legacy task skipped the WL_REG_ON / WCM init step that CYW55513 needs (main.c:344-347). No caller anywhere; use install_chip_power_then_ble().
A debugger reset leaves the display dark, which looks exactly like a failed flash (shipped package READMEs: dist mtb-mpy :51-52, mtb-only :54; in-zip README :47-49). Taught in A1/A2 at the first checkpoint. Dark screen after flash = reset, not failure.
The backlight can want a second unplug-replug on cold boot (dist mtb-only README :55). A dark screen after a power-cycle is not proof of a bad image.
Values must be a bare 1 or 0. The consuming Makefiles compare with ifeq ($(VAR),1), which is whitespace-sensitive, so BSP_HAS_CAPSENSE=1 (or an inline comment after the value) evaluates false and the feature's INCLUDES/CFLAGS vanish with no error anywhere — the build succeeds and the sensor is simply not there. The file states the rule itself (bsp_features.mk:28-30, restated at :55-57). Taught in The BSP flags, as this board sets them.
CY_RSLT_MODULE_MQTT_CONNECT_FAIL is assigned when the retries run out (cy_mqtt_api.c:2436), overwriting whatever result already held. A TLS failure and an authentication rejection therefore print the identical code. On 2026-09-11 three defects at three different layers all printed it, and it never changed once as each was fixed.
The signal that does work is the [TLS-DIAG] err= line at cy_tls.c:1860. It sits inside the mbedtls_ssl_handshake() loop, so it fires on every handshake failure — which means its absence is proof that TLS succeeded. Do not read the other [TLS-DIAG] lines (optiga_key, heap) that way: they print before the handshake and say nothing about it.
mqtt_mtls_setup.c:232 switches to 0xE0E1/0xE0F1 the moment verify_cert_key_pair() is true, so the certificate on the wire is not a configuration choice — it is decided at connect time by whether enrolment has succeeded.
The failure is inverted: a board that fails enrolment connects, and a board that succeeds is refused, until the broker carries CN=TESAIoT MCU CA in its trust bundle and its CN check compares against the device_id rather than the MQTT client id (the firmware deliberately sends the Trust M UID as client id). See D2 — Enrolment and Protected Update end to end.
After several enrolment cycles the secure element can stop opening (optiga_util_open_application failed, status 0x0102). mTLS then cannot read the client certificate and MQTT never starts — tesaiot.connect() prints nothing at all, not even [MQTT] Start request received.
HTTPS keeps working throughout, because it authenticates with an API key and never touches the chip. That asymmetry makes it look like an MQTT or broker problem when it is neither. Check optiga.init() before reading any broker log, and note that reflashing does not clear it: a flash resets the MCU, not the secure element. Only a power cut does.
Both cores link with --specs=nano.specs. newlib-nano knows exactly one length modifier character from h, l, L (_WANT_IO_LONG_LONG is off), so llu prints the letters "lu" and consumes no argument. Every conversion after it reads the argument meant for the one before; a later s dereferences an integer and the core takes a BusFault in memchr ← _printf_i. zu, jd, td and hhu fail the same way, and -Wformat cannot catch any of them because they are valid C99. Print a 64-bit value as two 32-bit halves (0x%08lx%08lx) or convert it yourself (bento_time.c bt_i64_to_str); cast size_t to unsigned long and use lu.
The WiFi page keeps up to six saved networks in slots 5, 6, 8, 9, 10 and 11 (wifi_saved.h), one 106-byte record per OID, SSID and password together. The names TESAIOT_SLOT_WIFI_SSID and TESAIOT_SLOT_USER0..3 do not describe that. The store treats any object shorter than one record as an empty slot and fills empty slots first, so a short value an application keeps in one of these OIDs (an API key, say) is overwritten the next time a network is saved on the screen. 0xF1D2 holds the HSM page PIN hash; 0xF1D4 is reserved for Protected Update. Keep application data in 0xF1E0 or 0xF1E1 (1500 bytes each). The full table is in ipc_communication.h.
CM55 executes in place from the same NOR chip that holds the LittleFS volume. While that chip erases a 256 KB sector (typically ~0.8 s, at most 5.9 s) it answers no reads, and CM55's next instruction fetch never returns: the frame stops mid-draw, touch dies, the IPC pipe wedges. bento_storage asks CM55 to park in ITCM before every erase and program (the XIP guard, ipc_xip_guard.h) and refuses the write when CM55 does not answer. Never call mtb_serial_memory_erase/write or Cy_SMIF_Mem* directly while CM55 is running. In the mtb-mpy variant the MicroPython QSPI driver takes the same handshake (EXT_FLASH_XIP_GUARD=1). Batch high-rate data in RAM and write it in blocks: each write can pause the screen for up to one erase.
The default SCB_AIRCR_BFHFNMINS_VAL 0 (the secure image's partition_ARMCM33.h) sends BusFault and HardFault to the secure side, which records the frame in cy_faultFrame and spins in Cy_SysLib_ProcessingFault without printing anything. The non-secure side just stops while CM55 keeps drawing. During development it can be set to 1 so the non-secure handlers see the fault (the handlers in ns_start_pse84.c now take the stack from EXC_RETURN), but that also hands faults raised in secure code, and NMI, to the non-secure side. Do not ship it.