Motion sensor ownership¶
The current IMU service owns the selected sensor, filter, sampling task and short snapshot publication. Board owns shared I2C controller lifetime; sensor recovery must not delete/reset the controller. Legacy drivers retain their existing sample and calibration contract.
BMI270 uses 100 Hz accel/gyro, ±4 g / ±1000 dps, with accelerometer and gyroscope
filter performance enabled and gyroscope noise performance enabled. Advanced
power save is explicitly disabled after initialisation: the continuous polling
path must not incur Bosch's 450 µs post-read busy wait. Auxiliary/BMM150 and
temperature sensing remain disabled. The adapter retains the vendor sample
parser and gyro cross-axis correction. See the
profiling measurements.
Each sample reads only the 12 contiguous accel/gyro bytes at 0x0c. The adapter
places them into the vendor parser's buffer layout; unused status, auxiliary and
sensor-time fields are zero and are not published by this adapter. Read failures
retain the normal failure/reinitialisation path. Sensor rate/ranges are unchanged.
CONFIG_ESPURR_PROFILE, off by default, adds bounded elapsed-time counters for
the legacy sampling stages and normal S3 output, plus native timer callback
profiling. Reports run after the output owner drains DMA. Durations include
blocking/preemption and are not exclusive CPU measurements; nested read/delay
stages must not be added to the enclosing sample duration. The option requires
fixed CPU frequency and does not allocate PSRAM or alter sensor rates.
BX owner, clock and FIFO¶
bx_sensor.c uses ST's pinned register driver with
bounded reset waits and first-I/O-error preservation. It configures ±4 g / ±2000 dps
at 120 Hz, uncompressed accel/gyro FIFO batching, a timestamp in every slot, and
INT1 watermark/overrun routing. Optional SFLP adds game quaternion, gravity and
bias at 120 Hz, the documented power-down page-D2 workaround and explicit init.
Watermarks are one frame (three raw words or six with SFLP), not ST's example 32.
Reads are bounded to 36 FIFO words per owner pass. Overrun flushes FIFO and loses
synchronisation without resetting the sensor, preserving the future distinction
between FIFO recovery and an active TDM capture reset.
The adapter currently reads individual tagged words through ST's helper. It does not assume that a long I2C read wraps FIFO registers on this exact part. Bus cost and any verified bulk-read optimisation remain hardware qualification work.
bx_fifo.c joins records by the two-bit time-slot counter, independent of tag
order. The next slot finalises the preceding slot, so acquisition-to-publication
latency includes one sensor period. Raw accel/gyro/timestamp must all be present;
duplicates, unknown/unconfigured formats and backward timestamps cannot fabricate
samples. Raw units retain sensor-axis g and degrees/second. Gravity follows the
manual's Z,Y,X order; quaternion and bias follow X,Y,Z. The quaternion is stored
as X,Y,Z,W with W nonnegative, not an absolute compass heading.
The decoder extends 32-bit timestamps across natural wrap, flags counter/time gaps,
and computes tick duration from 1 / (46080 * (1 + .0013 * FREQ_FINE)) seconds.
It does not use host burst-read time as sample time. FIFO overrun discards data
until a new timestamp and reanchors the source-time continuity segment. Jumps
beyond one second cannot fit this FIFO mode and are rejected. Sensor reset
requires owner reinitialisation; it is not
interpreted as a natural timestamp wrap.
SFLP gravity must have a plausible unit norm, and quaternion data must be finite and physically possible. Fusion validity additionally requires 240 consecutive valid frames after startup/loss (about two seconds at nominal ODR, not a measured convergence guarantee; actual duration follows the trimmed clock). Raw-frame validity and fusion validity remain separate. Invalid fusion never silently becomes a valid host-fused result. The on-chip gyro-bias record is diagnostic data; it is not subtracted from raw gyro in addition to the user's saved calibration bias.
The v2 profile enables BX motion with the existing host gravity estimator by default. Select Espurr cat ears → Experimental BX sensor-fused gravity through the profile's menuconfig to compare SFLP. Legacy profiles retain their selected sensor, 100 Hz polling, ranges and filter arithmetic. Neither path requires PSRAM.
The core-0 owner uses INT1 notifications, with a bounded 10 ms polling fallback if IRQ registration fails or notifications are missed. The ISR only notifies; all register access and filtering occur outside the short publication mutex. Each drain copies one coherent settings snapshot and consumes at most 36 words. V2 starts at the handoff's 100 kHz; raising it requires cable/loading qualification. The per-word adapter adds significant bus traffic when all SFLP records are enabled; FIFO/SFLP offload does not establish lower bus load or net CPU savings. The BX task has a 6144-byte internal stack; legacy tasks retain 4096 bytes. These allocations are not measured stack margins or proof of CPU/bus headroom.
bx_clock.c brackets a separate sensor timestamp read with monotonic host time,
uses factory-trimmed tick duration, and maps each FIFO sample to acquisition
time. Brackets over 5 ms, anchors older than 250 ms, estimates older than 100 ms
and impossible future times are rejected. The reported uncertainty includes half
the read bracket plus one conservative sensor tick. The next-slot join and owner
scheduling add publication latency; burst completion never replaces acquisition
time. A 150 ms absence of joined frames forces owner reinitialisation.
In SFLP mode only the gravity estimator is replaced. Mounting/recentre, raw gyro, the user's saved gyro bias, energy and gesture policy remain host-owned. The saved host gravity time constant is retained but ignored by this experimental backend; it does not tune SFLP. Settling/invalid fusion suppresses valid motion rather than silently falling back to host gravity. Gesture counters survive BX continuity loss/reinitialisation while partial gestures and stationary dwell are reset.
imu_health_snapshot() publishes selected source, starting/settling/ready/stale/
failed state, generation, accepted-frame count, loss counters, clock uncertainty,
IRQ registration and the factory-corrected configured rate. Loss counters and sample
count are scoped to a generation. The getter expires ready/settling after 100 ms.
The rate is not a measured drain rate; IRQ registration is not proof of delivery.
A separate short health publication lets readers run before startup and during
configuration/recovery without taking the configuration mutex or doing I2C. An
explicit sample-timestamp flag distinguishes startup from timestamp/counter wrap.
Expiry also reports ESP_ERR_TIMEOUT; it never mutates the owner's stored state.
Disabled motion reports unsupported. The optional read-only
motion.status record exports
this contract on every profile within the existing 64-byte value bound. Both
phone filtering pages expose source/health and disable only the inactive gravity
editor with SFLP; its saved value survives. The host gyro editor still applies.
Older firmware without this record keeps the existing capability-based behaviour.
FIFO overflow flushes only FIFO state. I/O/format failures and the no-frame watchdog use full sensor reinitialisation. Do not add independent BX register writers or reset the chip during active contact capture or a future MLC session: both share this owner's coordinated configuration and recovery transition. The adapter now has an optional all-axis TDM setup during power-down. The live owner compares requested contact demand with sensor configuration, obtains an explicit receiver-stop acknowledgement before full reset, and grants capture permission only after successful initialisation. Full I/O/watchdog failure revokes permission; FIFO-only recovery preserves capture. Capture ownership coordinates independent air/contact sources and render-owned flash pauses. Sources start off; both phone apps provide temporary capture, level diagnostics and bounded recording/ export. PDM Sound bands supplies independent render-owned air-capture demand. Contact VAD remains pending hardware and worn recordings; MLC loading is a future experiment. See the audio contract for demand and recorder ownership.
Hardware acceptance must verify identity/bus timing, actual acquisition timestamps, FIFO loss/latency, asymmetric gravity axes (manual versus example discrepancy), quaternion convention, settling/bias behaviour, mounted calibration, gesture parity, TDM coexistence and actual CPU/stack/heap use. Native fixtures do not prove those.
Primary evidence: AN5845 sections 6.4, 6.5, 8.4, 8.6.2 and 8.11, BX datasheet, and the pinned ST example/register sources above.