# zendo-protocol
[](https://crates.io/crates/zendo-protocol)
[](https://docs.rs/zendo-protocol)
The wire-protocol definitions for the [Zendo](https://github.com/akina-health/zendo)
motion-tracking WebSocket stream: port range, message-type tags, the joint and
landmark vocabularies, and the pure decode/encode routines.
This crate is the single source of truth for the protocol. It has **no
dependencies**, performs **no I/O**, **never allocates**, and is `no_std` by
disabling the default `std` feature. Most users want
[`zendo-sdk`](https://crates.io/crates/zendo-sdk) (Rust) or the
[`zendo-sdk`](https://pypi.org/project/zendo-sdk/) PyPI package (Python), which
build a connection on top of this crate. Depend on `zendo-protocol` directly
only if you bring your own transport.
## Frame layout
Every frame is one binary WebSocket message: byte 0 is the type tag, the rest is
the payload. All numeric values are little-endian `f64`.
| Hello | `0x01` | protocol version (`u16` LE) |
| Body quaternions | `0x02` | 13 joints × (w, x, y, z) |
| Body landmarks | `0x03` | 19 landmarks × (x, y, z, confidence) |
| Hand quaternions | `0x04` | 1 side byte + 16 joints × (w, x, y, z) |
| Hand landmarks | `0x05` | 1 side byte + 21 landmarks × (x, y, z, confidence) |
The server sends the hello frame first on every connection, carrying
`PROTOCOL_VERSION`; clients require an exact match. The side byte is `0` for the
right hand and `1` for the left. Body messages stream in body-tracking mode;
hand messages stream in hand-tracking mode; the two never appear in the same
session.
## Example
```rust
use zendo_protocol::{decode, Message};
fn handle(frame: &[u8]) {
match decode(frame) {
Ok(Message::BodyQuaternions(q)) => println!("hips: {:?}", q.hips),
Ok(Message::BodyLandmarks(l)) => println!("nose: {:?}", l.nose),
Ok(Message::HandQuaternions { side, frame }) => {
println!("{} wrist: {:?}", side.as_str(), frame.wrist)
}
Ok(Message::HandLandmarks { side, frame }) => {
println!("{} thumb tip: {:?}", side.as_str(), frame.thumb_tip)
}
Err(e) => eprintln!("bad frame: {e}"),
}
}
```
## License
Licensed under either of [Apache-2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT) at
your option.