connectManager.dart 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import 'dart:async';
  2. import 'package:flutter_blue_plus/flutter_blue_plus.dart';
  3. import 'package:flutter_vr/tools/YSTools.dart';
  4. class ConnectManager {
  5. final Guid SET_MODE_SERVICE_UUID = Guid("0000180f-0000-1000-8000-00805f9b34fb"); //设置模式-服务UUID
  6. final Guid SET_MODE_CHARACTERISTIC_UUID = Guid("00002a19-0000-1000-8000-00805f9b34fb"); //设置模式-特征值UUID
  7. final Guid SET_MODE_DESCRIPTOR_UUID = Guid("00002902-0000-1000-8000-00805f9b34fb"); //设置模式-特征值描述UUID(固定不变)
  8. final Guid WRITE_DATA_SERVICE_UUID = Guid("01ff0100-ba5e-f4ee-5ca1-eb1e5e4b1ce1"); //写数据-服务UUID
  9. final Guid WRITE_DATA_CHARACTERISTIC_UUID = Guid("01ff0101-ba5e-f4ee-5ca1-eb1e5e4b1ce1"); //写数据-特征值UUID
  10. final List<int> ENABLE_NOTIFICATION_VALUE = [0x01, 0x00]; //启用Notification模式
  11. final List<int> DISABLE_NOTIFICATION_VALUE = [0x00, 0x00]; //停用Notification模式
  12. final List<int> ENABLE_INDICATION_VALUE = [0x02, 0x00]; //启用Indication模式
  13. static const int CON_TIMEOUT = 10000;
  14. final GattCallback _gattCallback;
  15. BluetoothCharacteristic? _writeCharacteristic;
  16. ScanDevice? _scanDevice;
  17. DeviceBean? _device;
  18. bool isConnecting = false;
  19. ConnectManager(this._gattCallback);
  20. //1.扫描
  21. Future<void> start(String deviceName, {int timeout = ScanDevice.SCAN_TIMEOUT}) async {
  22. _scanDevice ??= ScanDevice(ScanCallback(
  23. onFind: (DeviceBean device) { //扫描到设备
  24. if(isConnecting) {
  25. _scanDevice?.stopScan(); //停止扫描
  26. return;
  27. }
  28. if(device.device.platformName == deviceName){
  29. _device = device;
  30. _scanDevice?.stopScan(); //停止扫描
  31. connect(device.device); //转 - 2.连接
  32. }
  33. },
  34. onStop: () { //停止扫描
  35. if(_device == null){
  36. LogUtil.d("没找到设备 >>>>>>");
  37. _gattCallback.onDeviceNotFind(); //没扫描到设备时, 回调外部
  38. }
  39. }
  40. ));
  41. _scanDevice?.startScan(timeout: timeout);
  42. isConnecting = false;
  43. }
  44. //2.连接
  45. Future<void> connect(BluetoothDevice device) async {
  46. isConnecting = true;
  47. LogUtil.d("2.开始连接 >>>>>>name: ${device.platformName}");
  48. await device.connect(timeout: const Duration(milliseconds: CON_TIMEOUT), autoConnect: false);
  49. LogUtil.d("连接成功 >>>>>>name: ${device.platformName}");
  50. _discoverServices(device);
  51. }
  52. //3.发现服务
  53. Future<void> _discoverServices(BluetoothDevice device) async {
  54. LogUtil.d("3.开始发现服务 >>>>>>name: ${device.platformName}");
  55. List<BluetoothService> services = await device.discoverServices();
  56. LogUtil.d("发现服务成功 >>>>>>name: ${device.platformName}");
  57. _handlerServices(device, services); //遍历服务列表,找出指定服务
  58. isConnecting = false;
  59. }
  60. //3.1遍历服务列表,找出指定服务
  61. void _handlerServices(BluetoothDevice device, List<BluetoothService> services){
  62. for (var sItem in services) {
  63. String sUuid = sItem.uuid.toString();
  64. if(sUuid == SET_MODE_SERVICE_UUID.toString()){ //找到设置模式的服务
  65. LogUtil.d("4.找到设置模式的服务 >>>>>>name: ${device.platformName} serviceGuid: ${SET_MODE_SERVICE_UUID.toString()}");
  66. _readCharacteristics(device, sItem); //读取特征值
  67. } else if(sUuid == WRITE_DATA_SERVICE_UUID.toString()){ //找到写数据的服务
  68. LogUtil.d("4.找到写数据的服务 >>>>>>name: ${device.platformName} serviceGuid: ${WRITE_DATA_SERVICE_UUID.toString()}");
  69. _readCharacteristics(device, sItem); //读取特征值
  70. }
  71. }
  72. }
  73. //4.读取特征值(读出设置模式与写数据的特征值)
  74. Future<void> _readCharacteristics(BluetoothDevice device, BluetoothService service) async {
  75. var characteristics = service.characteristics;
  76. for(BluetoothCharacteristic cItem in characteristics) {
  77. String cUuid = cItem.uuid.toString();
  78. if(cUuid == SET_MODE_CHARACTERISTIC_UUID.toString()){ //找到设置模式的特征值
  79. LogUtil.d("4.0.找到设置模式的特征值 >>>>>>name: ${device.platformName} characteristicUUID: ${SET_MODE_CHARACTERISTIC_UUID.toString()}");
  80. _requestMtu(device); //设置MTU
  81. _setNotificationMode(device, cItem); //设置为Notification模式(设备主动给手机发数据)
  82. } else if(cUuid == WRITE_DATA_CHARACTERISTIC_UUID.toString()){ //找到写数据的特征值
  83. LogUtil.d("4.0.找到写数据的特征值 >>>>>>name: ${device.platformName} characteristicUUID: ${WRITE_DATA_CHARACTERISTIC_UUID.toString()}");
  84. _writeCharacteristic = cItem; //保存写数据的征值
  85. }
  86. }
  87. }
  88. //4.1.设置MTU
  89. Future<void> _requestMtu(BluetoothDevice device) async {
  90. final mtu = await device.mtu.first;
  91. LogUtil.d("4.1.当前mtu: $mtu 请求设置mtu为512 >>>>>>name: ${device.platformName}");
  92. await device.requestMtu(512);
  93. }
  94. //4.2.设置为Notification模式(设备主动给手机发数据),Indication模式需要手机读设备的数据
  95. Future<void> _setNotificationMode(BluetoothDevice device, BluetoothCharacteristic cItem) async {
  96. LogUtil.d("4.2.设置为通知模式 >>>>>>name: ${device.platformName}");
  97. await cItem.setNotifyValue(true); //为指定特征的值设置通知
  98. cItem.lastValueStream.listen((value) {
  99. if (value.isEmpty) return;
  100. LogUtil.d("接收数据 >>>>>>name: ${device.platformName} value: $value");
  101. MessageData data = MessageData();
  102. //...省略解析设备数据的逻辑
  103. _gattCallback.onRead(data); //回调外部,返回设备发送的数据
  104. });
  105. var descriptors = cItem.descriptors;
  106. for (BluetoothDescriptor dItem in descriptors) {
  107. if (dItem.uuid.toString() == SET_MODE_DESCRIPTOR_UUID.toString()) {//找到设置模式的descriptor
  108. LogUtil.d("发送Notification模式给设备 >>>>>>name: ${device.platformName}");
  109. dItem.write(ENABLE_NOTIFICATION_VALUE); //发送Notification模式给设备
  110. return;
  111. }
  112. }
  113. }
  114. //发送指令到设备
  115. Future<void> writeCommand(List<int> data) async {
  116. LogUtil.d("发送指令给设备 >>>>>>name: ${_device?.device.platformName} data: $data");
  117. await _writeCharacteristic?.write(data);
  118. }
  119. //断开连接
  120. void disconnect(BluetoothDevice device) {
  121. LogUtil.d("断开连接 >>>>>>name: ${device.platformName}");
  122. device.disconnect(); //关闭连接
  123. _gattCallback.onDisconnect(); //连接失败回调
  124. }
  125. }
  126. class MessageData {
  127. }
  128. class ScanDevice {
  129. static const int SCAN_TIMEOUT = 10000;
  130. final String NAME_PREFIX = "T";
  131. final String NAME_PREFIX_2 = "HLW";
  132. final ScanCallback _callback; //回调接口
  133. var _isScanning = false;
  134. Timer? _timer;
  135. ScanDevice(this._callback);
  136. //开始扫描
  137. void startScan({int timeout = SCAN_TIMEOUT}) {
  138. LogUtil.d("1.开始扫描设备 >>>>>>");
  139. if (_isScanning) return;
  140. _isScanning = true;
  141. FlutterBluePlus.scanResults.listen((results) {
  142. if(results.isNotEmpty){
  143. LogUtil.d(">>>>>>$results");
  144. }
  145. for (ScanResult item in results) {
  146. _handlerScanResult(item);
  147. }
  148. });
  149. FlutterBluePlus.startScan(timeout: Duration(seconds: timeout));
  150. startTimer();
  151. }
  152. //N秒后停止扫描, 并回调通知外部
  153. void startTimer(){
  154. cancelTimer();
  155. _timer = Timer(const Duration(milliseconds: SCAN_TIMEOUT), () {
  156. stopScan(); //停止扫描
  157. _callback.onStop(); //回调通知外部
  158. });
  159. }
  160. void cancelTimer(){
  161. _timer?.cancel();
  162. _timer = null;
  163. }
  164. //是否扫描中
  165. bool isScan() {
  166. return _isScanning;
  167. }
  168. //停止扫描
  169. void stopScan() {
  170. LogUtil.d("停止扫描设备 >>>>>>");
  171. cancelTimer();
  172. if (!_isScanning) return;
  173. _isScanning = false;
  174. FlutterBluePlus.stopScan();
  175. }
  176. //处理扫描结果
  177. void _handlerScanResult(ScanResult result) {
  178. if (!result.device.platformName.startsWith(NAME_PREFIX) && !result.device.platformName.startsWith(NAME_PREFIX_2)) return; //过滤掉非本公司的蓝牙设备
  179. LogUtil.d('扫到设备, name: ${result.device.platformName}');
  180. if (result.device.platformName.startsWith(NAME_PREFIX_2)) { //针对定制过的蓝牙设备,需要从广播中获取真正的设备名称
  181. // var realName = getRealName(result); //从广播中获取真实的设备名称
  182. // if (realName != null && realName.startsWith("T")){
  183. // //...
  184. // }
  185. _callback.onFind(DeviceBean(result.device)); //回调到外部
  186. return;
  187. }
  188. _callback.onFind(DeviceBean(result.device)); //回调到外部
  189. }
  190. }
  191. typedef OnFind = void Function(DeviceBean device);
  192. typedef OnStop = void Function();
  193. class ScanCallback {
  194. ScanCallback ({required this.onFind, required this.onStop});
  195. OnFind onFind;
  196. OnStop onStop;
  197. }
  198. class DeviceBean {
  199. BluetoothDevice device; //设备
  200. DeviceBean(this.device);
  201. }
  202. typedef OnDeviceNotFind = void Function();
  203. typedef OnConnected = void Function();
  204. typedef OnDisconnect = void Function();
  205. typedef OnRead = void Function(MessageData data);
  206. class GattCallback {
  207. GattCallback ({required this.onDeviceNotFind, required this.onConnected, required this.onDisconnect, required this.onRead});
  208. OnDeviceNotFind onDeviceNotFind;
  209. OnConnected onConnected;
  210. OnDisconnect onDisconnect;
  211. OnRead onRead;
  212. }