鸿蒙HarmonyOS NEXT开发:蓝牙服务开发(低功耗蓝牙)

一、概述

蓝牙技术是一种无线通信技术,可以在短距离内传输数据。它是由爱立信公司于1994年提出的,使用2.4 GHz的ISM频段,可以在10米左右的距离内进行通信。可以用于连接手机、耳机、音箱、键盘、鼠标、打印机等各种设备。特点是低功耗、低成本、简单易用。目前已经发展到了第五代,支持更高的数据传输速率和更广的覆盖范围。

二、实现原理

蓝牙的实现原理是基于无线电技术的短距离通信协议,使用2.4GHz频段的无线电波进行通信,使用频率跳跃技术(Frequency Hopping Spread Spectrum,FHSS)来避免与其他无线设备的干扰。在通信过程中,蓝牙设备会发送和接收数据包,并且使用不同的蓝牙协议来控制通信流程和数据传输。

三、模块介绍

  • a2dp模块(高级音频分发配置文件):A2DP是Advanced Audio Distribution Profile的缩写,即高级音频分发配置文件。它是一种蓝牙协议,允许无线传输高品质音频流,例如音乐或语音通话,同时支持双向通信,因此可以用于耳机、扬声器、汽车音响等设备。详情请参考@ohos.bluetooth.a2dp API参考。

  • ble模块(低功耗蓝牙):BLE是Bluetooth Low Energy的缩写,意为“低功耗蓝牙”。它是一种能够在低功耗情况下进行通信的蓝牙技术,与传统蓝牙相比,BLE的功耗更低,适用于需要长时间运行的低功耗设备,如智能手表、健康监测设备、智能家居等。详情请参考@ohos.bluetooth.ble API参考。

  • hfp模块(免提模式):HFP模块是指蓝牙耳机或车载蓝牙设备中的Hands-Free Profile,即“免提模式”。HFP允许用户通过蓝牙连接手机或其他蓝牙设备,实现免提通话和语音控制等功能。详情请参考@ohos.bluetooth.hfp API参考。

  • hid模块(人机接口设备):HID是Human Interface Device的缩写,即人机接口设备。在蓝牙中,HID模块是一种允许用户通过蓝牙连接键盘、鼠标、游戏手柄等人机接口设备的模块。用户可以通过HID模块将这些设备连接到蓝牙主机上,实现无线控制和输入。详情请参考@ohos.bluetooth.hid API参考。

  • pan模块(个人区域网络):PAN(Personal Area Network)是个人区域网络的缩写,是一种无线通信技术,用于将设备连接到一个小范围的网络中。例如可以将手机、电脑、打印机等设备连接到一个PAN网络中,实现文件的共享和打印。详情请参考@ohos.bluetooth.pan API参考。

  • gatt模块(通用属性):GATT是指蓝牙技术中的通用属性(Generic Attribute),它是一种用于在蓝牙低功耗设备之间传输数据的协议。GATT协议定义了一套通用的属性和服务框架,用于描述蓝牙设备之间的通信,同时蓝牙设备可以向其他设备提供服务,也可以从其他设备获取服务。详情请参考@ohos.bluetooth.ble API参考。

  • spp模块(串口协议):SPP是Serial Port Profile(串口协议)的缩写,是一种蓝牙协议,用于在蓝牙设备之间建立串行通信连接。通过SPP,蓝牙设备可以像使用串口一样进行数据传输,例如传输文件、文本等。详情请参考@ohos.bluetooth.socket API参考。

四、蓝牙权限

1、需要的权限

// module.json5

"requestPermissions":[
    {
    
    
        "name": "ohos.permission.INTERNET"
    }, 
    {
    
    
        "name": "ohos.permission.USE_BLUETOOTH"
    }, 
    {
    
    
        "name": "ohos.permission.DISCOVER_BLUETOOTH"
    }, 
    {
    
    
        "name": "ohos.permission.ACCESS_BLUETOOTH", 
        "reason": "$string:grant_location", 
        "usedScene": {
    
    
            "abilities": [
                "EntryAbility"
            ], 
            "when": "inuse"
        }
    }, 
    {
    
    
        "name": "ohos.permission.LOCATION", 
        "reason": "$string:grant_location", 
        "usedScene": {
    
    
            "abilities": [
                "EntryAbility"
            ], 
            "when": "inuse"
        }
    }
]
2、检查是否授权
import {
    
     permissionManager } from '../manager';
import {
    
     Permissions } from '@kit.AbilityKit';
import {
    
     promptAction } from '@kit.ArkUI';

aboutToAppear(): void {
    
    
    this.checkPermissions()
}

  //检查是否授权
checkPermissions() {
    
    
    const permissions: Permissions[] = ['ohos.permission.ACCESS_BLUETOOTH']
    if (permissionManager.checkPermissions(permissions)) {
    
    
        // 已授权
    } else {
    
    
        // 未授权,弹窗授权
      this.requestPermission(permissions)
    }
}

  // 申请权限
async requestPermission(permissions: Permissions[]) {
    
    
    // 申请权限
    try {
    
    
      await permissionManager.requestPermissions(permissions)
      // 允许
    } catch (error) {
    
    
      // 禁止 -> 引导用户打开设置页开启权限
      // 如果禁止授权,先弹窗询问,用户点击按钮后打开设置页
      promptAction.showDialog({
    
    
        alignment: DialogAlignment.Center,
        title: '温馨提示',
        message: '建议开启蓝牙权限,以提供更好的体验',
        buttons: [
          {
    
     text: '取消', color: '#89939c' },
          {
    
     text: '去设置', color: '#0a59f7' },
        ]
      })
        .then((res) => {
    
    
          // 用户点击了去设置
          if (res.index === 1) {
    
    
            // 通过代码打开当前应用的设置页
            permissionManager.openPermissionSettingsPage()
          }
        })
    }
}

五、蓝牙扫描

1、监听扫描结果

ble.on(‘BLEDeviceFind’)

订阅BLE设备发现上报事件。使用Callback异步回调。
监听我们扫描的结果,一旦开始扫描,发现ble低功耗蓝牙设备。

on(type: 'BLEDeviceFind', callback: Callback<Array<ScanResult>>): void
  • ScanResult
    扫描结果数据
名称 类型 可读 可写 说明
deviceId string 表示扫描到的设备地址,例如:“XX:XX:XX:XX:XX:XX”。基于信息安全考虑,此处获取的设备地址为随机MAC地址。配对成功后,该地址不会变更;已配对设备取消配对后重新扫描或蓝牙服务下电时,该随机地址会变更。
rssi number 表示扫描到的设备的rssi值。
data ArrayBuffer 表示扫描到的设备发送的广播包。
deviceName string 表示扫描到的设备名称。
connectable boolean 表示扫描到的设备是否可连接。true表示可连接,false表示不可连接。
  • 示例
    点击按钮后调用openBle这个函数,这个函数里面会开启蓝牙扫描。
import {
    
     ble } from '@kit.ConnectivityKit';
import {
    
     BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct BlePage {
    
    

// 蓝牙扫描结果
  onReceiveEvent(data: Array<ble.ScanResult>) {
    
    
    console.info('bluetooth device find = ' + JSON.stringify(data));
  }

// 开启扫描
  openBel(): void {
    
    
    try {
    
    
     // 监听扫描结果
      ble.on("BLEDeviceFind", this.onReceiveEvent)
    } catch (err) {
    
    
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  build() {
    
    
    Column() {
    
    
      Button('蓝牙扫描').onClick(() => {
    
    
        this.openBel()
      })
    }
    .height('100%')
    .width('100%')
  }
}
2、启动扫描

ble.startBLEScan

发起BLE扫描流程。启动扫描。

startBLEScan(filters: Array<ScanFilter>, options?: ScanOptions): void
  • ScanOptions
    扫描的配置参数
名称 类型 可读 可写 说明
interval number 表示扫描结果上报延迟时间,默认值为0。
dutyMode ScanDuty 表示扫描模式,默认值为SCAN_MODE_LOW_POWER。
matchMode MatchMode 表示硬件的过滤匹配模式,默认值为MATCH_MODE_AGGRESSIVE。
phyType PhyType 表示扫描中使用的PHY类型。
  • 示例
import {
    
     ble } from '@kit.ConnectivityKit';
import {
    
     BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct BlePage {
    
    
  // 信号强度阈值
  private minRssi = -100
  // 扫描到的设备列表
  @State availableDevices: Array<ble.ScanResult> = []

  //蓝牙扫描结果
  onReceiveEvent(data: Array<ble.ScanResult>) {
    
    
    console.info('bluetooth device find = ' + JSON.stringify(data));

    data.forEach(element => {
    
    
      // 信号强的并且有设备名称的保存起来
      // rssi无线信号强度的指标,值越大信号越强,设备离得越近,反之表示设备较远,信号较弱。
      if (element.rssi > this.minRssi && element.deviceName != '') {
    
    
        // 符合条件的设备保存起来
        this.addData(element)
      }
    })
  }

  // 保存符合条件的设备
  addData(data: ble.ScanResult): void {
    
    
    let bleFind = false
    // 更新
    this.availableDevices.forEach(element => {
    
    
      if (!bleFind && element.deviceId == data.deviceId) {
    
    
        console.log('BLE scan update ' + data.deviceId + ' rssi:' + data.rssi);
        element.rssi = data.rssi
        bleFind = true
      }
    })
    // 新增
    if(!bleFind){
    
    
      console.log('BLE scan add ' + data.deviceId + ' rssi:' + data.rssi);
      this.availableDevices.push(data)
    }
  }

  // 开启扫描
  openBel(): void {
    
    
    try {
    
    
      // 监听扫描结果
      ble.on("BLEDeviceFind", this.onReceiveEvent)

      let scanOptions: ble.ScanOptions = {
    
    
        interval: 500,
        dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
        matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE
      }
      // 开启扫描
      ble.startBLEScan(null, scanOptions)
    } catch (err) {
    
    
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  build() {
    
    
    Column() {
    
    
      Button('蓝牙扫描').onClick(() => {
    
    
        this.openBel()
      })
    }
    .height('100%')
    .width('100%')
  }
}

六、连接设备

1、蓝牙连接
import {
    
     ble } from '@kit.ConnectivityKit';
import {
    
     BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct BlePage {
    
    

  // 蓝牙连接
  connectBel(): void {
    
    
    try {
    
    
      // 创建一个可使用的GattClientDevice实例。
      // 对端设备地址, 例如:"XX:XX:XX:XX:XX:XX"。
      let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
      // client端发起连接远端蓝牙低功耗设备。
      device.connect();

    } catch (err) {
    
    
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }


  build() {
    
    
    Column() {
    
    
      Button('蓝牙连接').onClick(() => {
    
    
        this.connectBel()
      })
    }
    .height('100%')
    .width('100%')
  }
}
2、监听连接状态
import {
    
     ble } from '@kit.ConnectivityKit';
import {
    
     BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct BlePage {
    
    

  // 蓝牙连接
  connectBel(): void {
    
    
    try {
    
    
      // 创建一个可使用的GattClientDevice实例。
      // 对端设备地址, 例如:"XX:XX:XX:XX:XX:XX"。
      let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
      // client端发起连接远端蓝牙低功耗设备。
      device.connect();

      // 监听蓝牙连接状态
      device.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState)=>{
    
    
        let connectState: ble.ProfileConnectionState = state.state;
        if(connectState === 2){
    
    
          console.info('bluetooth connect state changed');
        }
      });

    } catch (err) {
    
    
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }


  build() {
    
    
    Column() {
    
    
      Button('蓝牙连接').onClick(() => {
    
    
        this.connectBel()
      })
    }
    .height('100%')
    .width('100%')
  }
}
  • ProfileConnectionState
    枚举,蓝牙设备的profile连接状态。
名称 说明
STATE_DISCONNECTED 0 表示profile已断连。
STATE_CONNECTING 1 表示profile正在连接。
STATE_CONNECTED 2 表示profile已连接。
STATE_DISCONNECTING 3 表示profile正在断连。

七、获取服务

import {
    
     ble } from '@kit.ConnectivityKit';
import {
    
     BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct BlePage {
    
    

  // 蓝牙连接
  connectBel(): void {
    
    
    try {
    
    
      // 创建一个可使用的GattClientDevice实例。
      // 对端设备地址, 例如:"XX:XX:XX:XX:XX:XX"。
      let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
      // client端发起连接远端蓝牙低功耗设备。
      device.connect();

      // 监听蓝牙连接状态
      device.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState)=>{
    
    
        let connectState: ble.ProfileConnectionState = state.state;
        if(connectState === 2){
    
    
          console.info('bluetooth connect state changed');
          // client端获取蓝牙低功耗设备的所有服务,即服务发现。
          device.getServices().then((result: Array<ble.GattService>) => {
    
    
            console.info('getServices successfully:' + JSON.stringify(result));
          });
        }
      });

    } catch (err) {
    
    
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }


  build() {
    
    
    Column() {
    
    
      Button('蓝牙连接').onClick(() => {
    
    
        this.connectBel()
      })
    }
    .height('100%')
    .width('100%')
  }
}
  • GattService
    返回值
名称 类型 可读 可写 说明
serviceUuid string 特定服务(service)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。
isPrimary boolean 如果是主服务设置为true,否则设置为false。
characteristics Array 当前服务包含的特征列表。
includeServices Array 当前服务依赖的其它服务。

猜你喜欢

转载自blog.csdn.net/shanghai597/article/details/146035894