Skip to main content

zendo_protocol/
lib.rs

1//! Wire-protocol definitions for the Zendo motion-tracking WebSocket stream.
2//!
3//! This crate is the single source of truth for the binary protocol that the
4//! Zendo desktop app emits and that [`zendo-sdk`](https://crates.io/crates/zendo-sdk)
5//! consumes. It owns the port range, the message-type tags, the joint and
6//! landmark vocabularies, and the pure decode/encode routines.
7//!
8//! It has no dependencies, performs no I/O, never allocates, and is `no_std`
9//! by default-disabling the `std` feature.
10//!
11//! # Frame layout
12//!
13//! Every frame is one binary WebSocket message: byte 0 is the type tag, the
14//! rest is the payload. All numeric values are little-endian `f64`.
15//!
16//! | Message | Tag | Payload |
17//! |---|---|---|
18//! | Hello            | `0x01` | protocol version (`u16` LE) |
19//! | Body quaternions | `0x02` | 13 joints x (w, x, y, z) |
20//! | Body landmarks   | `0x03` | 19 landmarks x (x, y, z, confidence) |
21//! | Hand quaternions | `0x04` | 1 side byte + 16 joints x (w, x, y, z) |
22//! | Hand landmarks   | `0x05` | 1 side byte + 21 landmarks x (x, y, z, confidence) |
23//! | Body ISB angles  | `0x06` | 21 scalar angles (radians) |
24//!
25//! The server sends the hello frame first on every connection so clients can
26//! detect a [`PROTOCOL_VERSION`] mismatch before decoding data frames.
27//!
28//! # Example
29//!
30//! ```
31//! use zendo_protocol::{decode, Message};
32//!
33//! # let frame: &[u8] = &{
34//! #     let mut f = [0u8; 1 + 13 * 4 * 8];
35//! #     f[0] = zendo_protocol::MSG_BODY_QUATERNION;
36//! #     f
37//! # };
38//! match decode(frame) {
39//!     Ok(Message::BodyQuaternions(q)) => println!("hips: {:?}", q.hips),
40//!     Ok(other) => println!("other message: {other:?}"),
41//!     Err(e) => eprintln!("bad frame: {e}"),
42//! }
43//! ```
44
45#![cfg_attr(not(feature = "std"), no_std)]
46
47mod constants;
48mod decode;
49mod encode;
50mod error;
51mod frames;
52mod types;
53
54pub use constants::{
55    BODY_ISB_ANGLE_COUNT, BODY_JOINT_COUNT, BODY_LANDMARK_COUNT, HAND_JOINT_COUNT,
56    HAND_LANDMARK_COUNT, HAND_SIDE_LEFT, HAND_SIDE_RIGHT, MSG_BODY_ISB_ANGLES, MSG_BODY_LANDMARK,
57    MSG_BODY_QUATERNION, MSG_HAND_LANDMARK, MSG_HAND_QUATERNION, MSG_HELLO, PROTOCOL_VERSION,
58    WEBSOCKET_PORT_END, WEBSOCKET_PORT_START,
59};
60pub use decode::{decode, decode_hello, Message};
61pub use encode::{
62    encode_body_isb_angles, encode_body_landmarks, encode_body_quaternions, encode_hand_landmarks,
63    encode_hand_quaternions, encode_hello, ENCODED_BODY_ISB_ANGLES_LEN, ENCODED_BODY_LANDMARK_LEN,
64    ENCODED_BODY_QUATERNION_LEN, ENCODED_HAND_LANDMARK_LEN, ENCODED_HAND_QUATERNION_LEN,
65    ENCODED_HELLO_LEN,
66};
67pub use error::ProtocolError;
68pub use frames::{
69    BodyIsbAnglesFrame, BodyLandmarkFrame, BodyQuaternionFrame, HandLandmarkFrame,
70    HandQuaternionFrame,
71};
72pub use types::{
73    HandJoint, HandLandmarkName, HandSide, IsbAngleName, Joint, Landmark, LandmarkName, Quaternion,
74};