OpenHarmony开发者论坛

标题: 开发一个BLE低功耗蓝牙调试助手(二)实现BLE广播 [打印本页]

作者: hellokun    时间: 2024-9-18 17:38
标题: 开发一个BLE低功耗蓝牙调试助手(二)实现BLE广播
[md]# 1 简介

上一篇文章([开发一个BLE低功耗蓝牙调试助手(一)连接蓝牙服务设备-华为开发者论坛](https://developer.huawei.com/con ... 0109140870620153026))介绍了如何实现调试助手连接BLE服务端,本篇将介绍如何使用OpenHarmony原生能力实现BLE广播,以及介绍BLE调试助手的页面和整体功能优化。

通过本篇文章你将学到:

- 如何OH中实现BLE广播。
- 如何在OH中连接BEL设备进行数据交互。
- 学习Navigation、List、Slider等常用容器和组件的用法。
- 学习Consum、Provide状态管理。

目前BLE调试助手的功能效果为:

- **支持扫描BLE设备**
  - 能扫描周围的BLE设备,列出设备名称以及MAC地址。
- **支持连接、订阅、发送BLE消息**:
  - 在扫描列表中选择期望连接的设备,点击连接按钮即可与BLE设备建立连接。
  - **支持设置需要订阅的BLE服务**(默认9011)**及其特征值**(默认9012)。
- **支持设置广播参数**
  - 能扫描周围的BLE设备,列出设备名称以及MAC地址。
- **支持扫描响应、广播数据**:
  - 能对外广播数据,BLE客户端能扫描到调试助手配置的广播信息和服务。
    ![1 1.gif](https://dl-harmonyos.51cto.com/i ... 5e5973999f824f0.gif)

目前APP可在客户端与服务端之间切换和配置,功能基本完善,APP效果如下:

# 2 环境搭建

我们首先需要完成OH应用开发环境搭建,可参考([开发一个BLE低功耗蓝牙调试助手(一)连接蓝牙服务设备-华为开发者论坛](https://developer.huawei.com/con ... 0109140870620153026))中的第二章进行操作。

# 3 代码结构解读

本篇文档只对**新增功能的核心代码**进行讲解,对于作为BLE客户端的实现见上一篇文章。

```c
. entry/src
|-- common
|   |-- CommonConstants.ets
|   |-- Logger.ets
|   `-- PermissionUtil.ets
|-- entryability
|   `-- EntryAbility.ets
|-- pages
|   |-- Advertising.ets  // BLE广播管理子页面
|   |-- DeviceDetails.ets// BLE客户端管理子页面
|   |-- Index.ets        // 主页面
|   `-- ScanDevices.ets  // 设备扫描子页面
`-- servers
    |-- BLEAdvertising.ets      // BLE 广播接口
    |-- BLEGattClientManager.ets// BLE 通用属性开发接口
    `-- BLEScanManager.ets                 // BLE 扫描接口
```

# 4 构建应用主界面

调试助手入口页面使用Navigation组件实现布局,将BLE客户端管理页面放置到List Item点击后的层级,使用Navigation自带的toolbarConfiguration属性(可自定义控键和触发事件)实现广播和扫描页面导航:触发不同的按钮时设定显示状态。

```js
// Index.ets
@State ShowBLEClient:boolean = true
@State BLEClientTool: ToolbarItem = {'value': "BLE客户端", 'icon': $r('app.media.ble_client'), 'action': ()=> {
    this.ShowBLEClient = true
  }}
  @State BLEAdvertisingTool: ToolbarItem = {'value': "BLE广播", 'icon': $r('app.media.ble_adv'), 'action': ()=> {
    this.ShowBLEClient = false
  }}
```

Navigation组件其他常用的属性有: .title("设置标题")   .mode(可自动分栏、单页) .navDestination(可指定) ,可看出使用Navigation组件开发页面更方便,页面管理和导航一举两得。Index整体的布局框架实现如下:

```js
// Index.ets
@Entry
@Component
struct Index {
  build() {
    Column({space:20}) {
      Navigation(this.pageInfos) {
        if(this.ShowBLEClient){
          BleScan() // 扫描管理子页面
        }else {
          BleAdvertising() // 广播管理子页面
        }
      }
      .title("BLE调试助手")
      .mode(NavigationMode.Auto)
      .navDestination(this.PageMap) // 工具栏实现切换广播和扫描
      .toolbarConfiguration([this.BLEClientTool, this.BLEAdvertisingTool])
    }.justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}
```

在Index.ets页面中使用provide提供导航页面栈NavPathStack,在ScanDevices.ets设备扫描子页面使用Consume绑定Navigation的NavPathStack,点击扫描设备后跳转到BLE 客户端管理页面(DeviceDetails.ets)。

```js
// Index.ets
@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()
  @Builder
  PageMap(name: string) {
    if (name === "DeviceDetails") {
      DeviceDetails()
    }
  }

// ScanDevices.ets  // 设备扫描子页面
@Consume ('pageInfos') pageInfos: NavPathStack
      ListItem() {
                ...扫描到的Device...
      }
      .onClick(() => { // 点击对应设备,高亮并获取设备ID与设备名,用于后续连接
        this.deviceId = data.deviceId;
        this.deviceName = data.deviceName;
        this.clickNum = index;
        this.pageInfos.pushPath({ name: "DeviceDetails"}) // 跳转到BLE客户端管理页面
      })
```

# 5 BLE功能开发

## 5.1 设置广播参数

在文件BLEAdvertising.ets 中详细介绍了BLE 设备广播的实现,手机广播之前需要了解BLE广播主要的参数,一个BLE广播主要包含的信息整理如下:

- 广播设备的名称(Name,显示名字时不能超过31字节)
- 设备厂商名(manufactureName)与ID(manufactureId)
- 广播数据advData
- 广播服务UUID、和广播数值数据
- 广播发送的参数,有三个:
  - 广播间隔:间隔越长越不容易被扫描到,160个slot表示100ms,最大值设置16384个slot,
  - 广播功率:发送功率  默认-7 推荐值:高档(1),中档(-7),低档(-15)。
  - 广播是否可连接:设置能否被客户端连接

```js
//设置广播发送参数,见Advertising.ets
@State setting_: ble.AdvertiseSetting = {
    interval: 160,
    txPower: -7,  
    connectable: true
    };
// 可使用Slider组件设置有限定范围的变量,如使用Slider设置txPower
Slider({value:this.txPower,min:-15,max:1,step:1,style:SliderStyle.InSet})
            .onChange((value:number)=>{
              this.setting_.txPower = value
              bleAdvertisingManager.SetAdvertiseSetting(this.setting_)
            }).width('60%')
```

![image20240819021318630.png](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_399,h_124)

除设置广播发送的参数外,下面看如何构造广播数据ble.AdvertisingParams。

- 1.设置厂商参数,并且在Advertising.ets 页面中能使用提供的接口该配置

```js
// 初始化参数,需要注意使用Uint8Array
manufactureValueBuffer = new Uint8Array(4);
this.manufactureValueBuffer[0] = 1;
this.manufactureValueBuffer[1] = 2;
this.manufactureValueBuffer[2] = 3;
this.manufactureValueBuffer[3] = 4;
manufactureDataUnit: ble.ManufactureData = {
    manufactureId: 4567,
    manufactureValue: this.manufactureValueBuffer.buffer
};
// 提供的设置接口:输入厂商ID信息ManuID与数值manufactureValueBuffer
  SetManufactureValueBuffer(ManuID:number,ManuBuff:Array<number>){
    let manufactureValueBuffer = new Uint8Array(4);
    manufactureValueBuffer[0] = ManuBuff[0];
        ...
    this.manufactureDataUnit = manufactureDataUnit
  }
```

- 2.构建服务参数,包括服义UUID和serviceValue,同样提供了修改接口。

```js
// 默认参数
serviceValueBuffer = new Uint8Array(4);
this.serviceValueBuffer[0] = 5;
this.serviceValueBuffer[1] = 6;
this.serviceValueBuffer[2] = 7;
this.serviceValueBuffer[3] = 8;
let serviceDataUnit: ble.ServiceData = {
      serviceUuid: "00001888-0000-1000-8000-00805f9b34fb",
      serviceValue: this.serviceValueBuffer.buffer
    };
// 提供的修改接口,输入广播的服务UUID与值ServerValBuff
SetServiceValueBuffer(ServerUUID:string,ServerValBuff:Array<number>){...}
```

![](https://communityfile-drcn.op.hi ... 34020:2800)

- 3.广播数据构造,与1、2设置的厂商和服务参数相关,修改1、2即可。

```js
let advData: ble.AdvertiseData = {
      serviceUuids: ["0000"+ServerUUID+"-0000-1000-8000-00805f9b34fb"],
      manufactureData: [this.manufactureDataUnit],
      serviceData: [this.serviceDataUnit],
      includeDeviceName: true // 表示是否携带设备名,可选参数。注意带上设备名时广播包长度不能超出31个字节。
    };
    this.advData = advData

```

- 4.广播时响应的参数构造,同样与1、2设置的厂商和服务参数相关。

```js
    let advResponse: ble.AdvertiseData = {
      serviceUuids: ["0000"+ServerUUID+"-0000-1000-8000-00805f9b34fb"],
      manufactureData: [this.manufactureDataUnit],
      serviceData: [this.serviceDataUnit]
    };
    this.advResponse  = advResponse
```

- 5.使用1、2、3、4步的参数构造广播启动需要的完整参数AdvertisingParams

  ```js
   let advertisingParams: ble.AdvertisingParams = {
        advertisingSettings: setting,
        advertisingData: advData,
        advertisingResponse: advResponse,
        duration: 0 // 可选参数,若大于0,则广播发送一段时间后,则会临时停止,可重新启动发送
      }
  ```

![image.png](
https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_567,h_747)

## 5.2 广播管理

完成广播参数构造后,下一步需要管理广播,主要有以下步骤:

- 0.监听广播变化,可随时获取广播状态,可能的状态有

  ```js
  enum AdvertisingState {
      STARTED = 1, // 开启广播
      ENABLED = 2, // 继续广播
      DISABLED = 3,// 暂停广播
      STOPPED = 4  // 停止广播
  }
  ```

```js
public onAdvertisingStateChange() {
    try {
      ble.on('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => {
        console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data));
      });
    } catch (err) {
          ....
    }
  }
```

- 1.开启广播,使用用户设置的参数构造广播参数Param,然后使用ble.startAdvertising(Param)开启广播。

```js
Button("开启广播").onClick(()=>{
        bleAdvertisingManager.startAdvertising(bleAdvertisingManager.advertisingParams)
  })

public async startAdvertising(Param:ble.AdvertisingParams) {
    // 首次启动广播,且获取所启动广播的标识ID
    try {
      this.onAdvertisingStateChange();
      this.advHandle = await ble.startAdvertising(Param);
    } catch (err) {
     ...
    }
  }
```

开启广播后,可以使用BLE客户端设备扫描,可以看到广播的信息,可以核对是否与5.1节配置的参数一致。

- 2.暂停广播,当临时关闭广播时可以使用 ble.disableAdvertising(advertisingDisableParams)实现

```js
  public async disableAdvertising() {
    //构造临时停止广播参数
    let advertisingDisableParams: ble.AdvertisingDisableParams = {
      advertisingId: this.advHandle // 使用首次启动广播时获取到的广播标识ID
    }
    try {
      await ble.disableAdvertising(advertisingDisableParams);
    } catch (err) {
     ....
    }
  }
```

- 3、继续广播,临时暂停广播后继续广播调用ble.enableAdvertising(advertisingEnableParams)

```js
public async enableAdvertising(enableDuration: number) {
    let advertisingEnableParams: ble.AdvertisingEnableParams = {
      advertisingId: this.advHandle, // 使用首次启动广播时获取到的广播标识ID
      duration: enableDuration
    }
    try {
      await ble.enableAdvertising(advertisingEnableParams);  // 再次启动
    } catch (err) {
        ...
    }
```

- 4、停止广播,与临时暂停广播不同,直接关闭广播会释放资源,不能够继续使用首次广播的advHandle进行再次广播,需要重新开启新的广播流程。

```js
  public async stopAdvertising() {
    try {
      await ble.stopAdvertising(this.advHandle);
      ble.off('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => {
        console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data));
      });
    } catch (err) {
   ....
    }
  }
```

# 6.总结

至此BLE调试助手APP开发基本完成,实现了BLE广播与BLE服务端连接交互。感兴趣可访问仓库地址[HelloKun - Gitee.com](https://gitee.com/ckunkun),欢迎大家提意见,等待星闪API开放继续添加其调试功能!
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5