import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_vr/tools/YSTools.dart'; class ConnectManager { final Guid SET_MODE_SERVICE_UUID = Guid("0000180f-0000-1000-8000-00805f9b34fb"); //设置模式-服务UUID final Guid SET_MODE_CHARACTERISTIC_UUID = Guid("00002a19-0000-1000-8000-00805f9b34fb"); //设置模式-特征值UUID final Guid SET_MODE_DESCRIPTOR_UUID = Guid("00002902-0000-1000-8000-00805f9b34fb"); //设置模式-特征值描述UUID(固定不变) final Guid WRITE_DATA_SERVICE_UUID = Guid("01ff0100-ba5e-f4ee-5ca1-eb1e5e4b1ce1"); //写数据-服务UUID final Guid WRITE_DATA_CHARACTERISTIC_UUID = Guid("01ff0101-ba5e-f4ee-5ca1-eb1e5e4b1ce1"); //写数据-特征值UUID final List ENABLE_NOTIFICATION_VALUE = [0x01, 0x00]; //启用Notification模式 final List DISABLE_NOTIFICATION_VALUE = [0x00, 0x00]; //停用Notification模式 final List ENABLE_INDICATION_VALUE = [0x02, 0x00]; //启用Indication模式 static const int CON_TIMEOUT = 10000; final GattCallback _gattCallback; BluetoothCharacteristic? _writeCharacteristic; ScanDevice? _scanDevice; DeviceBean? _device; bool isConnecting = false; ConnectManager(this._gattCallback); //1.扫描 Future start(String deviceName, {int timeout = ScanDevice.SCAN_TIMEOUT}) async { _scanDevice ??= ScanDevice(ScanCallback( onFind: (DeviceBean device) { //扫描到设备 if(isConnecting) { _scanDevice?.stopScan(); //停止扫描 return; } if(device.device.platformName == deviceName){ _device = device; _scanDevice?.stopScan(); //停止扫描 connect(device.device); //转 - 2.连接 } }, onStop: () { //停止扫描 if(_device == null){ LogUtil.d("没找到设备 >>>>>>"); _gattCallback.onDeviceNotFind(); //没扫描到设备时, 回调外部 } } )); _scanDevice?.startScan(timeout: timeout); isConnecting = false; } //2.连接 Future connect(BluetoothDevice device) async { isConnecting = true; LogUtil.d("2.开始连接 >>>>>>name: ${device.platformName}"); await device.connect(timeout: const Duration(milliseconds: CON_TIMEOUT), autoConnect: false); LogUtil.d("连接成功 >>>>>>name: ${device.platformName}"); _discoverServices(device); } //3.发现服务 Future _discoverServices(BluetoothDevice device) async { LogUtil.d("3.开始发现服务 >>>>>>name: ${device.platformName}"); List services = await device.discoverServices(); LogUtil.d("发现服务成功 >>>>>>name: ${device.platformName}"); _handlerServices(device, services); //遍历服务列表,找出指定服务 isConnecting = false; } //3.1遍历服务列表,找出指定服务 void _handlerServices(BluetoothDevice device, List services){ for (var sItem in services) { String sUuid = sItem.uuid.toString(); if(sUuid == SET_MODE_SERVICE_UUID.toString()){ //找到设置模式的服务 LogUtil.d("4.找到设置模式的服务 >>>>>>name: ${device.platformName} serviceGuid: ${SET_MODE_SERVICE_UUID.toString()}"); _readCharacteristics(device, sItem); //读取特征值 } else if(sUuid == WRITE_DATA_SERVICE_UUID.toString()){ //找到写数据的服务 LogUtil.d("4.找到写数据的服务 >>>>>>name: ${device.platformName} serviceGuid: ${WRITE_DATA_SERVICE_UUID.toString()}"); _readCharacteristics(device, sItem); //读取特征值 } } } //4.读取特征值(读出设置模式与写数据的特征值) Future _readCharacteristics(BluetoothDevice device, BluetoothService service) async { var characteristics = service.characteristics; for(BluetoothCharacteristic cItem in characteristics) { String cUuid = cItem.uuid.toString(); if(cUuid == SET_MODE_CHARACTERISTIC_UUID.toString()){ //找到设置模式的特征值 LogUtil.d("4.0.找到设置模式的特征值 >>>>>>name: ${device.platformName} characteristicUUID: ${SET_MODE_CHARACTERISTIC_UUID.toString()}"); _requestMtu(device); //设置MTU _setNotificationMode(device, cItem); //设置为Notification模式(设备主动给手机发数据) } else if(cUuid == WRITE_DATA_CHARACTERISTIC_UUID.toString()){ //找到写数据的特征值 LogUtil.d("4.0.找到写数据的特征值 >>>>>>name: ${device.platformName} characteristicUUID: ${WRITE_DATA_CHARACTERISTIC_UUID.toString()}"); _writeCharacteristic = cItem; //保存写数据的征值 } } } //4.1.设置MTU Future _requestMtu(BluetoothDevice device) async { final mtu = await device.mtu.first; LogUtil.d("4.1.当前mtu: $mtu 请求设置mtu为512 >>>>>>name: ${device.platformName}"); await device.requestMtu(512); } //4.2.设置为Notification模式(设备主动给手机发数据),Indication模式需要手机读设备的数据 Future _setNotificationMode(BluetoothDevice device, BluetoothCharacteristic cItem) async { LogUtil.d("4.2.设置为通知模式 >>>>>>name: ${device.platformName}"); await cItem.setNotifyValue(true); //为指定特征的值设置通知 cItem.lastValueStream.listen((value) { if (value.isEmpty) return; LogUtil.d("接收数据 >>>>>>name: ${device.platformName} value: $value"); MessageData data = MessageData(); //...省略解析设备数据的逻辑 _gattCallback.onRead(data); //回调外部,返回设备发送的数据 }); var descriptors = cItem.descriptors; for (BluetoothDescriptor dItem in descriptors) { if (dItem.uuid.toString() == SET_MODE_DESCRIPTOR_UUID.toString()) {//找到设置模式的descriptor LogUtil.d("发送Notification模式给设备 >>>>>>name: ${device.platformName}"); dItem.write(ENABLE_NOTIFICATION_VALUE); //发送Notification模式给设备 return; } } } //发送指令到设备 Future writeCommand(List data) async { LogUtil.d("发送指令给设备 >>>>>>name: ${_device?.device.platformName} data: $data"); await _writeCharacteristic?.write(data); } //断开连接 void disconnect(BluetoothDevice device) { LogUtil.d("断开连接 >>>>>>name: ${device.platformName}"); device.disconnect(); //关闭连接 _gattCallback.onDisconnect(); //连接失败回调 } } class MessageData { } class ScanDevice { static const int SCAN_TIMEOUT = 10000; final String NAME_PREFIX = "T"; final String NAME_PREFIX_2 = "HLW"; final ScanCallback _callback; //回调接口 var _isScanning = false; Timer? _timer; ScanDevice(this._callback); //开始扫描 void startScan({int timeout = SCAN_TIMEOUT}) { LogUtil.d("1.开始扫描设备 >>>>>>"); if (_isScanning) return; _isScanning = true; FlutterBluePlus.scanResults.listen((results) { if(results.isNotEmpty){ LogUtil.d(">>>>>>$results"); } for (ScanResult item in results) { _handlerScanResult(item); } }); FlutterBluePlus.startScan(timeout: Duration(seconds: timeout)); startTimer(); } //N秒后停止扫描, 并回调通知外部 void startTimer(){ cancelTimer(); _timer = Timer(const Duration(milliseconds: SCAN_TIMEOUT), () { stopScan(); //停止扫描 _callback.onStop(); //回调通知外部 }); } void cancelTimer(){ _timer?.cancel(); _timer = null; } //是否扫描中 bool isScan() { return _isScanning; } //停止扫描 void stopScan() { LogUtil.d("停止扫描设备 >>>>>>"); cancelTimer(); if (!_isScanning) return; _isScanning = false; FlutterBluePlus.stopScan(); } //处理扫描结果 void _handlerScanResult(ScanResult result) { if (!result.device.platformName.startsWith(NAME_PREFIX) && !result.device.platformName.startsWith(NAME_PREFIX_2)) return; //过滤掉非本公司的蓝牙设备 LogUtil.d('扫到设备, name: ${result.device.platformName}'); if (result.device.platformName.startsWith(NAME_PREFIX_2)) { //针对定制过的蓝牙设备,需要从广播中获取真正的设备名称 // var realName = getRealName(result); //从广播中获取真实的设备名称 // if (realName != null && realName.startsWith("T")){ // //... // } _callback.onFind(DeviceBean(result.device)); //回调到外部 return; } _callback.onFind(DeviceBean(result.device)); //回调到外部 } } typedef OnFind = void Function(DeviceBean device); typedef OnStop = void Function(); class ScanCallback { ScanCallback ({required this.onFind, required this.onStop}); OnFind onFind; OnStop onStop; } class DeviceBean { BluetoothDevice device; //设备 DeviceBean(this.device); } typedef OnDeviceNotFind = void Function(); typedef OnConnected = void Function(); typedef OnDisconnect = void Function(); typedef OnRead = void Function(MessageData data); class GattCallback { GattCallback ({required this.onDeviceNotFind, required this.onConnected, required this.onDisconnect, required this.onRead}); OnDeviceNotFind onDeviceNotFind; OnConnected onConnected; OnDisconnect onDisconnect; OnRead onRead; }