Flutter_blue and Adafruit Bluefruit BLE特征错误:无法定位CCCD描述符的特征.&



我目前遇到了一个问题与我的ble项目的扑动。我正在使用Paul DeMarco的flutter blue软件包,该应用程序的附加页面基于"ThatProject"youtube上的灰尘传感器。我有一个Adafruit羽毛32u4板,我试图通知客户(我的扑动应用程序),它有一系列的数字发送,但我没有得到任何输出。我能够连接到设备,并且似乎正确地发送服务UUID和特征UUID,但我不确定它是否带有适当的属性。我正在使用adafruit BLE代码对板进行编程,如果我使用adafruit的应用程序,我可以获得值。我只是想在我自己的flutter应用程序上获得值。

我遇到如下错误:

E/flutter (32139): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(set_notification_error, could not locate CCCD descriptor for characteristic: 6e400002-b5a3-f393-e0a9-e50e24dcca9e, null, null)
这是我的代码。我相信缺失的CCCD来自这部分:
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:oscilloscope/oscilloscope.dart';
class SensorPage extends StatefulWidget {
const SensorPage({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
@override
_SensorPageState createState() => _SensorPageState();
}
class _SensorPageState extends State<SensorPage> {
final String SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
final String CHARACTERISTIC_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
bool isReady;
Stream<List<int>> stream;
List<double> traceDust = List();
@override
void initState() {
super.initState();
isReady = false;
connectToDevice();
}
connectToDevice() async {
// if (widget.device == null) {
//   // _Pop();
//   return;
// }
//timeout timer, watchdog timer if you will
new Timer(const Duration(seconds: 15), () {
if (!isReady) {
disconnectFromDevice();
//   _Pop();
}
});
await widget.device.connect();
discoverServices();
}
disconnectFromDevice() {
if (widget.device == null) {
//_Pop();
return;
}
widget.device.disconnect();
}
discoverServices() async {
// if (widget.device == null) {
//   //  _Pop();
//   return;
// }
BluetoothCharacteristic ss;
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
debugPrint("This Service UUID is!${service.uuid.toString()}");
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
debugPrint("This char UUID is!${characteristic.uuid.toString()}");
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
debugPrint("Here is !isNotifying: ${!characteristic.isNotifying}");
debugPrint("Here is characteristic.value: ${characteristic.value}");
ss = characteristic;
stream = ss.value;
setState(() {
isReady = true;
});
} //this one
});
} //this one
});
await ss.setNotifyValue(true);
stream = ss.value;
if (!isReady) {
// _Pop();
}
}
Future<bool> _onWillPop() {
return showDialog(
context: context,
builder: (context) =>
new AlertDialog(
title: Text('Are you sure?'),
content: Text('Do you want to disconnect device and go back?'),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No')),
new FlatButton(
onPressed: () {
disconnectFromDevice();
Navigator.of(context).pop(true);
},
child: new Text('Yes')),
],
) ??
false);
}
// _Pop() {
//   Navigator.of(context).pop(true);
// }
String _dataParser(List<int> dataFromDevice) {
debugPrint("current value is-> ${utf8.decode(dataFromDevice)}");
return utf8.decode(dataFromDevice);
}
@override
Widget build(BuildContext context) {
Oscilloscope oscilloscope = Oscilloscope(
showYAxis: true,
padding: 0.0,
backgroundColor: Colors.black,
traceColor: Colors.white,
yAxisMax: 3000.0,
yAxisMin: 0.0,
dataSet: traceDust,
);
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text('Optical Dust Sensor'),
),
body: Container(
child: !isReady
? Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
if (snapshot.connectionState ==
ConnectionState.active) {
debugPrint("snapshot.error: ${snapshot.error}.");
debugPrint("snapshot.data: ${snapshot.error}.");
debugPrint(
"snapshot.connectionState: ${snapshot.connectionState}.");
debugPrint("snapshot.hasdata?: ${snapshot.hasData}.");
var currentValue = _dataParser(snapshot.data);
traceDust.add(double.tryParse(currentValue) ?? 0);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Current value from Sensor',
style: TextStyle(fontSize: 14)),
Text('$currentValue ug/m3',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24))
]),
),
Expanded(
flex: 1,
child: oscilloscope,
)
],
));
} else {
return Text('Check the stream');
}
},
),
)),
),
);
}
}
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:K9Harness/Pages/Sensor_page.dart';
import 'package:K9Harness/Bluetooth/widgets.dart';
import 'package:flutter_blue/flutter_blue.dart';
class MyBluetoothPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
color: Colors.lightBlue,
home: StreamBuilder<BluetoothState>(
stream: FlutterBlue.instance.state,
initialData: BluetoothState.unknown,
builder: (c, snapshot) {
final state = snapshot.data;
if (state == BluetoothState.on) {
return FindDevicesScreen();
}
return BluetoothOffScreen(state: state);
}),
);
}
}
class BluetoothOffScreen extends StatelessWidget {
const BluetoothOffScreen({Key key, this.state}) : super(key: key);
final BluetoothState state;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${state.toString().substring(15)}.',
style: Theme.of(context)
.primaryTextTheme
.subhead
.copyWith(color: Colors.white),
),
],
),
),
);
}
}
class FindDevicesScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Find Devices'),
),
body: RefreshIndicator(
onRefresh: () => FlutterBlue.instance
.startScan(
scanMode: ScanMode.balanced,
withServices: [
Guid("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
], //FIXME check the other ways where ".startScan" is implemented
timeout: Duration(seconds: 4))
.catchError((error) {
print("error starting scan $error");
}),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<List<BluetoothDevice>>(
stream: Stream.periodic(Duration(seconds: 2))
.asyncMap((_) => FlutterBlue.instance.connectedDevices),
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map((d) => ListTile(
title: Text(d.name),
subtitle: Text(d.id.toString()),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return RaisedButton(
child: Text('OPEN'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
DeviceScreen(device: d))),
);
}
return Text(snapshot.data.toString());
},
),
))
.toList(),
),
),
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return SensorPage(device: r.device);
})),
),
)
.toList(),
),
),
],
),
),
),
floatingActionButton: StreamBuilder<bool>(
stream: FlutterBlue.instance.isScanning,
initialData: false,
builder: (c, snapshot) {
if (snapshot.data) {
return FloatingActionButton(
child: Icon(Icons.stop),
onPressed: () => FlutterBlue.instance.stopScan(),
backgroundColor: Colors.red,
);
} else {
return FloatingActionButton(
child: Icon(Icons.search),
onPressed: () => FlutterBlue.instance
.startScan(
scanMode: ScanMode.balanced,
withServices: [
Guid("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
], //FIXME check the other ways where ".startScan" is implemented
timeout: Duration(seconds: 4))
.catchError((error) {
print("error starting scan $error");
}));
}
},
),
);
}
}
class DeviceScreen extends StatelessWidget {
const DeviceScreen({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
List<Widget> _buildServiceTiles(List<BluetoothService> services) {
return services
.map(
(s) => ServiceTile(
service: s,
characteristicTiles: s.characteristics
.map(
(c) => CharacteristicTile(
characteristic: c,
onReadPressed: () => c.read(),
onWritePressed: () => c.write([13, 24]),
onNotificationPressed: () =>
c.setNotifyValue(!c.isNotifying),
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
onReadPressed: () => d.read(),
onWritePressed: () => d.write([11, 12]),
),
)
.toList(),
),
)
.toList(),
),
)
.toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(device.name),
actions: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) {
VoidCallback onPressed;
String text;
switch (snapshot.data) {
case BluetoothDeviceState.connected:
onPressed = () => device.disconnect();
text = 'DISCONNECT';
break;
case BluetoothDeviceState.disconnected:
onPressed = () => device.connect();
text = 'CONNECT';
break;
default:
onPressed = null;
text = snapshot.data.toString().substring(21).toUpperCase();
break;
}
return FlatButton(
onPressed: onPressed,
child: Text(
text,
style: Theme.of(context)
.primaryTextTheme
.button
.copyWith(color: Colors.white),
));
},
)
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text(
'Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data ? 1 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 18.0,
height: 18.0,
),
onPressed: null,
)
],
),
),
),
),
StreamBuilder<int>(
stream: device.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: Text('MTU Size'),
subtitle: Text('${snapshot.data} bytes'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () => device.requestMtu(223),
),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
initialData: [],
builder: (c, snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data),
);
},
),
],
),
),
);
}
}

我弄清楚了,我的特征UUID是6e400002-b5a3-f393-e0a9-e50e24dcca9e,它应该是6e400003-b5a3-f393-e0a9-e50e24dcca9e,因为对于羽毛,通知/通知特征必须是0x0003而不是2。当我改变这个值时,它正确地传递了CCCD。

最新更新