Skip to main content

Overview

Synheart Wear supports direct Bluetooth LE connections to any standard heart rate monitor. This enables real-time HR streaming without requiring a cloud API or vendor-specific SDK. Supported devices include:
  • WHOOP (Broadcast Heart Rate mode)
  • Polar chest straps (H10, OH1, Verity Sense)
  • Wahoo TICKR, TICKR X
  • Garmin HRM-Pro, HRM-Dual
  • Gym equipment with BLE HR broadcast
  • Any device implementing the standard BLE Heart Rate Profile (0x180D)

How It Works

The BLE HRM provider connects to devices advertising the standard Bluetooth Heart Rate Service (0x180D), subscribes to the Heart Rate Measurement characteristic (0x2A37), and parses incoming data per the Bluetooth SIG specification.
Phone (iOS/Android)

    │── BLE Scan (filter: 0x180D service)


BLE Heart Rate Monitor

    │── Subscribe to HR Measurement (0x2A37)


Real-time HR samples → HeartRateSample stream

    │── bpm, rr_intervals_ms, device_id, timestamp


Your App

Platform Setup

iOS

Add to Info.plist:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to heart rate monitors.</string>

Android

Add to AndroidManifest.xml:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Note: Android 12+ requires runtime permission requests for BLUETOOTH_SCAN and BLUETOOTH_CONNECT.

Usage

import 'package:synheart_wear/synheart_wear.dart';

final bleHrm = BleHrmBridge();

// Scan for nearby HR monitors
final devices = await bleHrm.scan(
  timeoutMs: 10000,
  namePrefix: 'WHOOP',  // optional filter
);

// Connect to a device
await bleHrm.connect(
  deviceId: devices.first.deviceId,
  sessionId: 'my-session',  // optional
);

// Listen to heart rate samples
bleHrm.heartRateStream.listen((sample) {
  print('BPM: ${sample.bpm}');
  if (sample.rrIntervalsMs != null) {
    print('RR: ${sample.rrIntervalsMs}');
  }
});

// Disconnect when done
await bleHrm.disconnect();

API Reference

Methods

MethodDescriptionReturns
scan(timeoutMs, namePrefix?)Scan for nearby BLE HR monitorsList<BleHrmDevice>
connect(deviceId, sessionId?, enableBattery?)Connect to a device and start HR streamingvoid
disconnect()Disconnect from the current devicevoid
isConnected()Check if a device is currently connectedbool

BleHrmDevice

Returned from scan():
FieldTypeDescription
deviceIdStringBLE device UUID
nameString?Device advertised name
rssiintSignal strength (dBm)

HeartRateSample

Emitted on the heart rate stream:
FieldTypeDescription
tsMsintPhone receipt timestamp (ms since epoch)
bpmintHeart rate in beats per minute
sourceStringAlways "ble_hrm"
deviceIdStringBLE device UUID
deviceNameString?Device advertised name
sessionIdString?Session tag (passed during connect)
rrIntervalsMsList<double>?RR intervals in milliseconds
Note: rrIntervalsMs availability depends on the device. Polar chest straps typically include RR intervals; WHOOP Broadcast typically does not.

Error Codes

CodeMeaning
PERMISSION_DENIEDBluetooth permission not granted
BLUETOOTH_OFFBluetooth adapter is disabled
DEVICE_NOT_FOUNDDevice not found or connection timed out
SUBSCRIBE_FAILEDFailed to subscribe to HR characteristic
DISCONNECTEDDevice disconnected unexpectedly

WHOOP Broadcast Setup

To use WHOOP as a BLE heart rate monitor:
  1. Open the WHOOP app on your phone
  2. Go to Device Settings (tap your WHOOP device)
  3. Enable Broadcast Heart Rate
  4. Your WHOOP will now appear in BLE scans as a standard HR monitor
Note: WHOOP Broadcast HR provides BPM only (no RR intervals). For full WHOOP data (recovery, sleep, strain), use the WHOOP cloud provider instead.

Reconnection

The BLE HRM provider automatically handles disconnections:
  • 3 retry attempts with exponential backoff (1s, 2s, 4s)
  • After retries are exhausted, a DISCONNECTED error is emitted on the stream
  • Your app can then prompt the user to reconnect

Limitations

  • Single device: Only one BLE HRM device can be connected at a time (v1)
  • Phone-side only: Requires the phone to be in BLE range of the HR monitor
  • No HRV calculation: Raw RR intervals are provided when available, but HRV calculation is not performed by the provider
  • Battery dependent: Battery level monitoring is optional (enableBattery: true during connect)

Architecture

The BLE HRM provider is implemented natively on each platform:
PlatformImplementationStreaming Pattern
iOSCoreBluetooth (CBCentralManager)AsyncStream<HeartRateSample>
AndroidAndroid BLE (BluetoothLeScanner, BluetoothGatt)SharedFlow<HeartRateSample>
FlutterPlatform Channels (MethodChannel + EventChannel)Stream<HeartRateSample>
All platforms parse HR data identically per the Bluetooth SIG Heart Rate Profile specification (flags byte, uint8/uint16 BPM, optional RR intervals at 1/1024s resolution).
Author: Israel Goytom