PhilipsHue / flutter_reactive_ble

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

Discover different services from Android and IOS on identical hardware #504

Open ride4sun opened 2 years ago

ride4sun commented 2 years ago

Describe the bug when I discover a service of an OBDII UART BLE device I discover different services for IOS and Android. Android works just fine and I expect a service ID 0000fff0-0000-1000-8000-00805f9b34fb

IOS

image

Android

image

To Reproduce Steps to reproduce the behavior: simply try to connect to the device and discover the Services

  Future<bool> _discoverServices(BleDevice device) async {
    try {
      List<DiscoveredService> services =
          await _interactor.discoverServices(device.id);

      _log.finest('Services for : ${device.name} --------------------');
      services.forEach((s) => _log.finest('${s.toString()}'));

      _discoveredService = services.firstWhere(
          (service) =>
              service.serviceId.toString() ==
              '0000fff0-0000-1000-8000-00805f9b34fb',
          orElse: () => null);

      if (_discoveredService == null) {
        _log.finest(
            'Could not find Services of the devices which are matching!');
        return false;
      }

      List<DiscoveredCharacteristic> characteristics;
      try {
        characteristics = _discoveredService.characteristics;
      } catch (error) {
        _log.finest('Call to discover services failed: $error ');
        return false;
      }

      var writeCharacteristic = characteristics.firstWhere(
          (char) =>
              char.characteristicId.toString() ==
              '0000fff2-0000-1000-8000-00805f9b34fb',
          orElse: () => null);

      _writeCharacteristic = QualifiedCharacteristic(
          characteristicId: writeCharacteristic.characteristicId,
          serviceId: _discoveredService.serviceId,
          deviceId: device.id);

      var notifyCharacteristic = characteristics.firstWhere(
          (char) =>
              char.characteristicId.toString() ==
              '0000fff1-0000-1000-8000-00805f9b34fb',
          orElse: () => null);

      _notifyCharacteristic = QualifiedCharacteristic(
          characteristicId: notifyCharacteristic.characteristicId,
          serviceId: _discoveredService.serviceId,
          deviceId: device.id);

      if (_notifyCharacteristic == null || _writeCharacteristic == null) {
        _log.finest('Could not find required characteristics');
        return false;
      }

      _notifySubscription =
          _interactor.subScribeToCharacteristic(_notifyCharacteristic).listen(
        (data) {
          if (isConnected.isTrue && _disconnectLock.inLock.isFalse)
            _notify.value = data;
        },
      );
      _notifySubscription.onError((error) {
        _log.finest('Error: Notify Stream error: $error');
        _disconnect();
        loading.value = false;
      });
      return true;
    } catch (e) {
      rethrow;
    }
  }

Expected behavior Identical characteristics on IOS and Android

I tried doing the same with a general BLE scanner application nRF (Android)

Screenshot_20220125-172014 Screenshot_20220125-172411-289

Smartphone / tablet

Peripheral device

ride4sun commented 2 years ago

if i change my code like this it all starts working. I would not expected that...

Future<bool> _discoverServices(BleDevice device) async {
    try {
      List<DiscoveredService> services =
          await _interactor.discoverServices(device.id);

      _log.finest('Services for : ${device.name} --------------------');
      services.forEach((s) => _log.finest('${s.toString()}'));

      _discoveredService = services.firstWhere(
          (service) =>
              service.serviceId.toString() ==
                  '0000fff0-0000-1000-8000-00805f9b34fb' ||
              service.serviceId.toString() == 'fff0',
          orElse: () => null);

      if (_discoveredService == null) {
        _log.finest(
            'Could not find Services of the devices which are matching!');
        return false;
      }

      List<DiscoveredCharacteristic> characteristics;
      try {
        characteristics = _discoveredService.characteristics;
      } catch (error) {
        _log.finest('Call to discover services failed: $error ');
        return false;
      }

      var writeCharacteristic = characteristics.firstWhere(
          (char) =>
              char.characteristicId.toString() ==
                  '0000fff2-0000-1000-8000-00805f9b34fb' ||
              char.characteristicId.toString() == 'fff2',
          orElse: () => null);

      _writeCharacteristic = QualifiedCharacteristic(
          characteristicId: writeCharacteristic.characteristicId,
          serviceId: _discoveredService.serviceId,
          deviceId: device.id);

      var notifyCharacteristic = characteristics.firstWhere(
          (char) =>
              char.characteristicId.toString() ==
                  '0000fff1-0000-1000-8000-00805f9b34fb' ||
              char.characteristicId.toString() == 'fff1',
          orElse: () => null);

      _notifyCharacteristic = QualifiedCharacteristic(
          characteristicId: notifyCharacteristic.characteristicId,
          serviceId: _discoveredService.serviceId,
          deviceId: device.id);

      if (_notifyCharacteristic == null || _writeCharacteristic == null) {
        _log.finest('Could not find required characteristics');
        return false;
      }

      _notifySubscription =
          _interactor.subScribeToCharacteristic(_notifyCharacteristic).listen(
        (data) {
          if (isConnected.isTrue && _disconnectLock.inLock.isFalse)
            _notify.value = data;
        },
      );
      _notifySubscription.onError((error) {
        _log.finest('Error: Notify Stream error: $error');
        _disconnect();
        loading.value = false;
      });
      return true;
    } catch (e) {
      rethrow;
    }
  }
spekary commented 1 year ago

Having the same problem. It looks like with services that have the predefined postfix of "0000-1000-8000-00805f9b34fb", iOS removes the postfix, but Android keeps it in. As a cross-platform library, reactive-ble should fix this and standardize on one format or the other, and not just pass through whatever the underlying libraries pass.

chplalex commented 5 months ago

Hi!

In my case, the getDiscoveredServices() method on the same BLE device returns different results in iOS and Android. The iOS results look restricted, and incomplete.

Why does it happen? What should I do to get the same and full results on both platforms?

The specific characteristic that is needed for my application is present in the Android lists and the writeCharacteristicWithResponse() method works well. But it is absent in the iOS list and that's why the writeCharacteristicWithResponse() falls.

target characteristic:

service id: d9cbb5cc-d795-11eb-b8bc-0242ac130003 characteristic id: 2b1c94ff-c7f4-4f90-adca-51e4945c6a58

The full lists of the services and characteristics are attached.

android_services.log ios_services.log

flutter_reactive_ble: ^5.3.1