ble_gatt_parser
Turn the raw bytes from Bluetooth heart-rate straps, blood pressure cuffs, thermometers, scales, glucose meters, pulse oximeters and cycling or running sensors into typed Dart objects.
Your BLE plugin hands you a List<int>. This package tells you what it means.
final value = GattParser.parse('2a37', [0x16, 0x50, 0x00, 0x04, 0x80, 0x03]);
// HeartRateMeasurement(bpm: 80, sensorContact: detected, rrIntervalsMs: [1000.0, 875.0])
- Works with any BLE plugin: flutter_blue_plus, flutter_reactive_ble, universal_ble, or your own platform channels
- Pure Dart: no native code, so it runs on Android, iOS, web, desktop and the server
- No dependencies
- Follows the Bluetooth SIG specs: every optional field, flag, unit and IEEE 11073 SFLOAT/FLOAT special value
- Type-safe: results are a sealed class, so a
switchcan handle every case
Supported characteristics
| Characteristic | UUID | Devices | Result |
|---|---|---|---|
| Heart Rate Measurement | 2A37 |
Chest straps, watches | HeartRateMeasurement (bpm, RR-intervals for HRV, energy, skin contact) |
| Body Sensor Location | 2A38 |
Heart-rate sensors | BodySensorLocation |
| Blood Pressure Measurement | 2A35 |
BP monitors | BloodPressureMeasurement (systolic, diastolic, MAP, pulse, status flags) |
| Intermediate Cuff Pressure | 2A36 |
BP monitors | BloodPressureMeasurement |
| Temperature Measurement | 2A1C |
Thermometers | TemperatureMeasurement (°C/°F, body site) |
| Intermediate Temperature | 2A1E |
Thermometers | TemperatureMeasurement |
| Weight Measurement | 2A9D |
Smart scales | WeightMeasurement (kg/lb, BMI, height, user) |
| Glucose Measurement | 2A18 |
Glucose meters | GlucoseMeasurement (mg/dL and mmol/L, sample type and site) |
| PLX Continuous Measurement | 2A5F |
Pulse oximeters | PlxContinuousMeasurement (SpO2, pulse, perfusion index) |
| PLX Spot-check Measurement | 2A5E |
Pulse oximeters | PlxSpotCheckMeasurement |
| Cycling Speed and Cadence | 2A5B |
Wheel and crank sensors | CscMeasurement (with speed and cadence helpers) |
| Cycling Power Measurement | 2A63 |
Power meters, smart trainers | CyclingPowerMeasurement (all 13 optional fields) |
| RSC Measurement | 2A53 |
Running foot pods | RscMeasurement (speed, pace, cadence, stride, distance) |
| Battery Level | 2A19 |
Almost everything | BatteryLevel |
| Device Name and Device Information strings | 2A00, 2A24–2A29 |
Almost everything | GattString |
Install
dart pub add ble_gatt_parser
Usage
With flutter_blue_plus
import 'package:ble_gatt_parser/ble_gatt_parser.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
Future<void> listen(BluetoothCharacteristic characteristic) async {
characteristic.onValueReceived.listen((bytes) {
final value = GattParser.tryParse(characteristic.uuid, bytes);
if (value is HeartRateMeasurement) {
print('${value.bpm} bpm');
}
});
await characteristic.setNotifyValue(true);
}
With flutter_reactive_ble
ble.subscribeToCharacteristic(characteristic).listen((bytes) {
switch (GattParser.tryParse(characteristic.characteristicId, bytes)) {
case BloodPressureMeasurement(:final systolic, :final diastolic):
print('$systolic/$diastolic');
case _:
break;
}
});
GattParser accepts the UUID as an int (0x2A37), a short string ('2a37'), a full 128-bit string, or your plugin's UUID object.
Parse one characteristic directly
If you already know what you're reading, call its parse constructor:
final bp = BloodPressureMeasurement.parse(bytes);
if (bp.status?.irregularPulse ?? false) {
showWarning('Irregular heartbeat detected');
}
Cadence and speed from cycling sensors
Cycling sensors send running totals that wrap around. Compare two notifications to get live numbers, and the wrap-around is handled for you:
CscMeasurement? previous;
void onCsc(List<int> bytes) {
final current = CscMeasurement.parse(bytes);
if (previous != null) {
final rpm = current.cadenceSince(previous!);
final speed = current.speedSince(previous!, wheelCircumferenceM: 2.105);
print('Cadence: $rpm rpm, speed: ${(speed ?? 0) * 3.6} km/h');
}
previous = current;
}
Errors
GattParser.parsethrows anArgumentErrorfor characteristics it doesn't support, and aGattFormatExceptionif the bytes are shorter than their flags say they are.GattParser.tryParsereturnsnullinstead of throwing.GattParser.supports(uuid)tells you in advance.
Good to know
- Time stamps from devices carry no time zone, so they're returned as local
DateTimes, as the device's clock showed them. - Readings a device marks as unavailable (IEEE 11073 NaN or NRes) come back as
double.nan. - Enum codes newer than the spec this package follows come back as
unknown, not as an error.
Contributing
Issues and pull requests are welcome, especially byte captures from real devices that parse incorrectly.
About the author
Built by Chandra ObulaReddy, a Flutter architect who has shipped Bluetooth LE and wearable integrations for safety, healthcare and fitness products.
Libraries
- ble_gatt_parser
- Parses standard Bluetooth Low Energy GATT characteristics into typed Dart objects.