PhilipsHue / flutter_reactive_ble

Flutter library that handles BLE operations for multiple devices.
https://developers.meethue.com/
Other
668 stars 335 forks source link

Crash on subsequent subscribeToCharacteristic (Android 12, v5.0.2) #609

Closed mk-dev-1 closed 2 years ago

mk-dev-1 commented 2 years ago

Describe the bug Connecting and subscribing to a characteristic only works once. After disconnecting & reconnecting to the same device either causes no error (and no reads) or the following error.

Exception: GenericFailure(code: CharacteristicValueUpdateError.unknown, message: "Disconnected from MAC='XX:XX:XX:XX:XX:XX' with status 0 (GATT_SUCCESS)")

This error does not happen with the example app, but it does happen with this simple sample app I put together (below).

To Reproduce Steps to reproduce the behavior:

  1. Use sample app code shown below.
  2. Hit start - 1st result (can be ignored):

    I/flutter (16096): CONNECTION STATE DeviceConnectionState.connecting I/flutter (16096): CONNECTION STATE DeviceConnectionState.disconnected

  3. Hit start - 2nd result:

    I/flutter (16096): CONNECTION STATE DeviceConnectionState.connecting I/flutter (16096): CONNECTION STATE DeviceConnectionState.connected I/flutter (16096): [30, 115, 0, 83, 0, 104, 0, 230, 7, 9, 17, 17, 11, 45, 56, 0, 0, 0, 0] I/flutter (16096): [30, 126, 0, 75, 0, 109, 0, 230, 7, 9, 17, 17, 52, 6, 61, 0, 0, 0, 0] I/flutter (16096): [30, 135, 0, 87, 0, 108, 0, 230, 7, 9, 18, 10, 43, 5, 56, 0, 0, 0, 0] I/flutter (16096): CONNECTION STATE DeviceConnectionState.disconnected I/flutter (16096): closing characteristics subscription I/flutter (16096): closing device subscription

  4. Hit start - 3rd result:

    I/flutter (16096): CONNECTION STATE DeviceConnectionState.connecting I/flutter (16096): CONNECTION STATE DeviceConnectionState.connected I/flutter (16096): Exception: GenericFailure(code: CharacteristicValueUpdateError.unknown, message: "Disconnected from MAC='XX:XX:XX:XX:XX:XX' with status 0 (GATT_SUCCESS)") I/flutter (16096): CONNECTION STATE DeviceConnectionState.disconnected I/flutter (16096): closing characteristics subscription I/flutter (16096): closing device subscription

CODE TO REPRODUCE ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:health_flutter/services/bluetooth_manager.dart'; class BluetoothScreen extends StatefulWidget { const BluetoothScreen({Key? key}) : super(key: key); @override State createState() => _BluetoothScreenState(); } class _BluetoothScreenState extends State { BluetoothScreenState? bluetoothScreenStateCurrent; StreamSubscription? deviceSubscription; StreamSubscription? characteristicSubscription; DeviceConnectionState connectionState = DeviceConnectionState.disconnected; String deviceId = 'xx:xx:xx:xx:xx:xx'; Uuid serviceId = Uuid.parse('00001810-0000-1000-8000-00805f9b34fb'); Uuid characteristicId = Uuid.parse('00002a35-0000-1000-8000-00805f9b34fb'); FlutterReactiveBle ble = FlutterReactiveBle(); Future _stop() async { print("closing characteristic subscription"); await characteristicSubscription?.cancel(); characteristicSubscription = null; await Future.delayed(const Duration(milliseconds: 500)); print("closing device subscription"); await deviceSubscription?.cancel(); deviceSubscription = null; connectionState = DeviceConnectionState.disconnected; setState(() {}); } void _listenToCharacteristic() async { await ble.discoverServices(deviceId); characteristicSubscription = ble .subscribeToCharacteristic( QualifiedCharacteristic( characteristicId: characteristicId, serviceId: serviceId, deviceId: deviceId, ), ).listen((v) { print(v.toString()); }, onError: (e) { print(e); }, onDone: () async { await _stop(); }, cancelOnError: false); } Future _start() async { connectionState = DeviceConnectionState.connecting; setState(() {}); deviceSubscription = ble.connectToDevice(id: deviceId).listen((event) async { connectionState = event.connectionState; print("CONNECTION STATE $connectionState"); setState(() {}); if (connectionState == DeviceConnectionState.connected) { await Future.delayed(const Duration(milliseconds: 500)); _listenToCharacteristic(); } }); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(connectionState.toString()), const SizedBox(height: 20), TextButton( onPressed: connectionState == DeviceConnectionState.disconnected ? _start : null, child: const Text('Start'), ), const SizedBox(height: 20), TextButton( onPressed: connectionState == DeviceConnectionState.disconnected ? null : _stop, child: const Text('Stop'), ), ], ), ), ); } } ```

Expected behavior Connecting, subscribing, disconnecting should work correctly everytime.

Smartphone / tablet

Peripheral device

Additional context The device (Beurer BM57) re-transmits the records it's got stored every time and they can be perfectly received with the code from the sample app that comes with the plugin, however not with the sample code that I added. I tried adding delays, changed the order of calling .cancel(), etc. but I sitll can't get consistent reads. I am hoping it is something very simple that I am doing wrong, but I just can't seem to figure out what it is. I am hoping for your guidance.

Thank you very much for your support.

mk-dev-1 commented 2 years ago

Should anyone stumble upon this, the issue can easily be remedied by adding a simple await Future.delayed(const Duration(milliseconds: 200)); in the right place. Close.

wsmzdyss commented 1 year ago

Should anyone stumble upon this, the issue can easily be remedied by adding a simple await Future.delayed(const Duration(milliseconds: 200)); in the right place. Close.

where is right place?

mk-dev-1 commented 1 year ago

@wsmzdyss - Here is an example:


        await _characteristicSubscription?.cancel();
        await Future.delayed(const Duration(milliseconds: 200)); // <---- Add this here
        _characteristicSubscription = ble
            .subscribeToCharacteristic(
          QualifiedCharacteristic(
            characteristicId: _bluetoothCharacteristicId,
            serviceId: _bluetoothServiceId,
            deviceId: id,
          ),
        )
wsmzdyss commented 1 year ago

@mk-dev-1 Thanks, I tried it but it didn't solve the problem