鸿蒙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
    扫描结果数据
名称类型可读可写说明
deviceIdstring表示扫描到的设备地址,例如:“XX:XX:XX:XX:XX:XX”。基于信息安全考虑,此处获取的设备地址为随机MAC地址。配对成功后,该地址不会变更;已配对设备取消配对后重新扫描或蓝牙服务下电时,该随机地址会变更。
rssinumber表示扫描到的设备的rssi值。
dataArrayBuffer表示扫描到的设备发送的广播包。
deviceNamestring表示扫描到的设备名称。
connectableboolean表示扫描到的设备是否可连接。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
    扫描的配置参数
名称类型可读可写说明
intervalnumber表示扫描结果上报延迟时间,默认值为0。
dutyModeScanDuty表示扫描模式,默认值为SCAN_MODE_LOW_POWER。
matchModeMatchMode表示硬件的过滤匹配模式,默认值为MATCH_MODE_AGGRESSIVE。
phyTypePhyType表示扫描中使用的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_DISCONNECTED0表示profile已断连。
STATE_CONNECTING1表示profile正在连接。
STATE_CONNECTED2表示profile已连接。
STATE_DISCONNECTING3表示profile正在断连。

七、获取服务

  • getServices
    client端获取蓝牙低功耗设备的所有服务,即服务发现。使用Promise异步回调。
getServices(): Promise<Array<GattService>>

返回值

名称类型可读可写说明
serviceUuidstring特定服务(service)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。
isPrimaryboolean如果是主服务设置为true,否则设置为false。
characteristicsArray当前服务包含的特征列表。
includeServicesArray当前服务依赖的其它服务。
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%')
  }
}

八、接收数据

1、接收一次
  • readCharacteristicValue
    client端读取蓝牙低功耗设备特定服务的特征值。使用Callback异步回调。
readCharacteristicValue(characteristic: BLECharacteristic, callback: AsyncCallback<BLECharacteristic>): void

参数:

参数名类型必填说明
characteristicBLECharacteristic待读取的特征值。
callbackAsyncCallbackclient读取特征值,通过注册回调函数获取。
  // 接收数据
  receiveData(): void {
    let descriptors: Array<ble.BLEDescriptor> = [];
    let bufferDesc = new ArrayBuffer(8);
    let descV = new Uint8Array(bufferDesc);
    descV[0] = 11;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
      descriptorUuid: '00002903-0000-1000-8000-00805F9B34FB',
      descriptorValue: bufferDesc
    };
    descriptors[0] = descriptor;

    let bufferCCC = new ArrayBuffer(8);
    let cccV = new Uint8Array(bufferCCC);
    cccV[0] = 1;
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
      characteristicValue: bufferCCC,
      descriptors: descriptors
    };

    try {
      let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
      device.readCharacteristicValue(characteristic, this.readCcc);
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 接收数据的回调函数
  readCcc(code: BusinessError, BLECharacteristic: ble.BLECharacteristic) {
    if (code.code != 0) {
      return;
    }
    console.info('bluetooth characteristic uuid: ' + BLECharacteristic.characteristicUuid);
    let value = new Uint8Array(BLECharacteristic.characteristicValue);
    console.info('bluetooth characteristic value: ' + value[0] + ',' + value[1] + ',' + value[2] + ',' + value[3]);
  }
2、间隔接收
  • on(‘BLECharacteristicChange’)
    订阅蓝牙低功耗设备的特征值变化事件。
on(type: 'BLECharacteristicChange', callback: Callback<BLECharacteristic>): void

参数:

参数名类型必填说明
typestring填写"BLECharacteristicChange"字符串,表示特征值变化事件。
callbackCallback表示蓝牙低功耗设备的特征值变化事件的回调函数。
  • setCharacteristicChangeNotification
    向服务端发送设置通知此特征值请求。使用Callback异步回调。
setCharacteristicChangeNotification(characteristic: BLECharacteristic, enable: boolean, callback: AsyncCallback<void>): void

参数:

参数名类型必填说明
characteristicBLECharacteristic蓝牙低功耗特征。
enableboolean启用接收notify设置为true,否则设置为false。
callbackAsyncCallback回调函数。当发送成功,err为undefined,否则为错误对象。
  // 间隔接收
  monitorData(): void {
    // 创建descriptors
    let descriptors: Array<ble.BLEDescriptor> = [];
    let arrayBuffer = new ArrayBuffer(8);
    let descV = new Uint8Array(arrayBuffer);
    descV[0] = 11;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
      descriptorUuid: '00002902-0000-1000-8000-00805F9B34FB',
      descriptorValue: arrayBuffer
    };
    descriptors[0] = descriptor;
    let arrayBufferC = new ArrayBuffer(8);
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
      characteristicValue: arrayBufferC,
      descriptors: descriptors
    };
    try {
      //向服务端发送设置通知此特征值请求。
      let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
      device.setCharacteristicChangeNotification(characteristic, false);
      // 订阅蓝牙低功耗设备的特征值变化事件。
      device.on('BLECharacteristicChange', this.CharacteristicChange);
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 间隔接收的回调函数
  CharacteristicChange(characteristicChangeReq: ble.BLECharacteristic) {
    let serviceUuid: string = characteristicChangeReq.serviceUuid;
    let characteristicUuid: string = characteristicChangeReq.characteristicUuid;
    let value: Uint8Array = new Uint8Array(characteristicChangeReq.characteristicValue);
    console.log('serviceUuid: ' + serviceUuid + ', characteristicUuid: ' + characteristicUuid + 'value: ' + value)
  }
  • BLECharacteristic
    返回值
名称类型可读可写说明
serviceUuidstring特定服务(service)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。
characteristicUuidstring特定服务(characteristic)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。
characteristicValueArrayBuffer特征对应的二进制值。
descriptorsArray特定特征的描述符列表。
propertiesGattProperties特定特征的属性描述。

九、发送数据

  • writeCharacteristicValue
    client端向低功耗蓝牙设备写入特定的特征值。使用Callback异步回调。
writeCharacteristicValue(characteristic: BLECharacteristic, writeType: GattWriteType, callback: AsyncCallback<void>): void

参数:

参数名类型必填说明
characteristicBLECharacteristic蓝牙设备特征对应的二进制值及其它参数。
writeTypeGattWriteType蓝牙设备特征的写入类型。
callbackAsyncCallback回调函数。当写入成功,err为undefined,否则为错误对象。
// 发送数据
  sendData(): void {
    let descriptors: Array<ble.BLEDescriptor> = [];
    let bufferDesc = new ArrayBuffer(8);
    let descV = new Uint8Array(bufferDesc);
    descV[0] = 11;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
      descriptorUuid: '00002903-0000-1000-8000-00805F9B34FB',
      descriptorValue: bufferDesc
    };
    descriptors[0] = descriptor;

    let bufferCCC = new ArrayBuffer(8);
    let cccV = new Uint8Array(bufferCCC);
    cccV[0] = 1;
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
      characteristicValue: bufferCCC,
      descriptors: descriptors
    };
    try {
      let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
      device.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE, this.writeCharacteristicValueCallBack);
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 发送数据回调
  writeCharacteristicValueCallBack(code: BusinessError) {
    if (code != null) {
      return;
    }
    console.info('bluetooth writeCharacteristicValue success');
  }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邹荣乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值