Navideck / universal_ble

A cross-platform Android/iOS/macOS/Windows/Linux/Web Bluetooth Low Energy (BLE) plugin for Flutter
https://pub.dev/packages/universal_ble
Other
13 stars 1 forks source link

`UniversalBle.onValueChanged` returns duplicate data on Android #15

Open HosamHasanRamadan opened 1 month ago

HosamHasanRamadan commented 1 month ago

First of all I want to thank you for your amazing effort, The plugin API is simple and straight forward

Issue: I have BLE device with two Characteristic (write: writeWithoutResponse) and (read: notify). If I connect to my BLE device and disconnect and reconnect again , the readings from the devices will be duplicated. The duplication is equal to the number of reconnection , like if you reconnect 3 times the data will be duplicated 3 times.

This issue happens only on Android

Steps to reproduce the issue: 1- Scan 2- Stop scan 3- Select BLE device and connect 4- Discover services 5- Set notifying to read characteristic 6- Write command 7- onValueChanged called and get the result 8- Disconnect 9- Reconnect with same steps 10- onValueChanged called and the result is repeated twice

Flutter doctor [✓] Flutter (Channel stable, 3.16.9, on macOS 14.3.1 23D60 darwin-arm64, locale en-EG) • Flutter version 3.16.9 on channel stable at /Users/hosamhasan/fvm/versions/3.16.9 • Upstream repository https://github.com/flutter/flutter.git • Framework revision 41456452f2 (5 weeks ago), 2024-01-25 10:06:23 -0800 • Engine revision f40e976bed • Dart version 3.2.6 • DevTools version 2.28.5 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/hosamhasan/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • ANDROID_HOME = /Users/hosamhasan/Library/Android/sdk • Java binary at: /Users/hosamhasan/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.2) • Xcode at /Applications/Xcode-15.2.0.app/Contents/Developer • Build 15C500b • CocoaPods version 1.15.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2023.1) • Android Studio at /Users/hosamhasan/Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) [✓] IntelliJ IDEA Community Edition (version 2023.3.2) • IntelliJ at /Users/hosamhasan/Applications/IntelliJ IDEA Community Edition.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] VS Code (version 1.86.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.83.20240201 [✓] Connected device (4 available) • Lenovo TB 8505FS (mobile) • HA16YBY9 • android-arm64 • Android 10 (API 29) • Hosam’s iPhone (mobile) • 00008101-000A6D303AF8001E • ios • iOS 17.3.1 21D61 • macOS (desktop) • macos • darwin-arm64 • macOS 14.3.1 23D60 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 122.0.6261.94 [✓] Network resources • All expected network resources are available. • No issues found!
Code ```dart import 'dart:async'; import 'dart:developer'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:universal_ble/universal_ble.dart'; class Example extends StatefulWidget { const Example({super.key}); @override State createState() => _ExampleState(); } class _ExampleState extends State { final List foundDevices = []; BleScanResult? connectedDevice; final result = []; String textResult = ''; @override void initState() { super.initState(); UniversalBle.onScanResult = (scanResult) { log(scanResult.name ?? scanResult.deviceId); if (foundDevices.map((e) => e.deviceId).contains(scanResult.deviceId)) { return; } if (scanResult.name == null) return; foundDevices.add(scanResult); setState(() {}); }; UniversalBle.onAvailabilityChange = (state) { print(state.name); }; UniversalBle.onValueChanged = ( String deviceId, String characteristicId, Uint8List value, ) { result.add(value); }; UniversalBle.onConnectionChanged = (String deviceId, BleConnectionState state) { print('$deviceId -- $state'); }; } @override Widget build(BuildContext context) { return MaterialApp( home: SafeArea( child: Scaffold( body: Column( children: [ Wrap( children: [ TextButton( onPressed: () async { UniversalBle.startScan(); }, child: Text('Scan'), ), TextButton( onPressed: () async { UniversalBle.stopScan(); }, child: Text('Stop'), ), TextButton( onPressed: () async { if (connectedDevice != null) { UniversalBle.disconnect(connectedDevice!.deviceId) .then((value) { connectedDevice = null; foundDevices.clear(); textResult = ''; setState(() {}); }); } }, child: Text('Disconnect'), ), TextButton( onPressed: setNotifying, child: const Text('Notifying'), ), TextButton( onPressed: () async { if (connectedDevice != null) { await UniversalBle.discoverServices( connectedDevice!.deviceId, ); } }, child: Text('Discover Services'), ), TextButton( onPressed: write, child: Text('Write'), ), ], ), Text( 'Result:\n$textResult', textAlign: TextAlign.center, ), Text('Scan Result'), Expanded( child: ListView( children: [ ...foundDevices.map( (value) { return ListTile( onTap: () { UniversalBle.connect(value.deviceId); connectedDevice = value; }, title: Text('${value.name}'), ); }, ) ?? [], ], ), ), ], ), ), ), ); } void setNotifying() { final readCharacteristicUuid = ''; final serviceUuid = ''; if (connectedDevice != null) { UniversalBle.setNotifiable( connectedDevice!.deviceId, serviceUuid, readCharacteristicUuid, BleInputProperty.notification, ); } } Future write() async { final writeCharacteristicUuid = ''; final serviceUuid = ''; final command = Uint8List.fromList([]); if (connectedDevice != null) { UniversalBle.writeValue( connectedDevice!.deviceId, serviceUuid, writeCharacteristicUuid, command, BleOutputProperty.withoutResponse, ); } } } ```
fotiDim commented 1 month ago

Kudos for the very detailed issue report! We will have a look and report back soon.

rohitsangwan01 commented 1 month ago

@HosamHasanRamadan i tried replicating this, seems to be working fine for me, can you cross check with NrfConnect app, sometimes issue can be with peripheral as well, if peripheral is not clearing characteristic handlers on disconnecting