chipweinberger / flutter_blue_plus

Flutter plugin for connecting and communicationg with Bluetooth Low Energy devices, on Android, iOS, macOS
Other
728 stars 444 forks source link

[Help]: Android scanning shows duplicates #547

Closed brain9520 closed 1 year ago

brain9520 commented 1 year ago

FlutterBluePlus Version

1.14.23

Flutter Version

3.10.6

What OS?

Android

OS Version

13

Ask your question

Is there a parameter to set not to scan devices repeatedly? thank you.

Logs

When I use the Android device to start scanning, it will display as follows:
I/flutter (18948): ble_device_name: EA02001022540
I/flutter (18948): ble_device_name: EA02001022540
I/flutter (18948): ble_device_name: EA02001022540
I/flutter (18948): ble_device_name: EC100200
I/flutter (18948): ble_device_name: EA02001022540
I/flutter (18948): ble_device_name: EA02001022540
I/flutter (18948): ble_device_name: EA02001022540

When I scan with an Apple device as follows:
flutter: ble_device_name: EA02001022540
flutter: ble_device_name: midea
flutter: ble_device_name: midea
flutter: ble_device_name: EC100200
flutter: ble_device_name: KeepB3-31
flutter: ble_device_name: HUAWEI Band 7-D5C
flutter: ble_device_name: MITV-2E531
flutter: ble_device_name: HUAWEI Band 7-BFF
flutter: ble_device_name: MITV-900BB
chipweinberger commented 1 year ago

what does your code look like?

allowDuplicates is the option you can control this, usually. But depends on your code.

this could also be a bug in android itself

brain9520 commented 1 year ago

what does your code look like?

allowDuplicates is the option you can control this, usually. But depends on your code.

this could also be a bug in android itself

FlutterBluePlus.scan(timeout: const Duration(seconds: 4),allowDuplicates: false).listen ,Tried it before, but it didn't work。

chipweinberger commented 1 year ago

use this code, which is stronger at detecting duplicates

// Setup Listener for scan results
// device not found? see "Common Problems" in the README
var subscription = FlutterBluePlus.scanResults.listen((results) {
    for (ScanResult r in results) {
        print('${r.device.localName} found! rssi: ${r.rssi}');
    }
});

// Start scanning
FlutterBluePlus.startScan(timeout: Duration(seconds: 4));

// Stop scanning
await FlutterBluePlus.stopScan();
brain9520 commented 1 year ago

use this code, which is stronger at detecting duplicates

// Setup Listener for scan results
// device not found? see "Common Problems" in the README
var subscription = FlutterBluePlus.scanResults.listen((results) {
    for (ScanResult r in results) {
        print('${r.device.localName} found! rssi: ${r.rssi}');
    }
});

// Start scanning
FlutterBluePlus.startScan(timeout: Duration(seconds: 4));

// Stop scanning
await FlutterBluePlus.stopScan();

Thank you so much for your selfless help.

chipweinberger commented 1 year ago

It appears there actually is a bug in FBP, when using scan & allowDuplicates=false. I'll push a fix

chipweinberger commented 1 year ago

this is now fixed in 1.15.1

brain9520 commented 1 year ago

this is now fixed in 1.15.1

Hello, I have upgraded the version to 1.15.1, and the encoding of my search device is as follows:

     FlutterBluePlus.scanResults.listen((results) {
       for (ScanResult r in results) {
         debugPrint('${r.device.localName} found! rssi: ${r.rssi}');
       }
     });

    await FlutterBluePlus.startScan(
      oneByOne: true,
      timeout: const Duration(seconds: 4),
      removeIfGone: const Duration(seconds: 1), 
    );

When I run the program, the console still displays many duplicate devices, as follows:

            I/flutter (17410): midea found! rssi: -81
            I/flutter (17410): EC100200 found! rssi: -76
            I/flutter (17410): EA222228 found! rssi: -59
            I/flutter (17410): LE-reserved_C found! rssi: -101
            I/flutter (17410): midea found! rssi: -79
            I/flutter (17410): EC100200 found! rssi: -49
            I/flutter (17410): EA222228 found! rssi: -59
            I/flutter (17410): midea found! rssi: -83
            I/flutter (17410): midea found! rssi: -87
            I/flutter (17410): midea found! rssi: -79
            I/flutter (17410): EC100200 found! rssi: -55
            I/flutter (17410): EA222228 found! rssi: -59
            I/flutter (17410): midea found! rssi: -83
            I/flutter (17410): midea found! rssi: -87
            I/flutter (17410): EA222228 found! rssi: -60
            I/flutter (17410): midea found! rssi: -86
            I/flutter (17410): midea found! rssi: -86
            I/flutter (17410): midea found! rssi: -82
            I/flutter (17410): midea found! rssi: -86
            I/flutter (17410): midea found! rssi: -87
            I/flutter (17410): EC100200 found! rssi: -49
            I/flutter (17410): EA222228 found! rssi: -59
            I/flutter (17410): midea found! rssi: -87
            I/flutter (17410): EC100200 found! rssi: -53
            I/flutter (17410): midea found! rssi: -83
            I/flutter (17410): EC100200 found! rssi: -65
            I/flutter (17410): EA222228 found! rssi: -61
            I/flutter (17410): midea found! rssi: -86
            I/flutter (17410): midea found! rssi: -79
            I/flutter (17410): Mi Smart Band 4 found! rssi: -79
            I/flutter (17410): EC100200 found! rssi: -50
            I/flutter (17410): EA222228 found! rssi: -59
            I/flutter (17410): midea found! rssi: -86

When I was doing Android native development before, I used a third-party library. There were four methods in the scan callback of that library, among which

             @Override
              public void onScanning(BleDevice bleDevice) {
                  super.onScanning(bleDevice) 
              }

Duplicate device names will not be returned in the callback,

              override fun onLeScan(bleDevice: BleDevice?) {
                  super.onLeScan(bleDevice) 
              }

Duplicate device names will be returned in the callback. But I don't have the ability to port the implementation over there to flutter_blue_plus.

chipweinberger commented 1 year ago

When I run the program, the console still displays many duplicate devices

This is because you are using oneByOne: true

Why did you think that option was needed? is the comment unclear?

chipweinberger commented 1 year ago

I've updated the comment. hopefully more clear

  ///   - [oneByOne] if true, we will stream every advertistment one by one, including duplicates.
  ///    If false, we deduplicate the advertisements, and return a list of devices.
brain9520 commented 1 year ago

@chipweinberger I tried the parameters you mentioned, if I set oneByOne: false,Scanning will not stop, even if I set a timeout, it will continue to scan.

brain9520 commented 1 year ago

I've updated the comment. hopefully more clear

  ///   - [oneByOne] if true, we will stream every advertistment one by one, including duplicates.
  ///    If false, we deduplicate the advertisements, and return a list of devices.

my codes:

 FlutterBluePlus.scanResults.listen((results) {
  for (ScanResult r in results) {
    // if (r.device.localName.isEmpty) return;q
    debugPrint(
        '${r.device.localName} found! rssi: ${r.rssi}-- time: ${DateTime.now()}');
  }
});

await FlutterBluePlus.startScan(
  // oneByOne: false,
  timeout: const Duration(seconds: 4),
  removeIfGone: const Duration(seconds: 1), 
);
brain9520 commented 1 year ago

There is another problem here, if I uncomment the // if (r.device.localName.isEmpty) return; uncommented in the code, I find that the name of any device will not be printed in the console, but in fact I am nearby Many Bluetooth devices.

chipweinberger commented 1 year ago

not sure, lmk if you figure it out

brain9520 commented 1 year ago

not sure, lmk if you figure it out

error_info.pdf

chipweinberger commented 1 year ago

the reason you have duplicates is because you are printing the list many times.

try this

List<ScanResult> finalResult = [];

FlutterBluePlus.scanResults.listen((results) {finalResult = results;});

await FlutterBluePlus.startScan(timeout: Duration(seconds: 10));

await Future.delayed(Duration(seconds:11));

for (ScanResult r in finalResult) {
    print('${r.device.localName} found!  ${r.device.remoteId}  rssi: ${r.rssi}');
}
brain9520 commented 1 year ago
FlutterBluePlus.scanResults.listen((results) {
   // This callback method is called every time the scanned array changes, right? I think it would be more convenient to add some parameter controllers as a screening mechanism, for example, the scanned device has a name and contains a certain prefix. Will adding these parameters make this monitoring method less likely to be called?
});

Then, I really appreciate you keep replying to my questions, please excuse my bad technique。 thank you very much!

brain9520 commented 1 year ago

@chipweinberger

FlutterBluePlus.scanResults.listen((results) {
   // This callback method is called every time the scanned array changes, right? I think it would be more convenient to add some parameter controllers as a screening mechanism, for example, the scanned device has a name and contains a certain prefix. Will adding these parameters make this monitoring method less likely to be called?
});

Then, I really appreciate you keep replying to my questions, please excuse my bad technique。 thank you very much!

chipweinberger commented 1 year ago

This callback method is called every time the scanned array changes, right?

yes.

simple code to remove duplicates.

Set<DeviceIdentifier> got = {};
var subscription = FlutterBluePlus.scanResults.listen((results) {
    for (ScanResult r in results) {
        if (got.contains(r.device.remoteId) == false) {
            print('${r.device.remoteId}: "${r.device.localName}" found! rssi: ${r.rssi}');
            got.add(r.device.remoteId);
        }
    }
});

I think it would be more convenient to add some parameter controllers as a screening mechanism, for example, the scanned device has a name and contains a certain prefix. Will adding these parameters make this monitoring method less likely to be called?

You can implement screening yourself.

chipweinberger commented 1 year ago

For example:

Set<DeviceIdentifier> got = {};

FlutterBluePlus.scanResults
    .map((list) => list.first)
    .where((r) => r.device.localName == "example")
    .where((r) => got.contains(r.device.remoteId) == false)
    .listen((r) {
  print('${r.device.remoteId}: "${r.device.localName}" found! rssi: ${r.rssi}');
  got.add(r.device.remoteId);
});

await FlutterBluePlus.startScan(timeout: Duration(seconds: 10), oneByOne:true);

this will only print for newly found devices, that have the name "example"