Binary protocol reference

The SDK speaks the same binary protocol as the Rust firmware. The canonical definition lives in omnitiles/src/protocol/messages.rs; this page mirrors it for reference.

Packet format

[START_BYTE] [msg_id] [payload bytes...] [checksum]
    0xA5        u8      0, 1, or 3 bytes      u8

checksum is the 8-bit sum of msg_id and every payload byte (wrapping). The SDK helper is:

from omnitiles.protocol import encode, checksum, MessageId

encode(MessageId.M1_EXTEND, bytes([200]))
# b'\xa5\x30\xc8\xf8'

Command IDs

Name

ID

Payload

Notes

M1_EXTEND

0x30

u8 speed

PWM 0–255

M1_RETRACT

0x31

u8 speed

M1_BRAKE

0x32

M1_SET_POSITION

0x33

u8 scaled

Target along stroke, 0–255

M2_EXTEND

0x40

u8 speed

M2_RETRACT

0x41

u8 speed

M2_BRAKE

0x42

M2_SET_POSITION

0x43

u8 scaled

PING

0x50

Connectivity check

TELEMETRY

0x60

Response-only

BASE_VELOCITY

0x70

i8, i8, i8

vx, vy, omega

BASE_BRAKE

0x71

Telemetry variants

Telemetry packets are identified by total length. The parser validates the checksum and returns a typed Telemetry dataclass.

Length

Fields

7 bytes

m1_pos, m2_pos

15 bytes

Above + d0, d1, d2, d3 UWB ranges

17 bytes

Above + tof

53 bytes

Above + 6-axis IMU + 4 M1 ADCs + 2 M2 ADCs

Invalid UWB ranges and ToF readings are encoded on-wire as 0xFFFF. The parser normalizes them to Python None.

Extending the protocol

When adding a new message ID to the firmware:

  1. Update omnitiles/src/protocol/messages.rs.

  2. Add the corresponding entry to omnitiles.protocol.messages.MessageId.

  3. If it’s a command, add a method to Tile in sdk/src/omnitiles/tile.py.

  4. If it’s a new telemetry variant, add its byte length to _TELEMETRY_LENGTHS in sdk/src/omnitiles/protocol/parser.py and extend _try_parse.

  5. Add a unit test in sdk/tests/test_protocol.py.