PhilipsHue / flutter_reactive_ble

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

iOS: Warning! No event channel set up to report a connection update, notifications not working #385

Closed JimTompkins closed 3 years ago

JimTompkins commented 3 years ago

Describe the bug I get a "Warning! No event channel set up to report a connection update" when trying to connect on iOS. The warning is not present on Android with similar code (on Android I use connectToAdvertisingDevice and on iOS connectTo).

On Android, I can successfully subscribe to a notification. On iOS, I do not receive any notifications. I'm not sure if this warning is related to the notification issue or not. I can successfully connect, read and write on iOS (although I had to change from writeWithoutResponse to writeWithResponse, as others have suggested).

To Reproduce Steps to reproduce the behavior:

  1. try to connect with this code: ble.dart

Expected behavior Should connect without the warning as it does on Android.

Smartphone / tablet

Peripheral device

Additional context The missing notifications on iOS work on Android and also using nRF Connect.

remonh87 commented 3 years ago

hmm this looks like a bug (thanks for the elaborate example btw). Can you try to add a small delay (500ms) as a workaround before subscribing to the notifications? It feels like a race condition.

JimTompkins commented 3 years ago

Thanks for the feedback!

I initially tried subscribing immediately after the connection was established. Now, I'm subscribing on a button press which is typically seconds after the connection is established in case there needed to be a delay after the connection is made for services to be discovered. This didn't work either.

Do you think the "no event channel" warning is related to the fact that notifications are not being received, or a separate issue?

I also had an issue ($360) where writeCharacteristicWithoutResponse didn't work on iOS so I changed it to WithResponse as a workaround. Not sure if this could be related or not...

mwernsen commented 3 years ago

I've also been observing the "Warning! No event channel set up to report a connection update" warning on ios. The warning appears as soon as the connectTo command is invoked. Connecting works fine, however the ConnectionStateUpdate.Connecting event is never added to the stream. This is ONLY the case for the first device I connect to after an app restart. Second, third nth device all work as expected.

Notifications, reading and writing characteristics are not affected by this warning after a connection has been established and this behaviour is not present on android.

Smartphone / tablet

Device: iPhone7 OS: iOS14 Package version: 4.0.1

JimTompkins commented 3 years ago

I checked and I only get this warning on the first connection after installing the app, the same as @mwernsen . It does not occur on the 2nd connection.

I've also found that readCharacteristics are also not working in addition to notifications. Both work in the Android version and on iOS using nRF Connect. I have try and catch blocks around both reads and subscriptions but do not get any error messages.

JimTompkins commented 3 years ago

I'm using Catalina OS so I'm limited to Xcode v12.4. I saw that v4.0.1 is for Xcode 13 so I changed back to version 4.0.0 of flutter_reactive_ble but this didn't make any change. I also tried using v4.0.1 with a dependency_override for reactive_ble_mobile to '4.0.0'.

The log messages on iOS are much more sparse than Android but I can't find a way to turn them on in Xcode.

Any suggestions for further digging?

Thanks!

JimTompkins commented 3 years ago

I've attached a console log that covers the time from connection to disconnection: Console log 2021-10-06-11-33-11.txt

I notice several things: 1) at line 205, there is an error due to UUID 2A05:

default 11:33:13.136261-0300 bluetoothd No characteristics with UUID 0x2A05 found in range [0x0008, 0x0008] on device "" default 11:33:13.156820-0300 bluetoothd Failed to locate GATT service changed characteristic handle on device "" error

11:33:13.157176-0300    bluetoothd  Command failed to execute with status 10

But my peripheral has a UUID 2A05 in the embedded code but no UUID 2A05 is actually present. Is this missing char enough to cause reads and notifies to not work when writes do?

2) at line 352, there is an error when the peripheral requests a connection timeout of 10s. Would this cause notifications and reads to not work when writes do?

error 11:33:18.927306-0300 bluetoothd Remote sides asks for a timeout of more than 6 seconds, this is not very user friendly - refusing

3) At line 231, I can see my discovered characteristic 0xFFF4 which has a notify property and a handle of 0x0027

default 11:33:13.163067-0300 bluetoothd statedump: 0x0026 Characteristic [ valueUUID: 0xFFF4, valueHandle: 0x0027, properties: notify, discoveredDescriptors: all ]

4) At line 474, my app subscribes to notifications and at line 481 the Bluetooth process subscribes but it's to handle 0x0026 not 0x0027. Is this expected?

default 11:33:39.073188-0300 bluetoothd Subscribing to updates of characteristic handle 0x0026 on device ""

5) I sent some notifies after they were subscribed to, but I don't see any evidence in the log file.

6) at line 726, I unsubscribe following a button click.

Thanks for any hints!

GaelleJoubert commented 2 years ago

I'm using Catalina OS so I'm limited to Xcode v12.4. I saw that v4.0.1 is for Xcode 13 so I changed back to version 4.0.0 of flutter_reactive_ble but this didn't make any change. I also tried using v4.0.1 with a dependency_override for reactive_ble_mobile to '4.0.0'.

The log messages on iOS are much more sparse than Android but I can't find a way to turn them on in Xcode.

Any suggestions for further digging?

Thanks!

I have the exact same issue. Which is quite annoying, because I need to read a characteristic after I connect ...

Device: iPhone 8 OS: iOS15.1 Package version: 5.0.2

Does anyone found a workaround ?

JimTompkins commented 2 years ago

Hi Gaelle, because of this issue I changed to flutter_blue_plus for the iOS version. It's not my preferred solution to use different packages for Android and iOS!

GaelleJoubert commented 2 years ago

I actually mannage to read the characteristic now ! I realise that i needed to use 16 bit ID and NOT 128 bits ID (which worked in Android!), the read on 128 bit ID were causing infinite read with no answer but no error either. With 16 bit characteristic id (the same you got when you do discover characteristics), it worked fine :)

tomstokes commented 2 years ago

@remonh87 I'm still experiencing this issue with the v5.0.2 package.

On iOS, I get the following error message for the first connection: Warning! No event channel set up to report a connection update

The connecting state is not communicated, but subsequent state changes are received successfully.

The library works correctly for subsequent connections. It's only the first connection on iOS that triggers this bug.

tomstokes commented 2 years ago

Digging deeper, the #406 commit that closed this issue was reverted in #440 . Looks like this issue should be re-opened.

dlewis2017 commented 2 years ago

I actually mannage to read the characteristic now ! I realise that i needed to use 16 bit ID and NOT 128 bits ID (which worked in Android!), the read on 128 bit ID were causing infinite read with no answer but no error either. With 16 bit characteristic id (the same you got when you do discover characteristics), it worked fine :)

This seemed to work for me. For some reason, it gave the above unhelpful errors. It doesn't like things in the format '180f', such as when passing Uuid.parse('180F') to the service/characteristic value(s) in subscribing to a characteristic. Not sure if that's an issue with the parsing function or inherent to iOS. I haven't been able to test on android so can't confirm there.

GaelleJoubert commented 2 years ago

I've tested it both with Android and IOS. For Android, I have to use the 128 bit ID, other it doesn't work, and for IOS I have to use the 16 bit ID. So I'm doing something like that :

final SERVICE_DEVICE_INFO = Platform.isAndroid ? "0000180A-0000-1000-8000-00805F9B34FB" : "180a";

And then later I call that string with Uuid.parse : Uuid.parse(SERVICE_DEVICE_INFO )

RikiZimbakov commented 2 years ago

this thread is amazing! thank you to @GaelleJoubert for the insight!

for reference I dont know if it has been updated since then but for me only 16bit works for both service and characteristic. For android and IOS it works being 16bit too so its perfect!

Thank you all for your help!

chipweinberger commented 1 year ago

I am also seeing this error message, and failing to stay connected for more than a ~0.5 seconds. https://github.com/PhilipsHue/flutter_reactive_ble/issues/728

Is the the 16-bit UUID relevant to me? I am not reading or writing a characteristic yet. Just trying to stay connected!

nissaba commented 1 year ago

getting this error also. if I re-run / hot reload the app then I do get 1 update on the listen of the connection.

the handleConnectionState method never gets called on the first try and only gets called once the second time I try to connect. I never get the update that the device is connected.

This is my code

import 'package:flutter/cupertino.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:notio/ble/ble_logger.dart';
import 'package:notio/services/locator.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../ble/ble_logger.dart';
import '../services/locator.dart';

part 'ble_device_connector.g.dart';

@Riverpod(keepAlive: true)
class BleDeviceConnector extends _$BleDeviceConnector {
  final FlutterReactiveBle _ble = locator.get<FlutterReactiveBle>();
  //final _deviceConnectionController = StreamController<ConnectionStateUpdate>();
// ignore: cancel_subscriptions
  StreamSubscription<ConnectionStateUpdate>? _connection;
  final Duration _connectionTimeout = const Duration(seconds: 10);

  @override
  ConnectionStateUpdate build() {
    return const ConnectionStateUpdate(
        deviceId: "",
        connectionState: DeviceConnectionState.disconnected,
        failure: null);
  }

  Future<void> connect(String deviceId) async {
    debugPrint('Start connecting to $deviceId');
    _connection = _ble
        .connectToDevice(id: deviceId, connectionTimeout: _connectionTimeout)
        .listen(
          handleConnectionState,
          onError: (Object e) =>
              debugPrint('Connecting to device $deviceId resulted in error $e'),
        );
  }

  Future<void> disconnect(String deviceId) async {
    try {
      debugPrint('disconnecting to device: $deviceId');
      await _connection?.cancel();
    } on Exception catch (e, _) {
      debugPrint("Error disconnecting from a device: $e");
    } finally {
// Since [_connection] subscription is terminated, the "disconnected" state cannot be received and propagated
      state = ConnectionStateUpdate(
        deviceId: deviceId,
        connectionState: DeviceConnectionState.disconnected,
        failure: null,
      );
    }
  }

  Future<void> handleConnectionState(ConnectionStateUpdate event) async {
    debugPrint(
        'ConnectionState for device ${event.deviceId} : ${event.connectionState}');
    //_deviceConnectionController.add(update);
    state = event;
  }

  Future<void> dispose() async {
    await _connection?.cancel();
  }
}