HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描

发布于 1周前 作者 itying888 来自 鸿蒙OS

HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描
<markdown _ngcontent-spp-c237="" class="markdownPreContainer">

前言

在鸿蒙系统(HarmonyOS)环境下,BLE(Bluetooth Low Energy)蓝牙设备扫描是一项核心功能,旨在帮助应用程序高效地探测周围低功耗蓝牙设备,为诸如智能穿戴、健康监控、智能家居等物联网设备的互连打下基础。

扫描机制概览

鸿蒙系统利用BLE 5.2技术标准,提供先进的扫描能力,确保在功耗控制与连接效率之间达到平衡。BLE扫描主要分为被动扫描和主动扫描两种模式。被动扫描是指设备监听周围的广播信息,不主动发起连接请求;而主动扫描则会在接收到广播的同时,向广播设备发送扫描请求,以获取更详尽的设备信息。这两种模式可根据应用需求灵活选择,通过配置扫描参数实现最优资源利用。

API与功能支持

鸿蒙系统的蓝牙API为开发者设计了一系列接口,以便轻松集成BLE设备扫描功能。核心API如startBLEScanstopBLEScan分别用于启动和终止扫描过程。开发者可以通过ScanSettings对象细致地配置扫描参数,比如设定扫描周期、模式、匹配过滤条件等,以优化扫描策略。同时,ScanFilter功能允许针对特定设备特征(如服务UUID、设备名)进行筛选,从而精准定位目标设备。

事件响应与数据处理

一旦有设备被扫描发现,系统将触发BLEDeviceFind事件,此时应用可获取设备的基本信息,包括设备名、MAC地址及信号强度(RSSI)等。此外,若扫描过程中遇到任何错误,如权限不足或硬件故障,通过事件捕获机制获取错误信息,便于开发者及时采取应对措施。

安全与权限管理

在确保用户体验和数据安全的前提下,鸿蒙系统对蓝牙权限管理严格要求。应用在执行扫描前必须获得相应的权限,例如ohos.permission.ACCESS_BLUETOOTH,并且遵循系统指引处理用户许可,保障用户隐私。开发者应当在应用中合理请求权限,并在用户授权后方可执行蓝牙操作。

文档和实践指导

官方文档不仅提供了详尽的API说明,还包含丰富的示例代码,指导开发者如何从零开始配置蓝牙适配器、设定扫描参数、监听扫描事件直至成功识别BLE设备的全过程。这些实例代码极大地降低了开发难度,让开发者能迅速掌握BLE设备扫描的实践技巧。接下来,给大家分享下,BLE 蓝牙设备的封装过程。

文档地址:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-bluetooth-ble-V5

蓝牙扫描封装

配置权限

在工程的src/main/module.json5中配置蓝牙权限

"requestPermissions": [
  // ...
  {
    "name": "ohos.permission.ACCESS_BLUETOOTH",
    "reason": "$string:app_name",
    "usedScene": {
      "abilities": [ "EntryAbility"],
      "when":"inuse"
    }
  }
]
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

基础封装

使用 class 封装扫描能力

  1. 新建低功耗蓝牙封装模块
class EcBlueDevice {
  scanDevice() {

} }

export const ecDevice = new EcBlueDevice() <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

  1. 调用 ble 扫描方法

文档中心 startBLEScan(filters: Array, options?: ScanOptions): void 发起BLE扫描流程。

需要权限:ohos.permission.ACCESS_BLUETOOTH

系统能力:SystemCapability.Communication.Bluetooth.Core。

参数:

参数名 类型 必填 说明
filters Array<ScanFilter> 表示扫描结果过滤策略集合,如果不使用过滤的方式,该参数设置为null。
options ScanOptions 表示扫描的参数配置,可选参数。

错误码: 以下错误码的详细介绍请参见蓝牙服务子系统错误码

错误码ID 错误信息
2900001 Service stopped.
2900003 Bluetooth switch is off.
2900099 Operation failed.

示例:

import { BusinessError } from '@ohos.base';
function onReceiveEvent(data: Array<ble.ScanResult>) {
    console.info('BLE scan device find result = '+ JSON.stringify(data));
}
try {
    ble.on("BLEDeviceFind", onReceiveEvent);
    let scanFilter: ble.ScanFilter = {
            deviceId:"XX:XX:XX:XX:XX:XX",
            name:"test",
            serviceUuid:"00001888-0000-1000-8000-00805f9b34fb"
        };
    let scanOptions: ble.ScanOptions = {
    interval: 500,
    dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
    matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
    }
    ble.startBLEScan([scanFilter],scanOptions);
} catch (err) {
    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  1. 根据示例封装基础扫描方法
  scanDevice() {
    // 1. 监听扫描结果
    ble.on("BLEDeviceFind", (devices: ble.ScanResult[])=>{
      // 打印扫到设备
      devices.forEach(item => {
        Log.warn(`扫描中...\n ${item.deviceName || '--'} ${item.deviceId}`)
      })
    });
    //  2. 开启扫描
    let scanOptions: ble.ScanOptions = {
      interval: 500,
      dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
      matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
    }
    ble.startBLEScan(null, scanOptions);
  }
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

问题:

  1. 扫描会一直执行
  2. 扫描结果重复
  1. 添加定时器控制扫描的时间
// 扫描定时器 id
timer: number = 0

scanDevice() { if (this.timer) { clearTimeout(this.timer) } // 1. 监听扫描结果 // … // 2. 开启扫描 // …

<span class="hljs-comment"><span class="hljs-comment">// 3. 开启定时器控制时长</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.timer = setTimeout(() =&gt; {
  ble.off(<span class="hljs-string"><span class="hljs-string">'BLEDeviceFind'</span></span>)
  ble.stopBLEScan()
}, <span class="hljs-number"><span class="hljs-number">10000</span></span>)

} <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

  1. 添加扫描结果的回调函数
// 类型定义
export type ScanCallback = (list: ble.ScanResult[]) => void
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
+  scanDevice(callback: ScanCallback) {
    if (this.timer) {
      clearTimeout(this.timer)
    }
    // 1. 监听扫描结果
+    let list: ble.ScanResult[] = []
    ble.on("BLEDeviceFind", (devices: ble.ScanResult[]) => {
      // 打印扫到设备
      devices.forEach(item => {
        Log.warn(`扫描中...\n ${item.deviceName || '--'} ${item.deviceId}`)
      })
+      list.push(...devices)
    });
    //  2. 开启扫描
    // ...
// 3. 开启定时器控制时长
this.timer = setTimeout(() =&gt; {

+ callback(list) ble.off(‘BLEDeviceFind’) ble.stopBLEScan() }, 10000) } <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

  1. 对扫描结果进行去重
  1. 过滤没有重复的有效设备
  2. 追加进列表
    // 1. 监听扫描结果
    let list: ble.ScanResult[] = []
    ble.on("BLEDeviceFind", (devices: ble.ScanResult[]) => {
      // 打印扫到设备
      devices.forEach(item => {
        Log.warn(`扫描中...\n ${item.deviceName || '--'} ${item.deviceId}`)
      })
+      const validDevices = devices.filter(dv => {
+        return list.some(item => item.deviceId === dv.deviceId) === false
      })
+      if (validDevices.length > 0) {
        list.push(...validDevices)
      }
    });
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

增强封装

需求:根据条件过滤扫描设备,获取指定条件设备

  1. 支持按照mac地址精确搜索匹配=》匹配一个
  2. 支持按照设备名称模糊匹配=》匹配多个
interface ScanFilter {
  type: 'mac' | 'name'
  value: string
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
mac 地址匹配
  1. 某血糖 mac 地址匹配

血糖适用:通过广播获取设备 mac 地址并匹配

  /**
   * 通过广播数据获取设备mac
   * [@param](/user/param) data
   * [@returns](/user/returns) FF-FF-11-57-B6-5B
   */
  getMac(data: ArrayBuffer) {
    //  1. Uint8Array处理二进制
    const arr = new Uint8Array(data).slice(6, 12)
    let mac = ''
    // 2. 转换 16 进制
    arr.forEach((item, i) => {
      mac += i === 0 ? '' : '-'
      mac += item.toString(16)
        .padStart(2, '0')
        .toUpperCase()
    })
    return mac
  }
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  1. 某门禁 mac 地址匹配

门禁适用:通过蓝牙名称匹配

  /**
   *
   * [@param](/user/param) nodeId 10-8A-9B-8F-EC-3C
   * [@param](/user/param) deviceName JL-ITHEIMA-8A10(BLE)
   * [@returns](/user/returns)
   */
  isMatchDoor(nodeId: string, deviceName: string) {
    const adrs = nodeId.split('-')
    const flag = adrs[1] + adrs[0]
    return deviceName.includes(flag)
  }
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
添加过滤条件

通过指定过滤条件参数,对设备进行过滤处理

  1. 根据条件过滤设备
const validDevices = devices.filter(dv => {
  if (scanFilter.type === 'mac') {
    let flag: boolean
    if (!scanFilter.door) {
      flag = this.getMac(dv.data) === scanFilter.value
    } else {
      flag = this.isMatchDoor(scanFilter.value, dv.deviceName)
    }
    return list.some(item => item.deviceId === dv.deviceId) === false && flag
  } else {
    return list.some(item => item.deviceId === dv.deviceId) === false &&
      dv.deviceName.startsWith(scanFilter.value)
  }
})
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  1. 精确匹配模式:匹配到后结束扫描
      if (validDevices.length > 0) {
        list.push(...validDevices)
        // 精确匹配到后直接结束扫描
        if (scanFilter.type === 'mac') {
          callback(list)
          clearTimeout(this.timer)
          ble.off('BLEDeviceFind')
          ble.stopBLEScan()
        }
      }
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  1. 测试

总结

鸿蒙系统通过提供统一且易于使用的API接口,startBLEScan、stopBLEScan以及配置ScanSettings和ScanFilter,极大简化了开发者实现蓝牙设备扫描的代码编写工作。开发者无需深入了解底层蓝牙协议细节,即可快速集成扫描功能到应用中。

同时通过高度优化的API集和详细的开发指南,为开发者搭建了一个强大且灵活的平台,以实现高效的BLE设备扫描功能,推动智能家居、健康监测等领域应用的创新与发展。

</markdown>

关于HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描的问题,您也可以访问:https://www.itying.com/category-93-b0.html 联系官网客服。
6 回复
老师您好 目前遇到场景是 手机与车机之间蓝牙通信 手机通过蓝牙发送指令给车机 车辆解锁 打火等操作 这些也是根据 订阅BLE设备发现上报事件 进行操作嘛

如果车机端是通过蓝牙协议的话,流程类似的哈

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

来个扫描连接,数据发送的封装库就完美了,大佬

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

回到顶部