Error trying to write to chracteristc on 0.6.0+2
The package I am trying to send is 20 bytes long on Android:
/flutter (21603): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: PlatformException(write_characteristic_error, writeCharacteristic failed, null)
E/flutter (21603): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:564:7)
E/flutter (21603): #1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
E/flutter (21603): <asynchronous suspension>
E/flutter (21603): #2 BluetoothCharacteristic.write (package:flutter_blue/src/bluetooth_characteristic.dart:123:10)
E/flutter (21603): <asynchronous suspension>
On iOS the error is a little different:
[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: Exception: Failed to write the characteristic
#0 BluetoothCharacteristic.write.<anonymous closure> (package:flutter_blue/src/bluetooth_characteristic.dart:142:15)
#1 _rootRunUnary (dart:async/zone.dart:1132:38)
#2 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#3 _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
#4 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
This is my sendPackage method:
_sendPackage(BLEConnectionService service, List<int> package) async {
if (package.length <= 20) {
print("Sending small package");
_characteristic.write(package);
} else {
print("Sending chunked package of ${package.length} bytes");
int chunk = 0;
int nextRemaining = package.length;
List<int> toSend;
while (nextRemaining > 0) {
toSend = package.sublist(chunk, chunk + min(20, nextRemaining));
print("Enviando chunk $toSend");
_characteristic.write(toSend);
await delay(20);
nextRemaining -= 20;
chunk += 20;
}
}
}
I am using flutter_blue: ^0.6.0+2 but the code worked fine with my previous version (which was 0.4.1)
There is any workaround for this?
And where is your code at?
I have the same problem and I am really interested in the solution too!
I do
await bluetoothCharacteristic.write(utf8.encode(command)), withoutResponse: true );
and the error occurs after some writings.
And in this way it is every time
await bluetoothCharacteristic.write(utf8.encode(command)));
And where is your code at?
I've updated the question.
I did a test and I pass 1 or 2 characters to the write command and in my case it does not seem to be the size that causes the error. The bluetooth device returns information during writing, could a collision between messages explain the error?
I did a test and I pass 1 or 2 characters to the write command and in my case it does not seem to be the size that causes the error. The bluetooth device returns information during writing, could a collision between messages explain the error?
I am chunking the packages because our hardware is the limiting part here. He can't hadle packages more than 20 bytes. But doesn't matter how much bytes we send to the hardware, it always gives the error above.
Again, in the version 0.4.1 everything was working fine. @pauldemarco Could you give us some light?
Did you try the write with the withoutResponse : true. In my case it works for some write and it crashes after.
Seems like the write calls are overlapping to the underlying platform. Please make sure to ‘await’ any calls to the plugin, and the outer functions as well. Example)
await _characteristic.write(X);
...
await sendPackage(service, package);
In my case I already have await before the write instruction and I still have the error. I notice that in my Characteristic listener I receive the writes that I send and also the returns of the bluetooth device. Is it possible that there is a problem when I send a message and return a message at the same time?
Awaiting with delay is not the same as awaiting the actual function call. Please post your code, including the outer function calls.
I just set "withoutResponse" to true and now I am able to send packages to the hardware.
But, now I am receiving duplicate characteristic updates, like https://github.com/pauldemarco/flutter_blue/issues/317.
@pauldemarco The API design got awesome BTW, congratulations! Just need to fx those small issues and it will be AWESOME.
I just set "withoutResponse" to
trueand now I am able to send packages to the hardware. But, now I am receiving duplicate characteristic updates, like #317.
Just be careful with this, when the documentation is still correct in this case, writing without response will NOT guarantee a successful write/send. Which means, you could still drop some packages you are sending out, but you will not notice it, when writing without response.
I just set "withoutResponse" to
trueand now I am able to send packages to the hardware. But, now I am receiving duplicate characteristic updates, like #317.Just be careful with this, when the documentation is still correct in this case, writing without response will NOT guarantee a successful write/send. Which means, you could still drop some packages you are sending out, but you will not notice it, when writing without response.
I understand, but when sending with response, the error mentioned on top happens =(
Here the code to write : @override void sendMessageToDevice(String command) async {
if (this.isConnected) {
await bluetoothCharacteristic.write(utf8.encode(command), withoutResponse: true );
}
}
@override void calibration() { sendMessageToDevice("I"); }
If necessary the code to set the characteristics listener : void _setServiceCharistic() async{
if (device==null) return;
device.discoverServices().then((s) {
services = s;
for(var service in services) {
if (service.uuid == Guid("0000ffe0-0000-1000-8000-00805F9B34FB")) {
for (var c in service.characteristics) {
if (c.uuid == Guid("0000ffe1-0000-1000-8000-00805F9B34FB")) {
bluetoothCharacteristic=c;
bluetoothCharacteristic.setNotifyValue(true);
Future.delayed(const Duration(milliseconds: 100), () {});
inConnectionStatus.add(ConnectionMessage(deviceState: deviceState, macAdresse: device.id.id));
valueChangedSubscriptions = bluetoothCharacteristic.value.listen((d){
...
});
}
}
}
}
});
}
@shinayser If it's working without response, that's fine. But if you ever feel the need to write with response, you could still try what paul suggested and guarantee a single write at a time by awaiting the write calls, awaiting the outer function (sendPackage) calls and maybe adding a mutex or something like this.
Currently I send only 1 write at a time to my device, there is no concurrency at this point. The first situation is that everything is fine when the device does not return anything. The second is that when a write is done and a return is expected the error will occur after a few iterations. I suppose there might be a problem when the send comes in at the same time as the receive, but I'm not sure. I see how to block the writes, but how to block the return of the device so that it does not conflict with the writes?
I just set "withoutResponse" to
trueand now I am able to send packages to the hardware. But, now I am receiving duplicate characteristic updates, like #317.@pauldemarco The API design got awesome BTW, congratulations! Just need to fx those small issues and it will be AWESOME.
You're getting duplicate characteristic updates? So on the device side that is broadcasting bluetooth you're getting duplicate writes? To elaborate... your write characteristic is being called multiple times?
The characterist.value is being called twice:
charac.value.listen((values) {
//This is getting called twice
});
I did several tests today and I confirm that there is a real problem when a write message is sent along with a message from the bluetooth device.
write ---> crash * <------- BT Device
- PlatformException (write_characteristic_error, writeCharacteristic failed, null)
If the write and read messages are sent sequentially there is no problem. Is there a way around the problem or is there a possible fix for the API?
Another question, is it normal that the bluetoothCharacteristic.value.listen recovers as much the messages coming from the write and the messages coming from bluetooth device? Normally we should not only receive messages from the bluetooth device?
Another question, is it normal that the bluetoothCharacteristic.value.listen recovers as much the messages coming from the write and the messages coming from bluetooth device? Normally we should not only receive messages from the bluetooth device?
See issue #311
I am not able to write even after setting withoutResponse to true.
write(List<int> data) async {
BluetoothDevice device = locator<BluetoothConnectionService>().device;
if (device != null) {
List<BluetoothService> services = await device.discoverServices();
print(await services[0].characteristics[1]
.write([0, 0], withoutResponse: true));
}
}
Gives me the same error as mentioned in the first comment.
I've write characteristic error also in the plugin example. Is there a release in witch is it working?
@z8a I had similar issue, can you please make sure if the characteristic you are writing to is not read-only?
@Prathik-Jain I checked writing with an HM-10 device and it worked. Then I changed the type of my device characteristic from 'Write without response' to 'Write' and it's working.
Hi, @z8a , how did you set device characteristics from 'Write without response' to 'Write' on HM-10 ? I have the same problem with flutter blue 0.7.2 on Android device. On IOS it works fine...
I had similar issues with certain BLE devices but I recently found that the root cause of these issues are just the device latency.
I mean, we should have some delay like await Future.delay(Duration(milliseconds: 100) just before such write calls.
Of course, with certain Android/iOS devices, we don't need them but they don't harm anything.
Thanks but I have already try the delay without success. I don't what I can do!!! my app is finished and deployed on AppStore, just the write function don't works on Android...
Furthermore, i have a function that writes only data without any exchange before when I click on a button, the pb is the same...
I ended up with a retry loop on the write:
import 'package:quiver/iterables.dart'; // for "partition" function
...
int mtu = await device.mtu.first; // default 20 on Android?
partition(bytes, mtu).forEach((bytes) async {
log.info('Sending chunk of ${bytes.length}: $bytes');
var retry = 0;
do {
try {
await characteristicWrite.write(bytes, withoutResponse: true);
break;
} on PlatformException {
await Future.delayed(Duration(milliseconds: 100));
}
// 3 retries to have a failsafe and avoid potential infinite loop
// TODO: Check if we cannot have a 'real/decent' max retry...
} while (retry < 3);
});
I changed a couple things to get mine to work --
- set minSdkVersion to 21 in build.gradle
- targetSdkVersion 28
- Add
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
to debug AND main Android.manifest.xml
- Change all forEach statements (run asynchronously which causes overlap) to synchronous for loops. For example:
List<BluetoothService> services = await r.device.discoverServices();
print('got services');
for (int j = 0; j < services.length; j++) {
}
from
services.forEach
My full code:
void doBluetooth() async {
batteryLevels = "";
FlutterBlue flutterBlue = FlutterBlue.instance;
print('got flutter blue instance');
updateAlreadyConnectedDevices();
await flutterBlue.startScan(timeout: Duration(seconds: 1));
print('started scan');
flutterBlue.scanResults.listen((results) async {
print('got results');
for (ScanResult r in results) {
if (r.advertisementData.serviceUuids.contains("1811") ||
r.advertisementData.serviceUuids
.contains("00001811-0000-1000-8000-00805f9b34fb")) {
print('connecting to device that contained our service');
await r.device.connect();
print('connected looking for services');
List<BluetoothService> services = await r.device.discoverServices();
print('got services');
for (int j = 0; j < services.length; j++) {
var service = services[j];
print(service.uuid);
if (service.uuid == Guid("00001811-0000-1000-8000-00805F9B34FB")) {
print('matched our uuid -- going through chars');
for (int i = 0; i < service.characteristics.length; i++) {
var characteristic = service.characteristics[i];
print(characteristic.uuid);
if (characteristic.uuid ==
Guid("00002A1F-0000-1000-8000-00805F9B34FB")) {
print('matched our char');
if (locksOn) {
print('locks is on writing 1');
await characteristic.write([1], withoutResponse: true);
} else {
print('locks not on writing 0');
await characteristic.write([0], withoutResponse: true);
}
}
}
}
}
}
}
});
}
- Change all forEach statements (run asynchronously which causes overlap) to synchronous for loops. For example:
List<BluetoothService> services = await r.device.discoverServices(); print('got services'); for (int j = 0; j < services.length; j++) { }from
services.forEach
@SethKitchen Changing the forEach statements worked for me. Thank you