Skip to content

Protocol

everything-imu speaks two UDP protocols: the SlimeVR tracker protocol (bridge → server) and the eimu remote-hub protocol (phone → desktop).

UDP, big-endian. Every datagram begins with a 12-byte header: [u32 BE tag][u64 BE seq]. The tag is the packet ID; the sequence number is per-tracker and monotonically increasing, and the server uses it to drop out-of-order packets.

ID Name Notes
0 Heartbeat Trailing 1-byte tracker id (always 0)
1 Rotation Deprecated - use ID 17 on modern servers
3 Handshake Initial connect (protocol version 19)
4 Acceleration Linear acceleration in m/s² (not g)
5 Magnetometer Mag readings (µT) when 9-DOF fusion is active
10 Ping/Pong Server challenge, tracker echoes
12 Battery level [f32 voltage][f32 level 0..1]
15 Sensor info Tracker description (one per logical tracker)
17 Rotation data Modern rotation - quaternion in (X, Y, Z, W) order
21 Calibration action User reset events (yaw / full / mounting)
22 Feature flags Bidirectional (see below)
100 BUNDLE Batched datagram, gated by the server’s feature flags
  • Acceleration is m/s², not g. Multiply g by 9.80665. Note the tracker id is the last byte of an accel packet (vs the first in a rotation packet).
  • Rotation quaternion byte order is (X, Y, Z, W), i.e. (i, j, k, w).
  • Sensor status: send 1 (OK) in packet 15 to flip the dashboard tracker green. The sensor_config bitmask reports magnetometer state - 0x0003 enabled, 0x0002 supported-but-disabled, 0x0000 unsupported.

BUNDLE (packet 100) is only usable if the server’s feature-flags reply advertises PROTOCOL_BUNDLE_SUPPORT. On servers without it, type-100 datagrams are silently dropped, so the bridge falls back to sending rotation (17) then acceleration (4) as separate datagrams - rotation first, because accel-first produced a visible one-frame jitter. The handful of samples that arrive before the feature-flags reply take the fallback path automatically, so no data is lost.

Any change to the encoder must pass byte-for-byte reference tests against known-good captures. SlimeVR-Server silently rejects malformed packets, and silent failures are extremely costly to debug.

A second, separate UDP protocol. The mobile app acts as a hub: it forwards its own IMU and any BLE controllers it owns to the desktop bridge, which runs fusion and registers each forwarded device as a normal tracker.

[4 bytes] magic "EIMU" (45 49 4D 55)
[1 byte] version = 1
[1 byte] message type
[ ... ] payload, LITTLE-endian (opposite of the SlimeVR protocol)

The hub binds 0.0.0.0:9320 by default. Datagrams with a wrong magic or version are dropped.

ID Name Direction Purpose
0x01 HELLO phone → desktop Announce the hub (uuid + name, opt. clock)
0x02 HELLO_ACK desktop → phone Ack, echo the clock for RTT
0x03 ANNOUNCE phone → desktop Register a forwarded device (a handle)
0x04 REMOVE phone → desktop Drop a forwarded device
0x05 IMU phone → desktop Sample burst for a handle
0x06 BATTERY phone → desktop Battery fraction + charging
0x07 BUTTON phone → desktop Reset request (yaw / full)
0x08 RUMBLE desktop → phone Rumble intensity for a handle
0x09 IMU2 phone → desktop Like IMU, plus a wrapping seq for loss

handle is a u16 chosen by the phone; each (hub, handle) pair maps to one tracker on the desktop. An IMU sample burst carries, per sample: an 8-byte µs timestamp, gyro (3× f32, rad/s), accel (3× f32, m/s²), a has_mag byte, and mag (3× f32, µT) that is always on the wire but ignored when has_mag is 0.