| title | App-Device Protocol |
|---|---|
| icon | file-code |
| description | BLE protocol specification for communicating with Omi wearable devices |
This document describes the Bluetooth Low Energy (BLE) protocol used to communicate between the Omi app and device. Use this reference when building custom apps or SDKs.
The official app discovers the device by scanning for BLE devices with the name Omi.
The Omi wearable implements three BLE services:
Standard BLE battery level monitoring Device metadata and firmware version Audio streaming and codec configurationBattery notifications require firmware v1.5 or later. Earlier versions (1.0.x) do not support notifications.
| Property | Value |
|---|---|
| Service UUID | 0x180F (standard) |
| Characteristic UUID | 0x2A19 (Battery Level) |
| Supports Notifications | Yes (firmware v1.5+) |
This is the standard BLE Battery Service.
Available since firmware version 1.0.3
| Property | Value |
|---|---|
| Service UUID | 0x180A (standard) |
This is the standard BLE Device Information Service.
| Characteristic | UUID | Value |
|---|---|---|
| Manufacturer Name | 0x2A29 |
"Based Hardware" |
| Model Number | 0x2A24 |
"Omi" |
| Hardware Revision | 0x2A27 |
"Seeed Xiao BLE Sense" |
| Firmware Revision | 0x2A26 |
e.g., "1.0.3" |
This is the main service for streaming audio from the device to the app.
| Property | Value |
|---|---|
| Service UUID | 19B10000-E8F2-537E-4F6C-D104768A1214 |
| Characteristic | UUID | Purpose |
|---|---|---|
| Audio Data | 19B10001-E8F2-537E-4F6C-D104768A1214 |
Audio stream from device |
| Codec Type | 19B10002-E8F2-537E-4F6C-D104768A1214 |
Audio codec identifier |
The codec type characteristic determines how to decode the audio data:
| Value | Codec | Sample Rate | Bit Depth |
|---|---|---|---|
0 |
PCM | 16 kHz | 16-bit mono |
1 |
PCM | 8 kHz | 16-bit mono |
10 |
Mu-law | 16 kHz | 8-bit mono |
11 |
Mu-law | 8 kHz | 8-bit mono |
20 |
Opus | 16 kHz | 16-bit mono |
Audio data is sent as BLE notifications on the Audio Data characteristic.
flowchart LR
subgraph Header["Header (3 bytes)"]
PN[Packet Number<br/>2 bytes]
IDX[Index<br/>1 byte]
end
subgraph Payload["Audio Payload"]
DATA[Audio Samples<br/>160 samples/packet]
end
Header --> Payload
| Bytes | Field | Description |
|-------|-------|-------------|
| 0-1 | Packet Number | Overall packet counter (0-65535, wraps around) |
| 2 | Index | Position of this value within the current packet |
**Example:** PCM 16-bit at 16 kHz
- 160 samples × 2 bytes = 320 bytes per packet
- On iOS devices, this typically results in:
- Notification 1: packet n, index 0, 251 bytes
- Notification 2: packet n+1, index 1, 75 bytes
# Example: Parsing audio packet header
def parse_audio_header(data: bytes):
packet_number = int.from_bytes(data[0:2], 'little')
index = data[2]
audio_data = data[3:]
return packet_number, index, audio_dataPython SDK with Opus decoding Native iOS SDK Cross-platform mobile SDK Compile custom firmware