flutter_reactive_ble icon indicating copy to clipboard operation
flutter_reactive_ble copied to clipboard

Re-Enable notifications on reconnection to device

Open rowdy15 opened this issue 3 years ago • 8 comments

Describe the bug Basically the notifications are not re-enabled on reconnection to the ble device.

This is supported by the below code where on first setup of the .connectToDevice() listener. I recieve the below output on first connection when subscribing to the three characteristics.

D/BluetoothGatt( 3822): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt( 3822): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt( 3822): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: true

on disconnection I get the following.

D/BluetoothGatt( 5669): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: false
D/BluetoothGatt( 5669): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: false
D/BluetoothGatt( 5669): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: false

But unfortunately on reconnection when I go back into range of the BLE device (and it reconnects) I don't get a reenabling of the the notifications of those same characteristics.

Below is the complete output of during these events.

D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: true
I/flutter (17897): periodic clock updates enabled in 54 seconds
D/BluetoothGatt(17897): onClientConnectionState() - status=0 clientIf=6 device=EF:2C:0C:2E:78:1A
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: false
D/BluetoothManager(17897): getConnectionState()
D/BluetoothManager(17897): getConnectedDevices
D/BluetoothGatt(17897): close()
D/BluetoothGatt(17897): unregisterApp() - mClientIf=6
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: false
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: false
I/flutter (17897): we are disconnected
D/BluetoothGatt(17897): connect() - device: EF:2C:0C:2E:78:1A, auto: true
D/BluetoothGatt(17897): registerApp()
D/BluetoothGatt(17897): registerApp() - UUID=11ead47f-0cea-470c-b384-4348265fcab4
D/BluetoothGatt(17897): onClientRegistered() - status=0 clientIf=6
D/BluetoothGatt(17897): onClientConnectionState() - status=0 clientIf=6 device=EF:2C:0C:2E:78:1A
D/BluetoothGatt(17897): discoverServices() - device: EF:2C:0C:2E:78:1A
D/BluetoothGatt(17897): onSearchComplete() = Device=EF:2C:0C:2E:78:1A Status=0

To Reproduce walk far enough away to disconnect from BLE device then go back within range of the device and reconnect to it.

Expected behavior It does re-enable notifications on reconnection

Smartphone / tablet Huawei P9 (EVA-L19) - EMUI 5.0.3 (Android Version 7)

Peripheral device nRF52832 (custom firmware) which I have tested extensively and am 99.9% sure it is not the peripheral.

Additional context Interestingly when I disable and reenable bluetooth on the phone (simulating a disconnection event) it connects and reenables the notifications (see below output).

D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: true
D/BluetoothManager(17897): getConnectionState()
D/BluetoothManager(17897): getConnectedDevices
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: false
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: false
D/BluetoothGatt(17897): cancelOpen() - device: EF:2C:0C:2E:78:1A
D/BluetoothGatt(17897): onClientConnectionState() - status=0 clientIf=6 device=EF:2C:0C:2E:78:1A
D/BluetoothGatt(17897): close()
D/BluetoothGatt(17897): unregisterApp() - mClientIf=6
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: false
I/flutter (17897): we are disconnected
I/flutter (17897): periodic clock updates enabled in 22 seconds
D/BluetoothGatt(17897): connect() - device: EF:2C:0C:2E:78:1A, auto: true
D/BluetoothGatt(17897): registerApp()
D/BluetoothGatt(17897): registerApp() - UUID=c3879833-134c-416d-9c46-df8dd924163c
I/flutter (17897): connecting
D/BluetoothGatt(17897): onClientRegistered() - status=0 clientIf=6
I/art     (17897): Do partial code cache collection, code=58KB, data=54KB
I/art     (17897): After code cache collection, code=54KB, data=52KB
I/art     (17897): Increasing code cache capacity to 256KB
I/art     (17897): Compiler allocated 8MB to compile int com.google.protobuf.MessageSchema.getSerializedSizeProto3(java.lang.Object)
D/BluetoothGatt(17897): onClientConnectionState() - status=0 clientIf=6 device=EF:2C:0C:2E:78:1A
I/flutter (17897): you are connected
D/BluetoothGatt(17897): discoverServices() - device: EF:2C:0C:2E:78:1A
D/BluetoothGatt(17897): onSearchComplete() = Device=EF:2C:0C:2E:78:1A Status=0
W/BluetoothGatt(17897): onCharacteristicRead() - Device=EF:2C:0C:2E:78:1A handle=21 Status=0
I/flutter (17897): the place on the device is: thompsons
I/flutter (17897): the length of the place string is 9
I/flutter (17897): requesting count log
I/art     (17897): Compiler allocated 5MB to compile void com.google.protobuf.MessageSchema.writeFieldsInAscendingOrderProto3(java.lang.Object, com.google.protobuf.Writer)
W/BluetoothGatt(17897): onCharacteristicRead() - Device=EF:2C:0C:2E:78:1A handle=13 Status=0
I/flutter (17897): setup notification listeners
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641401-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: f3641403-00b0-4240-ba50-05ca45bf8abc enable: true
D/BluetoothGatt(17897): setCharacteristicNotification() - uuid: 00002a19-0000-1000-8000-00805f9b34fb enable: true

I feel like I'm missing something here that could fix this.

Also below is the switch statement that I have put into the connectToDevice() listener.

switch(event.connectionState) {
  case DeviceConnectionState.disconnecting:
    {
      if (event.connectionState != bleConnection.currentConnectionState) {
        bleConnection.currentConnectionState = event.connectionState;
        print('disconnecting');
      }
    }
    break;
  case DeviceConnectionState.connecting:
    {
      if (event.connectionState != bleConnection.currentConnectionState) {
        bleConnection.currentConnectionState = event.connectionState;
        print('connecting');
      }
    }
    break;
  case DeviceConnectionState.disconnected:
    {
      if (event.connectionState != bleConnection.currentConnectionState) {
        bleConnection.currentConnectionState = event.connectionState;
        print('we are disconnected');
        requestCountLog = true;
      }
    }
    break;

  case DeviceConnectionState.connected:
    {

      if (event.connectionState != bleConnection.currentConnectionState) {
        bleConnection.currentConnectionState = event.connectionState;
        print('you are connected');
        bleConnection.isConnected = true;
        bleConnection.connectionController.add(true);
        bleConnection.setPlace(await _ble.readCharacteristic(bleConnection.placeCharacteristic));

        if (requestCountLog) {
          requestCountLog = false;
          print('requesting count log');
          // request log data on the device.
          await _ble.writeCharacteristicWithoutResponse(bleConnection.optionCharacteristic, value: [0x07]);

          //once log data is complete, then send time to device.
          final DateTime now = DateTime.now();
          final int hour = now.hour;
          final int minute = now.minute;
          await _ble.writeCharacteristicWithoutResponse(bleConnection.optionCharacteristic, value: [0x04, hour, minute]);
          Timer(Duration(seconds: now.second), () {
            print('periodic clock updates enabled in ${now.second} seconds');
            Timer.periodic(Duration(seconds: 60), (Timer t) => _getTime());
          });
          // once the time timer has been set, we can finally get the battery level.
          bleConnection.batteryLevel = await _ble.readCharacteristic(bleConnection.batteryCharacteristic);
          bleConnection.batteryStreamController.add(bleConnection.batteryLevel.last);
        }

        print('setup notification listeners');
        bleConnection.countStream = _ble.subscribeToCharacteristic(bleConnection.countCharacteristic).listen((ev) async {
          bleConnection.setCountData(ev);
        }, onError: (dynamic error) {
          // code to handle errors
        });

        //command to get log dat in log char listener
        bleConnection.logStream = _ble.subscribeToCharacteristic(bleConnection.logCharacteristic).listen((ev) async {
          if (ev.isNotEmpty) {
            bleConnection.processLogCountData(ev);
          }
        });

        bleConnection.batteryStream = _ble.subscribeToCharacteristic(bleConnection.batteryCharacteristic).listen((ev) async {
          print('Battery notification: $ev');
          bleConnection.batteryLevel = ev;
        });
      }
    }
    break;

  default:
    {
      //statements;
    }
    break;
}

rowdy15 avatar Sep 27 '21 13:09 rowdy15

Thank you for the elaborate description. This really helps. I tried to reproduce it using the example app but despite I spent quite some significant time I wasn't able to. Can you check if you can reproduce the same behaviour with another phone and the example app? I do not have a Android 7 phone but I tested it with a Samsung s7 on android 8

remonh87 avatar Sep 27 '21 18:09 remonh87

Thanks Remonh87. I really appreciate you looking into this. Yes I will investigate if I can reproduce it the the example app.

rowdy15 avatar Sep 29 '21 01:09 rowdy15

Just doing to some testing on the example app and there is a difference in my implementation of the flutter_reactive_ble library.

The difference is that subscribing of notifications is disabled once you go back out of the dialog and is not re-enabled upon automatic reconnection even while keeping the dialog box on top.

However, thank you, as I did need to look at the example app more closely to figure it out.

I have been able to customise the example app implementation slightly in my app so that it automatically and safely cancels the StreamSubscription 'connectToDevice' listener in the ble_device_connector.dart file.

I also listen to the Stream<ConnectionStateUpdate> state stream in my app.

The key is to call disconnect(deviceId) in the connectToDevice listener (in the ble_device_connector.dart file) when the DeviceConnectionState.disconnected event is emmitted. This gracefully disconnects the device (by cancelling the listener).

Then you call widget.connector.connect(deviceId) in the separate Stream<ConnectionStateUpdate> state stream in my app in the DeviceConnectionState.disconnected emitted event.

Also NOTE: the switch statement for dealing with initial reads/writes and subscriptions is in the StreamSubscription 'connectToDevice' listener in the ble_device_connector.dart file before the _deviceConnectionController.add(event); so that the app disconnects gracefully before trying to connect again.

rowdy15 avatar Sep 30 '21 05:09 rowdy15

@remonh87 I am alos facing a similar type of issue, where On the first attempt I connect the BLE device and was able to get the data from characteristics streams and receive the data... but if I disconnect to the same device and connect again then I can get the discover services but not getting the data while I call subscribeToCharacteristic.

I am trying the same on the example app under this repository...

Note: Also getting multiple entries of same service id when clicking on the discovery service. Check the below screenshot.

screenshot-1637660664448

@dhaval-k-simformsolutions I am Facing the exact same issue. In the exact same configuration. I connect & diconnect, and then reconnect again, and I when I try to subscribe to a characteristic I get Error unsubscribing from notifications: PlatformException(reactive_ble_mobile.PluginError:3, The operation couldn’t be completed. (reactive_ble_mobile.PluginError error 3.), {}, null) . I can read and write a characteristic though. The issue is only in subscribing. Have you find a workaround ? thanks.

GaelleJoubert avatar Sep 08 '22 14:09 GaelleJoubert

@rowdy15 I am facing the same issue, and don't know how to deal it exactly. it is disconnected when notifying the characteristic and I try to reconnect, but it disconnect over and over again. Have you find the solution ? thanks.

ILittleNiZi avatar Dec 01 '22 01:12 ILittleNiZi

Running in the same issue, is there any progress?

ben91187 avatar Apr 05 '23 19:04 ben91187