flutter_blue
flutter_blue copied to clipboard
PlatformException(set_notification_error, error when writing the descriptor, null) on setNotifyValue
I'm trying to turn on notifications for a characteristic, but every time that I run characteristic(true)
I get this error PlatformException(set_notification_error, error when writing the descriptor, null)
. Here's a piece of my code:
BluetoothCharacteristic ss;
List<BluetoothService> services = await connectedDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString().compareTo("0d3448ae-12ec-4eee-8adf-e4330cfcfd5d") == 0)
{
service.characteristics.forEach((c) {
if(c.uuid.toString().compareTo("31b3a584-9c79-438d-8b41-c033a9089e8b") == 0)
{
ss = c;
}
});
}
});
await ss.setNotifyValue(true);
ss.value.listen((value) {
_status = value.elementAt(0);
});
I am experiencing this too, but only with a characteristic that is not readable (flutter_blue 0.6.0+1). (Tool is called LightBlue) Here's what I mean: I can setNotifyValue to true here, without a problem: But here, I am getting exactly the same error as @rma6.
It seems like a bug.
Here some code I am using:
// services come from discoverServices
// this is working
var stateCharacteristic = _services
.singleWhere((service) => service.uuid == STATE_SERVICE)
.characteristics
.singleWhere((c) => c.uuid == STATE_RESPONSE)
..setNotifyValue(true);
// this isn't working => see screenshots above
var infoCharacteristic = _services
.singleWhere((service) => service.uuid == INFO_SERVICE)
.characteristics
.singleWhere((c) => c.uuid == INFO_RESPONSE)
..setNotifyValue(true);
Since I am calling those right after eachother I thought that this might be a problem (since I am not respecting the return of the future yet), but I turned them around and tried respecting Future and even let it run without the first one. Same result, the lower one doesn't subscribe.
@rma6 most probably
await ss.setNotifyValue(true);
runs before you finish iterating through the services/characteristics. If you run this it would work:
BluetoothCharacteristic ss;
List<BluetoothService> services = await connectedDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString().compareTo("0d3448ae-12ec-4eee-8adf-e4330cfcfd5d") == 0)
{
service.characteristics.forEach((c) {
if(c.uuid.toString().compareTo("31b3a584-9c79-438d-8b41-c033a9089e8b") == 0)
{
ss = c;
await ss.setNotifyValue(true);
ss.value.listen((value) {
_status = value.elementAt(0);
}
});
}
});
});
+1
+1
@aytunch I think that your code example is wrong. You should not call await inside forEach method
The issue is solved if we have a delay between consecutive setNotifyValue.
The issue is solved if we have a delay between consecutive setNotifyValue.
could you paste your code, i have the same problem, thank you
@aytunch The snippet you provided did not solve the problem
In the example above, await inside a forEach doesn't work. Separate discovery of service/characteristics and setNotifyValue call rather than making it in a single loop.
For example
accelerometerCharacteristic = _getAccelerometerCharacteristic(sensorService);
gyroscopeCharacteristic = _getGyroscopeCharacteristic(sensorService);
if(accelerometerCharacteristic` == null || gyroscopeCharacteristic == null){
return false;
}else {
bool accelerometerSubscribed;
bool gyroscopeSubsribed;
if (!accelerometerCharacteristic.isNotifying) {
accelerometerSubscribed =
await accelerometerCharacteristic.setNotifyValue(true);
}
await Future.delayed(const Duration(milliseconds: 500));
if (!gyroscopeCharacteristic.isNotifying) {
gyroscopeSubsribed = await gyroscopeCharacteristic.setNotifyValue(true);
}
@itsgk92 I did like this, and it continues throwing the exception:
` Future<void> _connect (BluetoothDevice dev) async {
String envSensServiceUUID = '0000181a-0000-1000-8000-00805f9b34fb';
String tempCharUUID = '00002a6e-0000-1000-8000-00805f9b34fb';
List<int> tempValue;
List<BluetoothCharacteristic> characteristics;
BluetoothCharacteristic c, MyChar;
BluetoothService s, Myservice;
bool tempCharSubscribed = false;
await dev.connect();
print("CONNECTED");
List<BluetoothService> services = await dev.discoverServices();
for (s in services) {
if (s.uuid.toString() == envSensServiceUUID) {
Myservice = s;
}
}
for (c in Myservice.characteristics) {
if (c.uuid.toString() == tempCharUUID) {
MyChar = c;
}
}
tempCharSubscribed = await MyChar.setNotifyValue(true);
} `
For me a synchronized call to setNotifyValue
sufficed. I used synchronized package to achive that:
await _lock.synchronized(() async {
await characteristic.setNotifyValue(true);
},
);
Also funny thing is that the exception only happend on Android.
I had encountered the same issue. Here's my solution to solved:
- Use
for in
instead offorEach
; - Add
await
keyword before method.setNotifyValue
; - Add a Delayed moment between any actions;
Here's my codes:
List<BluetoothService> services = await currentDevice.result.device.discoverServices();
for (BluetoothService s in services) {
// services.forEach((s) async {
if (s.uuid == DATA_SERVICE_UDID) {
BLELog('>>> Discover DATA_SERVICE_UDID');
// await s.characteristics.forEach((c) async {
for (BluetoothCharacteristic c in s.characteristics) {
if (c.uuid == CMD_WRITE_UDID) {
BLELog('>>> >>> Discover CMD_WRITE_UDID');
_CMD_WRITE_CHAR = c;
} else if (c.uuid == CMD_NOTIFY_UDID) {
BLELog('>>> >>> Discover CMD_NOTIFY_UDID');
_CMD_NOTIFY_CHAR = c;
await _CMD_NOTIFY_CHAR.setNotifyValue(true);
_subscriptions.add(_CMD_NOTIFY_CHAR.value.listen(_didRecvCommandRespond));
await Future.delayed(CommandDelayDuration);
BLELog('>>> >>> Delayed completed');
} else if (c.uuid == STATE_NOTIFY_UDID) {
BLELog('>>> >>> Discover STATE_NOTIFY_UDID');
_STATE_NOTIFY_CHAR = c;
await _STATE_NOTIFY_CHAR.setNotifyValue(true);
_subscriptions.add(_STATE_NOTIFY_CHAR.value.listen(_didRecvStateRespond));
await Future.delayed(CommandDelayDuration);
BLELog('>>> >>> Delayed completed');
}
else {
BLELog('>>> >>> Discover Unknow Char ${c.uuid}');
}
// });
}
} else {
BLELog('>>> Discover Unknow Service : ${s.uuid}');
}
// });
}
I think this issue can be closed because its working as expected in current version 0.7.3
as long as you await
calls to setNotifyValue(true)
AND as per the previous comment, the key is to not use async forEach()
!
A very good explaination as to why async forEach()
causes a problem is documented in this SO question.
So for example I used await Future.forEach()
instead.
Hi guys i face this for two days this is my solution it work for me i hope it will solve ur problem i think we need await every function return future
discoverService(List<BluetoothDevice> device){ device.forEach((d) async { List<BluetoothService> ser = await d.discoverServices(); print("this is llist of servieces"); for(BluetoothService service in ser){ for(BluetoothCharacteristic chr in service.characteristics){ if(chr.properties.notify){ await chr.setNotifyValue(true); print("this notify bit now after setNotifiction true ${chr.isNotifying}"); chr.value.listen((event) { print("this is last value of chr = ${event.toString()}"); }); await Future.delayed(Duration(seconds: 3));
print("${chr.uuid}");
}
}
}
print("this is llist of servieces");
});}
For me a synchronized call to
setNotifyValue
sufficed. I used synchronized package to achive that:await _lock.synchronized(() async { await characteristic.setNotifyValue(true); }, );
Also funny thing is that the exception only happend on Android.
It's works!
The fact that adding a synchronized call suggests that you were making a second setNotifyValue( true )
call before the first one had completed. Note that the conditional !gyroscopeCharacteristic.isNotifying()
doesn't prevent multiple simultaneous asynchronous calls since the second one starts before the first has completed.
In @saramonteiro 's example, this should work:
bool subscribeAlreadyCalled = false;
Future<void> _connect (BluetoothDevice dev) async {
String envSensServiceUUID = '0000181a-0000-1000-8000-00805f9b34fb';
String tempCharUUID = '00002a6e-0000-1000-8000-00805f9b34fb';
List<int> tempValue;
List<BluetoothCharacteristic> characteristics;
BluetoothCharacteristic c, MyChar;
BluetoothService s, Myservice;
bool tempCharSubscribed = false;
await dev.connect();
print("CONNECTED");
List<BluetoothService> services = await dev.discoverServices();
for (s in services) {
if (s.uuid.toString() == envSensServiceUUID) {
Myservice = s;
}
}
for (c in Myservice.characteristics) {
if (c.uuid.toString() == tempCharUUID) {
MyChar = c;
}
}
if (!subscribeAlreadyCalled) {
subscribeAlreadyCalled = true;
tempCharSubscribed = await MyChar.setNotifyValue(true);
}
}
@rma6 most probably
await ss.setNotifyValue(true);
runs before you finish iterating through the services/characteristics. If you run this it would work:BluetoothCharacteristic ss; List<BluetoothService> services = await connectedDevice.discoverServices(); services.forEach((service) { if(service.uuid.toString().compareTo("0d3448ae-12ec-4eee-8adf-e4330cfcfd5d") == 0) { service.characteristics.forEach((c) { if(c.uuid.toString().compareTo("31b3a584-9c79-438d-8b41-c033a9089e8b") == 0) { ss = c; await ss.setNotifyValue(true); ss.value.listen((value) { _status = value.elementAt(0); } }); } });
});
This helped me 👍 Thank you
For anyone looking into this in the future, I decided to switch this library out (3 years out of date now) for a fork of this one, called flutter blue plus.
It'll take some rewriting in your code to get set up with, but this library is much more reliable than the latest release of the original Flutter Blue library.