Search code examples
flutterfloating-pointbluetooth-lowenergydeviceieee-11073

Correcting Raw Data Reading Issue in flutter


I received raw data for blood pressure measurement: [DE, E2, F4, 3E, F3, FF, 07, E8, 07, 03, 13, 02, 21, 00, 34, F3, 01, C0, 00]. I managed to read the values of systolic and diastolic pressure, and pulse. However, when I tried to read the value of mean arterial pressure (meanAP), which should normally take two bytes (FF,07) at index 5 and 6, I received meanAP=-20.41 mmHg. Whereas using nRF Connect, it's equal to 2047.0 mmHg. How can I correct this in my code below, please?

import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'dart:typed_data';
import 'dart:math';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BLE Scanner',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final FlutterReactiveBle flutterReactiveBle = FlutterReactiveBle();
  List<DiscoveredDevice> devices = [];
  double systolic = 0;
  double diastolic = 0;
  List<double> result = [
    0.0,
    0.0,
    0.0,
    0.0,
  ]; // Déclaration de la variable result
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Mesurer votre Pression Arterielle'),
        backgroundColor: Colors.blue,
      ),
      body: Padding(
        padding: EdgeInsets.all(20.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: _scanForDevices,
              child: Text('Rechercher votre appareil BM96'),
              style: ElevatedButton.styleFrom(
                foregroundColor: Colors.white,
                backgroundColor: Colors.blue,
              ),
            ),
            SizedBox(height: 20),
            Text(
              'Discovered Devices:',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.blue,
              ),
            ),
            SizedBox(height: 10),
            Expanded(
              child: ListView.builder(
                itemCount: devices.length,
                itemBuilder: (context, index) {
                  final device = devices[index];
                  return ListTile(
                    title: Text(
                      device.name ?? 'Unknown',
                      style: TextStyle(
                        fontSize: 16,
                        color: Colors.black,
                      ),
                    ),
                    subtitle: Text(
                      device.id,
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.grey,
                      ),
                    ),
                    onTap: () {
                      _connectToDevice(device.id);
                      _subscribeToBloodPressureMeasurement(device.id);
                    },
                  );
                },
              ),
            ),
            SizedBox(height: 20),
            Text(
              'Result Systolic: ${result[0]}',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 10),
            Text(
              'Result Diastolic: ${result[1]}',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 10),
            Text(
              'Result pulse: ${result[2]}',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 10),
            Text(
              'Result MeanAP: ${result[3]}',
              style: TextStyle(fontSize: 20),
            ),
          ],
        ),
      ),
    );
  }

  void _scanForDevices() {
    flutterReactiveBle.scanForDevices(
      withServices: [Uuid.parse('00001810-0000-1000-8000-00805f9b34fb')],
      scanMode: ScanMode.lowLatency,
    ).listen(
      (device) {
        final existingDeviceIndex =
            devices.indexWhere((d) => d.id == device.id);
        if (existingDeviceIndex != -1) {
          setState(() {
            devices[existingDeviceIndex] = device;
          });
        } else {
          setState(() {
            devices.add(device);
          });
        }
      },
      onError: (error) {
        print('Error: $error');
      },
    );
  }

  void _connectToDevice(String deviceId) {
    flutterReactiveBle
        .connectToDevice(
      id: deviceId,
      servicesWithCharacteristicsToDiscover: {
        Uuid.parse('00001810-0000-1000-8000-00805f9b34fb'): [
          Uuid.parse('00002a35-0000-1000-8000-00805f9b34fb'),
          Uuid.parse('00002a49-0000-1000-8000-00805f9b34fb'),
        ],
      },
      connectionTimeout: const Duration(seconds: 2),
    )
        .listen(
      (connectionState) {
        if (connectionState == DeviceConnectionState.connected) {
          print("Connected successfully to device: $deviceId");
          _subscribeToBloodPressureMeasurement(deviceId);
        }
      },
      onError: (error) {
        print('Error connecting to device: $error');
      },
    );
  }

  void _subscribeToBloodPressureMeasurement(String deviceId) {
    try {
      final bloodPressureCharacteristic = QualifiedCharacteristic(
        characteristicId: Uuid.parse('00002a35-0000-1000-8000-00805f9b34fb'),
        deviceId: deviceId,
        serviceId: Uuid.parse('00001810-0000-1000-8000-00805f9b34fb'),
      );

      flutterReactiveBle
          .subscribeToCharacteristic(bloodPressureCharacteristic)
          .listen(
        (data) {
          print('Received raw data for blood pressure measurement: $data');
          setState(() {
            result = parseBloodPressureData(data);
          });
        },
        onError: (error) {
          print('Error subscribing to blood pressure measurement: $error');
        },
      );
    } catch (e) {
      print('Exception during subscribing to blood pressure measurement: $e');
    }
  }

  List<double> parseBloodPressureData(List<int> packet) {
    final data = ByteData.sublistView(Uint8List.fromList(packet));
    const startByte = 1;
    int value = 0;

//systolic
    value = data.getInt16(startByte, Endian.little);
    int mantissa = value & 0xfff;

    if (mantissa >= 0x0800) {
      mantissa = -((0x0FFF + 1) - mantissa);
    }

    int exponent = value >> 12;

    if (exponent >= 0x0008) {
      exponent = -((0x000F + 1) - exponent);
    }

    double magnitude = pow(10.0, exponent).toDouble();
    double systolic = mantissa * magnitude;

//diastolic
    value = data.getInt16(startByte + 2, Endian.little);
    mantissa = value & 0xfff;

    if (mantissa >= 0x0800) {
      mantissa = -((0x0FFF + 1) - mantissa);
    }

    exponent = value >> 12;

    if (exponent >= 0x0008) {
      exponent = -((0x000F + 1) - exponent);
    }

    magnitude = pow(10.0, exponent).toDouble();
    double diastolic = mantissa * magnitude;

    //pulse
    value = data.getInt16(startByte + 13, Endian.little);
    mantissa = value & 0xfff;

    if (mantissa >= 0x0800) {
      mantissa = -((0x0FFF + 1) - mantissa);
    }

    exponent = value >> 12;

    if (exponent >= 0x0008) {
      exponent = -((0x000F + 1) - exponent);
    }

    magnitude = pow(10.0, exponent).toDouble();
    double pulse = mantissa * magnitude;

    //meanAP
    value = data.getInt16(startByte + 5, Endian.little);
  
    mantissa = value & 0xfff;

    if (mantissa >= 0x0800) {
     mantissa = -((0x0FFF + 1) - mantissa);
     }

     exponent = value >> 12;

      if (exponent >= 0x0008) {
      exponent = -((0x000F + 1) - exponent);
      }

    magnitude = pow(10.0, exponent).toDouble();
      double meanAP= mantissa * magnitude;

    return [systolic, diastolic, pulse,meanAP];
    
  }
}


Solution

  • One thing that's easily missed when handling BLE data that takes more than one byte is Endianness. The order in which the bytes are places is essential. Take your values as example:

    0xFF07 = 65287
    0x07FF = 2047

    Interestingly, you already extract the int16 value using little Endian. I tried your code on DartPad and received 2047 as result:

    import 'dart:typed_data';
    import 'dart:math';
    
    void main() {
      
      final data = ByteData.sublistView(Uint8List.fromList([0xFF, 0x07]));
      
      int value = data.getInt16(0, Endian.little);
      
        int mantissa = value & 0xfff;
    
        if (mantissa >= 0x0800) {
         mantissa = -((0x0FFF + 1) - mantissa);
         }
    
         int exponent = value >> 12;
    
          if (exponent >= 0x0008) {
          exponent = -((0x000F + 1) - exponent);
          }
    
        double magnitude = pow(10.0, exponent).toDouble();
          double meanAP= mantissa * magnitude;
      print(meanAP);
    }