如何解决Flutter_blue 和 Adafruit Bluefruit BLE 特征错误:找不到特征的 CCCD 描述符
我目前遇到了我的 ble 项目的颤振问题。我正在使用 Paul Demarco 的 Flutter blue 包,该应用程序的附加页面基于来自 youtube 的“ThatProject”灰尘传感器。我有一个 Adafruit Feather 32u4 板,我试图通知客户端(我的 Flutter 应用程序)它有一系列数字要发送,但我没有得到任何输出。我能够连接到设备,并且似乎可以正确发送服务 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: [],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,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,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,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,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,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,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,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,snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data),);
},);
}
}
解决方法
我想通了,我的特征 UUID 是“6e400002-b5a3-f393-e0a9-e50e24dcca9e”,它应该是“6e400003-b5a3-f393-e0a9-e50e24dcca9e”,因为通知通知特性必须是 0x0003 而不是 2。然后当我改变这个值时它正确地通过了 CCCD。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。