This information is only confirmed for Oura Ring 4. Furter testing is needed for other models.
Parameter | Value |
---|---|
Channel | 0x0004 |
Handle (Write Request) | 0x0015 |
Handle (Handle Value Notification) | 0x0012 |
Byte order | Little endian |
The communication looks as follows:
- Client sends the request in form of a BLE Write Request, the associated Write Response will be always empty.
- After the ring processes the request, BLE Handle Value Notification with the data will be sent.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Request tag | 08 |
1 | uint8 | |
Payload length | 03 |
1 | uint8 | |
Payload | 000000 |
varies | bytes |
Response comes in form of Handle Value Notification
, same format as request.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Major | 02 |
1 | uint8 | |
Minor | 00 |
1 | uint8 | |
Patch | 0A |
1 | uint8 |
Write Request handle: 0x0013
No payload, no response observed.
Example: 0100
.
Write Request handle: 0x0004
No payload, no response observed.
Example: 0200
.
Not observed.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Unknown | 000000 |
3 | bytes |
Example: 0803 0000 00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
API version | 0112 01 |
3 | semver | |
Firmware version | 0200 02 |
3 | semver | |
Bootloader version | 0100 03 |
3 | semver | |
Bluetooth stack version | 0500 04 |
3 | semver | |
MAC address | 1111 2222 3333 |
6 | bytes | Probably prepending some constant to this, since MAC should be 8 bytes... |
Example: 0912 0112 0102 0002 0100 0305 0004 1111 2222 3333
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Unknown | FFFF FFFF |
8 | bytes |
Example: 0A04 FFFF FFFF
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Unknown | FFFF FFFF |
8 | bytes |
Example: 0B04 FFFF FFFF
No payload.
Example: 0C00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Battery level | 64 |
1 | uint8 | |
Charging progress | 00 |
1 | uint8 | |
Charging recommended | 00 |
1 | uint8 | |
Unknown | FFFF FF |
3 | bytes |
Example: 0D06 6400 00FF FFFF
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Flags | 00 |
1 | uint8 |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status | 00 |
1 | uint8 (Start firmware update response) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Start timestamp | B0E8 A900 |
4 | uint32 | |
Maximum number of events | FF |
1 | uint8 | |
Unknown | FFFF FFFF |
4 | int32 | Always seems to equal -1 . |
Example: 1009 B0E8 A900 FFFF FFFF FF
If response tag is greater than or equal to 0x41
, the packet is treated as a response.
See: Events
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Events received | 00 |
1 | uint8 | |
Sleep analysis progress (optional) | 00 |
1 | uint8 | |
Bytes left (optional) | 0000 0000 |
4 | uint32 | |
Unknown (optional) | 0300 |
2 | uint16 |
Denotes the end of event data.
Example: 1108 0000 0000 0000 0300
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Unix timestamp | BC61 1000 0000 0000 |
8 | uint64 | seconds |
Timezone offset | 04 |
1 | uint8 | measured in half-hours from UTC (getOffset() / 1800000 ) |
Example: 1209 BC61 1000 0000 0000 04
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Current device timestamp | 4BED A900 |
4 | uint32 | seconds |
Unknown (Status?) | 00 |
1 | uint8 |
Example: 1305 4BED A900 00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Mode | 01 |
1 | uint8 |
Example: 1601 01
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Mode | 01 |
1 | uint8 |
Example: 1701 01
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Type | 0800 10 |
3 | bytes |
Example: 1803 0800 10
No payload.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status | 0000 |
2 | uint16 |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Flags | 3F |
1 | uint8 |
Example: 1C01 3F
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Unknown | 00 |
1 | uint8 |
Example: 1D01 00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
User info type | 02 |
1 | uint8 (User info type) | |
Data | 00 |
varies | varies | See: User info type for details. |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
User info type | 02 |
1 | uint8 (User info type) | |
Result | 00 |
1 | uint8 (User info result) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Key | 1111 2222 3333 4444 5555 6666 7777 8888 |
16 | bytes |
Example: 2410 1111 2222 3333 4444 5555 6666 7777 8888
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status (?) | 00 |
1 | uint8 |
Example: 2501 00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Unknown | 6027 |
2 | bytes |
Example: 2602 6027
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Mode | 01 |
1 | uint8 | |
Payload | 0200 0000 0000 0000 0000 0000 00 |
varies | bytes | See below. |
No payload.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Application ID | 02 (TODO: find) |
1 | uint8 | |
Version | 0000 00 |
3 | semver | |
Start address | 0000 0000 |
4 | int32 | |
Image length | 0000 0000 |
4 | int32 |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Application ID | 02 (TODO: find) |
1 | uint8 | |
Block type | 00 |
1 | uint8 | |
Block index | 0000 |
2 | uint16 | |
Block size | 0000 |
2 | uint16 | |
Number of packets | 00 |
1 | uint8 | |
CRC32 | 0000 0000 |
4 | int32 |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Application ID | 02 (TODO: find) |
1 | uint8 | |
CRC32 | 0000 0000 |
4 | int32 | |
Force flags (optional) | 00 |
1 | uint8 |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Request ID | 01 |
1 | uint8 | |
Payload | FF |
varies | bytes | See below. |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Page | FF |
1 | uint8 |
Example: 2F01 FF
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Bundling enabled | 01 |
1 | uint8 |
Example: 2F02 0301
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Bundling enabled | 01 |
1 | uint8 |
Example: 2F02 0401
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) |
Example: 2F02 200C
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) | |
Mode | 00 |
1 | uint8 (Feature mode) | |
Status value | 00 |
1 | uint8 (Feature status value) | |
State | 00 |
1 | uint8 (Feature state) | |
Subscription mode | 00 |
1 | uint8 (Feature subscription mode) |
Example: 2F06 210C 0000 0000
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) | |
Mode | 00 |
1 | uint8 (Feature mode) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) | |
Subscription mode | 00 |
1 | uint8 (Feature subscription mode) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) | |
Result | 01 |
1 | uint8 (Feature set subscription mode result) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) | |
Bytes | 0000 0000 |
varies | bytes |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Feature ID | 0C |
1 | uint8 (Feature ID) | |
Status | 00 |
1 | uint8 |
No payload.
Example: 2F01 2B
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Nonce | 1111 2222 3333 4444 5555 6666 7777 88 |
15 | bytes |
Example: 2F10 2C11 1122 2233 3344 4455 5566 6677 7788
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Encrypted nonce | 1111 2222 3333 4444 5555 6666 7777 8888 |
16 | bytes |
Example: 2F11 2D11 1122 2233 3344 4455 5566 6677 7788 88
Auth nonce is encrypted using AES-CBC (using the key provided in Set Auth Key beforehand), with PKCS5 padding.
Auth key can also be retrieved from application data, see: Storage.
In node.js this can be emulated using AES-ECB with empty IV:
import { createCipheriv } from "crypto";
function encryptNonce(key: Uint8Array, nonce: Uint8Array) {
const cipher = createCipheriv("aes-128-ecb", key, "");
cipher.setAutoPadding(true);
return Buffer.concat([cipher.update(nonce), cipher.final()]);
}
Field | Example | Length | Type | Notes |
---|---|---|---|---|
State | 00 |
1 | uint8 (Authentication result) |
Example: 2F02 2E00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Force sleep analysis | 00 |
1 | uint8 |
Example: 2801 00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status | 00 |
1 | uint8 |
Example: 2901 00
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Ring mode | 00 |
1 | uint8 (Ring mode) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status | 0000 |
2 | uint16 |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Manufacturing mode | 0300 0000 |
4 | bytes (Ring manufacturing mode) |
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status | 00 |
1 | uint8 |
No payload.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Status | 00 |
1 | uint8 |
Similar format to base packet, can be packed as multiple inside of one Handle Value Notification.
Field | Example | Length | Type | Notes |
---|---|---|---|---|
Event tag | 61 |
1 | uint8 (Event tag) | Must be greater than or equal to 0x41 . |
Length | 10 |
1 | uint8 | |
Timestamp | B0E8 A900 |
4 | uint32 | Measured in seconds since device boot. (Reference value can be obtained from Sync Time). |
Payload | 1A18 0025 0000 0000 0000 00F7 |
varies | bytes |
Example: 6110 B0E8 A900 1A18 0025 0000 0000 0000 00F7
Read by Type request
Start: 0x0005 - 0x000F
0805 000F 0000 2A
1F04 2003 0200
uint8
Ring mode | Value |
---|---|
Normal | 0x00 |
Fast heart rate measurement | 0x01 |
Deep sleep | 0x02 |
uint8
Ring mode | Value |
---|---|
Test | 0x03000000 |
Production | 0x07000000 |
uint8
Authentication result | Value |
---|---|
Success | 0x00 |
Battery level too low | 0x01 |
Sleep analysis in progress | 0x02 |
uint8
Event tag | Value |
---|---|
Ring start | 0x41 |
Time sync | 0x42 |
Debug event | 0x43 |
IBI event | 0x44 |
State change | 0x45 |
Temp event | 0x46 |
Motion event | 0x47 |
Sleep period information | 0x48 |
Sleep summary (1) | 0x49 |
PPG amplitude | 0x4A |
Sleep phase information | 0x4B |
Sleep summary (2) | 0x4C |
Ring sleep feature information | 0x4D |
Sleep phase details | 0x4E |
Sleep summary (3) | 0x4F |
Activity information | 0x50 |
Activity summary (1) | 0x51 |
Activity summary (2) | 0x52 |
Wear event | 0x53 |
Recovery summary | 0x54 |
Sleep heart rate | 0x55 |
Alert event | 0x56 |
Ring sleep feature information (2) | 0x57 |
Sleep summary (4) | 0x58 |
EDA event | 0x59 |
Sleep phase data | 0x5A |
BLE connection | 0x5B |
User information | 0x5C |
HRV event | 0x5D |
Self-test event | 0x5E |
Raw ACM event | 0x5F |
IBI and amplitude event | 0x60 |
Debug data | 0x61 |
On-demand MEAs | 0x62 |
PPG peak event | 0x63 |
Raw PPG event | 0x64 |
On-demand session | 0x65 |
On-demand motion | 0x66 |
Raw PPG summary | 0x67 |
Raw PPG Data | 0x68 |
Temp period | 0x69 |
Sleep period information (2) | 0x6A |
Motion period | 0x6B |
Feature session | 0x6C |
MEAs quality event | 0x6D |
SPO2 IBI and amplitude event | 0x6E |
SPO2 event | 0x6F |
SPO2 smoothed event | 0x70 |
Green IBI and amplitude event | 0x71 |
Sleep ACM period | 0x72 |
EHR trace event | 0x73 |
EHR ACM intensity event | 0x74 |
Sleep temp event | 0x75 |
Bedtime period | 0x76 |
SPO2 DC event | 0x77 |
Self-test data event | 0x79 |
Tag event | 0x7A |
Real step event feature (1) | 0x7E |
Real step event feature (2) | 0x7F |
CVA raw PPG data | 0x81 |
Scan start | 0x82 |
Scan end | 0x83 |
uint8
Authentication result | Value |
---|---|
Success | 0x00 |
Authentication error | 0x01 |
In factory reset | 0x02 |
Not original onboarded device | 0x03 |
uint8
Feature | Value |
---|---|
Background DFU | 0x00 |
Research Data | 0x01 |
Daytime HR | 0x02 |
Exercise HR | 0x03 |
SPO2 | 0x04 |
Bundling | 0x05 |
Encrypted API | 0x06 |
Tap to tag | 0x07 |
Resting HR | 0x08 |
App auth | 0x09 |
BLE mode | 0x0A |
Real steps | 0x0B |
Experimental | 0x0C |
CVA PPG Sampler | 0x0D |
uint8
Feature mode | Value |
---|---|
Off | 0x00 |
Automatic | 0x01 |
Requested | 0x02 |
Requested subscription | 0x03 |
uint8
Status value | Value |
---|---|
Off | 0x00 |
On | 0x01 |
Searching | 0x02 |
No reliable PPG signal | 0x03 |
Cold fingers | 0x04 |
Too much movements | 0x05 |
Identifying signal | 0x06 |
uint8
State | Value |
---|---|
Idle | 0x00 |
Scanning | 0x01 |
Measuring | 0x02 |
Postprocessing | 0x03 |
uint8
Subscription mode | Value |
---|---|
Off | 0x00 |
State | 0x01 |
Latest | 0x02 |
uint8
Result | Value |
---|---|
Not supported | 0x01 |
Not available | 0x02 |
Not in finger | 0x03 |
Message too short | 0x04 |
Low battery | 0x05 |
uint8
Status | Value |
---|---|
Success | 0x00 |
Incomplete image | 0x01 |
Image validation failed | 0x02 |
Downgrade not allowed | 0x03 |
Other error | 0x04 |
Battery low | 0x05 |
Sleep | 0x06 |
Disabled | 0x07 |
Rdata | 0x08 |
Sync time | 0x09 |
uint8
Type | Value |
---|---|
None | 0x00 |
EIV | 0x01 |
Image | 0x02 |
Signature | 0x03 |
uint8
User info type | Value | Data type | Notes |
---|---|---|---|
Gender | 0x02 |
uint8 | Currently unused (always 0x00 ) |
Height | 0x03 |
uint16 | Currently unused (always 0x0000 ) |
Weight | 0x04 |
uint16 | Currently unused (always 0x0000 ) |
Date of birth | 0x05 |
uint64 | Seems to be a timestamp. |
Unit system | 0x06 |
uint8 | Currently unused (always 0x0000 ) |
uint8
User info result | Value |
---|---|
Success | 0x00 |
Invalid start offset address | 0x02 |
Unknown parameter identifier | 0x03 |
File system busy | 0x05 |