HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描
HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描
<markdown _ngcontent-spp-c237="" class="markdownPreContainer">
前言
在鸿蒙系统(HarmonyOS)环境下,BLE(Bluetooth Low Energy)蓝牙设备扫描是一项核心功能,旨在帮助应用程序高效地探测周围低功耗蓝牙设备,为诸如智能穿戴、健康监控、智能家居等物联网设备的互连打下基础。
扫描机制概览
鸿蒙系统利用BLE 5.2技术标准,提供先进的扫描能力,确保在功耗控制与连接效率之间达到平衡。BLE扫描主要分为被动扫描和主动扫描两种模式。被动扫描是指设备监听周围的广播信息,不主动发起连接请求;而主动扫描则会在接收到广播的同时,向广播设备发送扫描请求,以获取更详尽的设备信息。这两种模式可根据应用需求灵活选择,通过配置扫描参数实现最优资源利用。
API与功能支持
鸿蒙系统的蓝牙API为开发者设计了一系列接口,以便轻松集成BLE设备扫描功能。核心API如startBLEScan和stopBLEScan分别用于启动和终止扫描过程。开发者可以通过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 封装扫描能力
- 新建低功耗蓝牙封装模块
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>- 调用 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>- 根据示例封装基础扫描方法
  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>问题:
- 扫描会一直执行
- 扫描结果重复
- 添加定时器控制扫描的时间
// 扫描定时器 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(() => {
  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>- 添加扫描结果的回调函数
// 类型定义
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(() => {
+      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. 监听扫描结果
    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>增强封装
需求:根据条件过滤扫描设备,获取指定条件设备
- 支持按照mac地址精确搜索匹配=》匹配一个
- 支持按照设备名称模糊匹配=》匹配多个
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 地址匹配
- 某血糖 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>- 某门禁 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>添加过滤条件
通过指定过滤条件参数,对设备进行过滤处理
- 根据条件过滤设备
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>- 精确匹配模式:匹配到后结束扫描
      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>- 测试
总结
鸿蒙系统通过提供统一且易于使用的API接口,startBLEScan、stopBLEScan以及配置ScanSettings和ScanFilter,极大简化了开发者实现蓝牙设备扫描的代码编写工作。开发者无需深入了解底层蓝牙协议细节,即可快速集成扫描功能到应用中。
同时通过高度优化的API集和详细的开发指南,为开发者搭建了一个强大且灵活的平台,以实现高效的BLE设备扫描功能,推动智能家居、健康监测等领域应用的创新与发展。
</markdown>关于HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描的问题,您也可以访问:https://www.itying.com/category-93-b0.html 联系官网客服。
更多关于HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS 鸿蒙Next物联网Iot开发-BLE设备扫描的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
如果车机端是通过蓝牙协议的话,流程类似的哈
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17
有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html
可以有哈
 
        
       
                   
                   
                  

