diff --git a/CHANGELOG.md b/CHANGELOG.md index a52e545..80db357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.20.1 +* Fix `writeWithoutResponse()` which was not properly waiting for completion on Android and Apple + ## 0.20.0 * Add new high level API. Services are auto-discovered. the BleDevice class offers convenient member methods and properties. * BREAKING CHANGE: `bleDevice.isPaired` is now `bleDevice.paired` diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt index 806387c..a72663a 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt @@ -153,6 +153,9 @@ fun Int.parseGattErrorCode(): String? { BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> "GATT_INSUFFICIENT_ENCRYPTION" BluetoothGatt.GATT_CONNECTION_CONGESTED -> "GATT_CONNECTION_CONGESTED" BluetoothGatt.GATT_FAILURE -> "GATT_FAILURE" + BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> "ERROR_GATT_WRITE_NOT_ALLOWED" + BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> "ERROR_GATT_WRITE_REQUEST_BUSY" + BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> "FEATURE_NOT_CONFIGURED" 0x01 -> "GATT_INVALID_HANDLE" 0x04 -> "GATT_INVALID_PDU" 0x09 -> "GATT_PREPARE_QUEUE_FULL" diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt index 3442916..ad61ee8 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt @@ -469,34 +469,39 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE } + + val writeFuture = WriteResultFuture( + gatt.device.address, + gattCharacteristic.uuid.toString(), + gattCharacteristic.service.uuid.toString(), + callback + ) + + // Wait for the result + writeResultFutureList.add(writeFuture) + val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val writeResult = gatt.writeCharacteristic(gattCharacteristic, value, writeType) - writeResult == BluetoothGatt.GATT_SUCCESS + gatt.writeCharacteristic(gattCharacteristic, value, writeType) } else { @Suppress("DEPRECATION") gattCharacteristic.value = value gattCharacteristic.writeType = writeType @Suppress("DEPRECATION") - gatt.writeCharacteristic(gattCharacteristic) + val status = gatt.writeCharacteristic(gattCharacteristic) + if (status) BluetoothGatt.GATT_SUCCESS else BluetoothGatt.GATT_FAILURE } - if (!result) { - callback(Result.failure(FlutterError("Failed", "Failed to write", null))) - return - } - - if (writeType == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) { - // wait for the result - writeResultFutureList.add( - WriteResultFuture( - gatt.device.address, - gattCharacteristic.uuid.toString(), - gattCharacteristic.service.uuid.toString(), - callback + if (result != BluetoothGatt.GATT_SUCCESS) { + writeResultFutureList.remove(writeFuture) + callback( + Result.failure( + FlutterError( + "WriteError", + "Failed to write: ${result.parseGattErrorCode()}", + null + ) ) ) - } else { - callback(Result.success(Unit)) } } catch (e: FlutterError) { callback(Result.failure(e)) diff --git a/darwin/Classes/UniversalBlePlugin.swift b/darwin/Classes/UniversalBlePlugin.swift index 0381193..0a54537 100644 --- a/darwin/Classes/UniversalBlePlugin.swift +++ b/darwin/Classes/UniversalBlePlugin.swift @@ -36,6 +36,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral private var discoveredServicesProgressMap: [String: [UniversalBleService]] = [:] private var characteristicReadFutures = [CharacteristicReadFuture]() private var characteristicWriteFutures = [CharacteristicWriteFuture]() + private var characteristicWriteWithoutResponseFutures = [CharacteristicWriteFuture]() private var characteristicNotifyFutures = [CharacteristicNotifyFuture]() private var discoverServicesFutures = [DiscoverServicesFuture]() @@ -263,11 +264,12 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral } peripheral.writeValue(value.data, for: gattCharacteristic, type: type) + // Wait for future response + let future = CharacteristicWriteFuture(deviceId: deviceId, characteristicId: gattCharacteristic.uuid.uuidStr, serviceId: gattCharacteristic.service?.uuid.uuidStr, result: completion) if type == CBCharacteristicWriteType.withResponse { - // Wait for future response - characteristicWriteFutures.append(CharacteristicWriteFuture(deviceId: deviceId, characteristicId: gattCharacteristic.uuid.uuidStr, serviceId: gattCharacteristic.service?.uuid.uuidStr, result: completion)) + characteristicWriteFutures.append(future) } else { - completion(Result.success({}())) + characteristicWriteWithoutResponseFutures.append(future) } } @@ -412,6 +414,16 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral } } + public func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) { + characteristicWriteWithoutResponseFutures.removeAll { future in + if future.deviceId == peripheral.uuid.uuidString { + future.result(Result.success({}())) + return true + } + return false + } + } + public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { characteristicWriteFutures.removeAll { future in if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr { diff --git a/example/lib/peripheral_details/peripheral_detail_page.dart b/example/lib/peripheral_details/peripheral_detail_page.dart index 9c93781..651415a 100644 --- a/example/lib/peripheral_details/peripheral_detail_page.dart +++ b/example/lib/peripheral_details/peripheral_detail_page.dart @@ -129,7 +129,7 @@ class _PeripheralDetailPageState extends State { } } - Future _writeValue() async { + Future _writeValue({required bool withResponse}) async { BleCharacteristic? selectedCharacteristic = this.selectedCharacteristic; if (selectedCharacteristic == null || !valueFormKey.currentState!.validate() || @@ -146,13 +146,8 @@ class _PeripheralDetailPageState extends State { } try { - await selectedCharacteristic.write( - value, - withResponse: _hasSelectedCharacteristicProperty( - [CharacteristicProperty.writeWithoutResponse], - ), - ); - _addLog('Write', value); + await selectedCharacteristic.write(value, withResponse: withResponse); + _addLog('Write${withResponse ? "" : "WithoutResponse"}', value); } catch (e) { debugPrint(e.toString()); _addLog('WriteError', e); @@ -377,11 +372,19 @@ class _PeripheralDetailPageState extends State { discoveredServices.isNotEmpty && _hasSelectedCharacteristicProperty([ CharacteristicProperty.write, - CharacteristicProperty.writeWithoutResponse ]), - onPressed: _writeValue, + onPressed: () => _writeValue(withResponse: true), text: 'Write', ), + PlatformButton( + enabled: isConnected && + discoveredServices.isNotEmpty && + _hasSelectedCharacteristicProperty([ + CharacteristicProperty.writeWithoutResponse, + ]), + onPressed: () => _writeValue(withResponse: false), + text: 'WriteWithoutResponse', + ), PlatformButton( enabled: isConnected && discoveredServices.isNotEmpty && diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index e0cabd0..7ec7ff3 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -20,9 +20,9 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin SPEC CHECKSUMS: - device_info_plus: b0fafc687fb901e2af612763340f1b0d4352f8e5 + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - universal_ble: ff19787898040d721109c6324472e5dd4bc86adc + universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/pubspec.yaml b/pubspec.yaml index fb0ace1..34b97ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: universal_ble description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter -version: 0.20.0 +version: 0.20.1 homepage: https://navideck.com repository: https://github.com/Navideck/universal_ble issue_tracker: https://github.com/Navideck/universal_ble/issues