文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Flutter:BLE蓝牙开发

2023-09-09 09:13

关注

说明:

使用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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯