Overview
The Health Monitor feature provides real-time streaming of heart rate, RR intervals, and other physiological data from the Whoop 4.0. This is the same data stream shown in the “Health Monitor” section of the Whoop app.
Health Monitor Commands
Health Monitor is toggled using commands to CMD_TO_STRAP:
aa0800a823 05 03 00 e44e25be # Health Monitor off
aa0800a823 06 03 01 2bc064cb # Health Monitor on
aa0800a823 66 74 01 3ae4cde3 # Alternate on command
Packet Structure
| Offset | Size | Field | Description |
|---|
| 0x00 | 5 bytes | Header | Always aa0800a823 |
| 0x05 | 1 byte | Packet Count | Increments with each packet |
| 0x06 | 1 byte | Category | 0x03 for health monitor |
| 0x07 | 1 byte | State | 0x00 = Off, 0x01 = On |
| 0x08 | 4 bytes | Checksum | CRC-32 with custom parameters |
Enabling Health Monitor
# Turn ON health monitor
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX \
--char-write -a 0x0010 -n aa0800a82306030 12bc064cb
# Turn OFF health monitor
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX \
--char-write -a 0x0010 -n aa0800a82305030 0e44e25be
Using Python
Listen for health monitor data on DATA_FROM_STRAP:
python3 enable_notifications.py --address XX:XX:XX:XX:XX:XX \
61080005-8d6d-82b8-614a-1c8cb0f8dcc6
When Health Monitor is active, the device sends 24-byte packets on DATA_FROM_STRAP every second:
Header Unix S HR RR RR data Checksum
aa1800ff2802 ad896566 f065 42 01 67060000000000000101 3ba00d4d
aa1800ff2802 ae896566 f860 43 00 00000000000000000101 5025f793
aa1800ff2802 af896566 085c 42 00 00000000000000000101 add7df13
aa1800ff2802 b0896566 1057 42 00 00000000000000000101 24b22179
aa1800ff2802 b1896566 1852 42 00 00000000000000000101 e0a905b8
aa1800ff2802 b2896566 284d 42 00 00000000000000000101 d943226a
aa1800ff2802 b3896566 3848 43 00 00000000000000000101 28364865
aa1800ff2802 b4896566 4043 43 00 00000000000000000101 9c2e99ba
aa1800ff2802 b5896566 503e 43 00 00000000000000000101 ebb8a1ce
aa1800ff2802 b6896566 5039 43 00 00000000000000000101 d1635459
aa1800ff2802 b7896566 6834 43 00 00000000000000000101 ccefa569
aa1800ff2802 b8896566 702f 43 00 00000000000000000101 8770ff99
aa1800ff2802 b9896566 802a 43 00 00000000000000000101 d2299e02
aa1800ff2802 ba896566 8825 44 00 00000000000000000101 3cd6a988
aa1800ff2802 bb896566 9020 44 00 00000000000000000101 94f13f2f
aa1800ff2802 bc896566 a01b 44 00 00000000000000000101 c8ed7785
aa1800ff2802 bd896566 b016 44 00 00000000000000000101 3b07bb4b
Packet Structure
| Offset | Size | Field | Description |
|---|
| 0x00 | 5 bytes | Header | Always aa1800ff2802 |
| 0x05 | 4 bytes | Unix Time | Little-endian timestamp |
| 0x09 | 2 bytes | Unknown | Purpose unclear |
| 0x0B | 1 byte | Heart Rate | BPM value |
| 0x0C | 1 byte | RR Count | Number of RR intervals |
| 0x0D | 8 bytes | RR Data | RR interval values |
| 0x15 | 3 bytes | Unknown | Purpose unclear |
| 0x18 | 4 bytes | Checksum | CRC-32 |
Example: Parsing Heart Rate
From the first packet:
aa1800ff2802 ad896566 f065 42 01 67060000000000000101 3ba00d4d
- Heart Rate (byte 0x0B):
0x42 = 66 BPM
- RR Count (byte 0x0C):
0x01 = 1 RR interval
- RR Data (bytes 0x0D-0x14):
67060000000000000101
Heart Rate Progression
Looking at the sequence:
| Time | HR Byte | Heart Rate (BPM) |
|---|
| ad896566 | 42 | 66 |
| ae896566 | 43 | 67 |
| af896566 | 42 | 66 |
| b0896566 | 42 | 66 |
| b1896566 | 42 | 66 |
| b2896566 | 42 | 66 |
| b3896566 | 43 | 67 |
| b4896566 | 43 | 67 |
| b5896566 | 43 | 67 |
| b6896566 | 43 | 67 |
| b7896566 | 43 | 67 |
| b8896566 | 43 | 67 |
| b9896566 | 43 | 67 |
| ba896566 | 44 | 68 |
| bb896566 | 44 | 68 |
| bc896566 | 44 | 68 |
| bd896566 | 44 | 68 |
The heart rate is increasing from 66 to 68 BPM over this period.
Data vs. Activity Tracking
Health Monitor data uses the exact same packet format as Activity Tracking data. Both features stream to DATA_FROM_STRAP with identical structure.
The only difference is the control command:
- Activity Tracking: Category
0x03, various start/stop commands
- Health Monitor: Category
0x03 or 0x74, on/off toggle
Characteristics Used
CMD_TO_STRAP (Control)
- UUID:
61080002-8d6d-82b8-614a-1c8cb0f8dcc6
- Handle:
0x0010
- Properties: Write only
- Purpose: Send on/off commands
DATA_FROM_STRAP (Health Data)
- UUID:
61080005-8d6d-82b8-614a-1c8cb0f8dcc6
- Handle:
0x0018
- Properties: Notify
- Purpose: Receive real-time health metrics
- Update Rate: ~1 second
Stopping Health Monitor
Send the off command to stop the data stream:
sudo gatttool -i hci0 -t random -b XX:XX:XX:XX:XX:XX \
--char-write -a 0x0010 -n aa0800a82305030 0e44e25be
Notifications on DATA_FROM_STRAP will stop immediately.
RR Interval Data
The 8 bytes of RR data (bytes 0x0D-0x14) contain RR interval information:
- RR Count indicates how many RR intervals are included
- When count is
0x00, RR data bytes are all zeros
- When count is
0x01 or higher, RR values are encoded in the data
The exact encoding format for RR intervals is not fully documented. The values may be compressed or require scaling to convert to standard millisecond intervals.
Example RR Data
RR Count: 01, RR Data: 67060000000000000101
RR Count: 00, RR Data: 00000000000000000101
RR Count: 02, RR Data: b802b90200000000
Notice that:
- Count
0x01 has data in first bytes: 6706
- Count
0x00 has all zeros in RR section
- Count
0x02 has two values: b802 and b902
Use Cases
- Real-time HR monitoring: Display live heart rate in custom apps
- HRV analysis: Extract RR intervals for heart rate variability calculations
- Biofeedback: Use live data for meditation, breathing exercises, or training
- Research: Collect raw physiological data for studies
- Third-party integrations: Stream Whoop data to other platforms
For broadcasting heart rate to standard BLE Heart Rate Service, see Heart Rate Broadcast.
For starting activities that also stream similar data, see Activity Tracking.