说明:
使用flutter_blue_plus插件实现低功耗蓝牙开发。
一、添加蓝牙权限:
Android网络权限(工程/android/app/src/main/AndroidManifest.xml):
iOS蓝牙权限(工程/ios/Runner/Info.plist):
... NSBluetoothAlwaysUsageDescription Need BLE permission NSBluetoothPeripheralUsageDescription Need BLE permission NSLocationAlwaysAndWhenInUseUsageDescription Need Location permission NSLocationAlwaysUsageDescription Need Location permission NSLocationWhenInUseUsageDescription Need Location permission
二、实现扫描/连接/接收BLE设备数据:
添加flutter_blue_plus插件依赖,在pubspec.yaml中:
dependencies: flutter_blue_plus: ^1.3.1 #蓝牙插件
实现BLE蓝牙设备扫描:
(1)蓝牙扫描类:
class ScanDevice { static const int SCAN_TIMEOUT = 10000; final String NAME_PREFIX = "T"; final String NAME_PREFIX_2 = "HLW"; final FlutterBluePlus _flutterBlue = FlutterBluePlus.instance; //蓝牙API final ScanCallback _callback; //回调接口 var _isScanning = false; late Timer? _timer = null; ScanDevice(this._callback); //开始扫描 void startScan({int timeout = SCAN_TIMEOUT}) { log("1.开始扫描设备 >>>>>>"); if (_isScanning) return; _isScanning = true; _flutterBlue.scanResults.listen((results) { for (ScanResult item in results) { _handlerScanResult(item); } }); _flutterBlue?.startScan(timeout: Duration(seconds: timeout)); startTimer(); } //N秒后停止扫描, 并回调通知外部 void startTimer(){ cancelTimer(); _timer = Timer(Duration(milliseconds: SCAN_TIMEOUT), () { stopScan(); //停止扫描 _callback.onStop(); //回调通知外部 }); } void cancelTimer(){ _timer?.cancel(); _timer = null; } //是否扫描中 bool isScan() { return _isScanning; } //停止扫描 void stopScan() { log("停止扫描设备 >>>>>>"); cancelTimer(); if (!_isScanning) return; _isScanning = false; _flutterBlue.stopScan(); } //处理扫描结果 void _handlerScanResult(ScanResult result) { if (!result.device.name.startsWith(NAME_PREFIX) && !result.device.name.startsWith(NAME_PREFIX_2)) return; //过滤掉非本公司的蓝牙设备 log('扫到设备, name: ${result.device.name}'); if (result.device.name.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)); //回调到外部 }}
(2)蓝牙扫描回调接口:
typedef OnFind = void Function(DeviceBean device);typedef OnStop = void Function();class ScanCallback { ScanCallback ({required this.onFind, required this.onStop}); OnFind onFind; OnStop onStop;}
(3)自定义实体类:
class DeviceBean { BluetoothDevice device; //设备 DeviceBean(this.device);}
实现连接设备/接收数据:
(1)连接设备/接收数据管理类:
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; late BluetoothCharacteristic? _writeCharacteristic = null; late ScanDevice? _scanDevice = null; late DeviceBean? _device = null; bool isConnecting = false; ConnectManager(this._gattCallback); //1.扫描 Future start(String deviceName, {int timeout = ScanDevice.SCAN_TIMEOUT}) async { if (_scanDevice == null) { _scanDevice = ScanDevice(ScanCallback( onFind: (DeviceBean device) { //扫描到设备 if(isConnecting) { _scanDevice?.stopScan(); //停止扫描 return; } if(device.device.name == deviceName){ this._device = device; _scanDevice?.stopScan(); //停止扫描 connect(device.device); //转 - 2.连接 } }, onStop: () { //停止扫描 if(this._device == null){ log("没找到设备 >>>>>>"); _gattCallback.onDeviceNotFind(); //没扫描到设备时, 回调外部 } } )); } _scanDevice?.startScan(timeout: timeout); isConnecting = false; } //2.连接 Future connect(BluetoothDevice device) async { isConnecting = true; log("2.开始连接 >>>>>>name: ${device.name}"); await device.connect(timeout: Duration(milliseconds: CON_TIMEOUT), autoConnect: false); log("连接成功 >>>>>>name: ${device.name}"); _discoverServices(device); } //3.发现服务 Future _discoverServices(BluetoothDevice device) async { log("3.开始发现服务 >>>>>>name: ${device.name}"); List services = await device.discoverServices(); log("发现服务成功 >>>>>>name: ${device.name}"); _handlerServices(device, services); //遍历服务列表,找出指定服务 isConnecting = false; } //3.1遍历服务列表,找出指定服务 void _handlerServices(BluetoothDevice device, List services){ services.forEach((sItem) { String sUuid = sItem.uuid.toString(); if(sUuid == SET_MODE_SERVICE_UUID.toString()){ //找到设置模式的服务 log("4.找到设置模式的服务 >>>>>>name: ${device.name} serviceGuid: ${SET_MODE_SERVICE_UUID.toString()}"); _readCharacteristics(device, sItem); //读取特征值 } else if(sUuid == WRITE_DATA_SERVICE_UUID.toString()){ //找到写数据的服务 log("4.找到写数据的服务 >>>>>>name: ${device.name} 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()){ //找到设置模式的特征值 log("4.0.找到设置模式的特征值 >>>>>>name: ${device.name} characteristicUUID: ${SET_MODE_CHARACTERISTIC_UUID.toString()}"); _requestMtu(device); //设置MTU _setNotificationMode(device, cItem); //设置为Notification模式(设备主动给手机发数据) } else if(cUuid == WRITE_DATA_CHARACTERISTIC_UUID.toString()){ //找到写数据的特征值 log("4.0.找到写数据的特征值 >>>>>>name: ${device.name} characteristicUUID: ${WRITE_DATA_CHARACTERISTIC_UUID.toString()}"); _writeCharacteristic = cItem; //保存写数据的征值 } } } //4.1.设置MTU Future _requestMtu(BluetoothDevice device) async { final mtu = await device.mtu.first; log("4.1.当前mtu: $mtu 请求设置mtu为512 >>>>>>name: ${device.name}"); await device.requestMtu(512); } //4.2.设置为Notification模式(设备主动给手机发数据),Indication模式需要手机读设备的数据 Future _setNotificationMode(BluetoothDevice device, BluetoothCharacteristic cItem) async { log("4.2.设置为通知模式 >>>>>>name: ${device.name}"); await cItem.setNotifyValue(true); //为指定特征的值设置通知 cItem.value.listen((value) { if (value == null || value.isEmpty) return; log("接收数据 >>>>>>name: ${device.name} 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 log("发送Notification模式给设备 >>>>>>name: ${device.name}"); dItem.write(ENABLE_NOTIFICATION_VALUE); //发送Notification模式给设备 return; } } } //发送指令到设备 Future writeCommand(List data) async { log("发送指令给设备 >>>>>>name: ${device.name} data: $data"); await _writeCharacteristic?.write(data); } //断开连接 void disconnect(BluetoothDevice device) { log("断开连接 >>>>>>name: ${device.name}"); device.disconnect(); //关闭连接 _gattCallback.onDisconnect(); //连接失败回调 }}
(2)蓝牙连接回调接口:
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;}
调用例子:
...//省略其他ConnectManager connectManager = ConnectManager(GattCallback( //1.实例化连接管理类,并监听连接状态 onDeviceNotFind: () { //没找到设备 }, onConnected: () { //连接成功回调 }, onDisconnect: () { //连接关闭回调 }, onRead: (MessageData data) { //设备发过来的数据 }));...//省略其他if(!connectManager.isConnecting){ connectManager.start("T11302002020169"); //2.开始连接蓝牙设备,T11302002020169为蓝牙设备名称}
来源地址:https://blog.csdn.net/a526001650a/article/details/127845077