On the glasses
Head tracking
Read head orientation and motion right on the glasses.
There’s no phone round trip, so head tracking is fast enough for games and steering.
Orientation
| Function | Returns |
|---|---|
imu_yaw_cdeg() | Heading. Positive is turning right. |
imu_pitch_cdeg() | Tilt. Positive is looking up. |
imu_roll_cdeg() | Roll. |
imu_ready() | 1 once a calibrated sample has arrived, otherwise 0. |
Angles are in centidegrees: 100 is one degree. Yaw wraps at ±180°, so always take the shortest signed difference between two headings.
#[link(wasm_import_module = "mira")]
extern "C" {
fn imu_yaw_cdeg() -> i32;
fn imu_pitch_cdeg() -> i32;
fn imu_ready() -> i32;
}
static mut CENTER: Option<(i32, i32)> = None;
// Shortest signed difference, in centidegrees.
fn wrap(d: i32) -> i32 {
let d = d % 36_000;
if d > 18_000 { d - 36_000 } else if d < -18_000 { d + 36_000 } else { d }
}
unsafe fn pointer_position() -> Option<(i32, i32)> {
let (yaw, pitch) = (imu_yaw_cdeg(), imu_pitch_cdeg()); // call first
if imu_ready() == 0 { return None; }
let (yaw0, pitch0) = *CENTER.get_or_insert((yaw, pitch));
let x = 320 + wrap(yaw - yaw0) * 8 / 100; // 8 px per degree
let y = 240 - (pitch - pitch0) * 8 / 100; // up is negative y
Some((x.clamp(0, 639), y.clamp(0, 479)))
}Screen y grows downward while positive pitch means looking up, which is why the example subtracts pitch. Recenter by storing a new starting heading, for example when the wearer taps.
Sharing the sensor with MIRA
While your app reads orientation, MIRA pauses its look-up dashboard so a glance upward doesn’t interrupt your game. When you’re done with head tracking, call imu_stop() (content::imu::stop() in Rust) to give it back. Leaving the app releases it automatically.
If you only need to know whether the wearer is looking up or down, use imu_threshold(above_cdeg, below_cdeg). It doesn’t take the sensor away from MIRA, so the dashboard keeps working.
imu_threshold returns | Meaning |
|---|---|
1 | Above the upper threshold |
0 | Below the lower threshold |
-1 | Display asleep, or no calibrated data yet |
-2 | Full head tracking is active, by this app or by the phone |
Raw motion
imu_raw_read(out) writes nine 32-bit floats (36 bytes): accelerometer x, y, z, then gyroscope x, y, z, then magnetometer x, y, z. The values come straight from the sensors, with no filtering or fusion.
It returns 1 when the sample is fresh (at most 500 ms old), 0 when it’s unavailable or stale, and -1 for an invalid buffer. Only use the values when it returns 1. Like the orientation functions, reading raw motion pauses the look-up dashboard until you call imu_stop().
From the phone
Your phone companion can also read motion with miraCompanion.getIMU() and release it with miraCompanion.stopIMU(). Phone orientation readings are in degrees, their pitch sign is the reverse of the glasses functions, and they arrive over Bluetooth. Poll them at a modest rate (every 400 ms or so) and use the glasses functions above for anything that needs low latency. See Phone companion.