Skip to content

Data & Protocol

Two layers: the raw formats the firmware transmits, and UF1, the interchange format the tools consume. Know both and you can parse the data directly or write an adapter for your own tool.

Firmware → raw binary (BLE GATT / base serial) → Adapter → UF1 UDP → Tools

The firmware does not send UF1. It emits raw binary — GATT notifications over BLE, or unframed serial from the base. A small adapter reads that and emits UF1 over UDP. Any software that reads a device’s raw format and outputs valid UF1 is a conforming adapter — that’s how third-party sensors join the ecosystem. Existing adapters: the Android app (BLE → UF1) and the gesture-lab host (base serial → UF1).

The base forwards the direct radio over USB serial, unframed. 921 600 baud. Sync: [0] 0x4F [1] 0xD5 [2] rssi [3…] nRF24 payload (length = payload byte [1]).

The nRF24 payload — 62 bytes (v3_1), big-endian:

OffFieldNotes
0packet_idrolling 0–128
1packet_lentotal length — use as boundary, don’t hardcode 62
2–5unit_idu32 device ID
6packet_typeEMG count = packet_type − 80 (valid range 80–120)
8batteryraw → mV = 2000 + raw × 10
11adc_data_id8-bit sample counter, wraps at 256
12–27emg8 × int16
28–35fft4 bins (unsigned)
36–43quatw,x,y,z — ±32000 = ±1.0
44–49accax,ay,az — 16384 LSB/g (±2 g)
50–55euler or gyroshipped fw: euler (dropped); gyro fw: gx,gy,gz ±500 dps
56–61magmx,my,mz

The base path is the only one carrying the FFT bins in every packet.

Raw binary GATT notifications, little-endian, with a format byte at position 0 that selects the profile (auto-chosen by negotiated MTU):

ByteProfileMin MTUContents
0x01S1 (low-MTU fallback)20EMG + QUAT combined
0x02S2 EMG5324 raw EMG samples
0x03S2 aux27IMU + MAG + QUAT

No FFT over BLE (compute it receive-side). Cheap CSR8510- / HM-10-class dongles can’t reach MTU ≥ 53 → no full-stream S2.

Little-endian, one frame per UDP datagram, port 26750. Frame = 24-byte header + TLV blocks (+ optional CRC32). Header: magic “UD”, version 2, device_id, seq (per device), t_us (µs receive-time). Blocks are type · len · value — skip unknown types by len:

TypeBlockContents
0x01EMG_RAWchannel_count, samples/ch, int16 samples
0x02EMG_FFT44 × int16 bins
0x03IMU_6DOFax,ay,az,gx,gy,gz
0x04MAG_3mx,my,mz
0x05QUATw,x,y,z (±32000 = ±1.0)
0x06STATUSsample counter, rate, battery %, RSSI, mode
0x07DEVICE_NAMEUTF-8 label

Timing: t_us (wall-clock rx) aligns across devices; t_src_sample in STATUS (the device’s own sample counter) gives intra-device sample precision. Frames co-emitted from one raw packet share t_src_sample. This is what makes multi-device recordings time-alignable.

EMG ~1150 Hz (8 samples/frame → ~144 fps) · IMU & quaternion ~50 Hz · FFT 4 bins.

Full spec and reference adapters → uf1-tools.