• Lv0
    粉丝1

积分23 / 贡献0

提问0答案被采纳0文章3

[经验分享] 开发一个BLE低功耗蓝牙调试助手(二)实现BLE广播 原创

hellokun 显示全部楼层 发表于 2024-9-18 17:38:45

1 简介

上一篇文章(开发一个BLE低功耗蓝牙调试助手(一)连接蓝牙服务设备-华为开发者论坛)介绍了如何实现调试助手连接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

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

2 环境搭建

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

3 代码结构解读

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

. 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属性(可自定义控键和触发事件)实现广播和扫描页面导航:触发不同的按钮时设定显示状态。

 // 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整体的布局框架实现如下:

// 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)。

// 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)。
    • 广播是否可连接:设置能否被客户端连接
 //设置广播发送参数,见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

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

  • 1.设置厂商参数,并且在Advertising.ets 页面中能使用提供的接口该配置
// 初始化参数,需要注意使用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,同样提供了修改接口。
// 默认参数
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>){...}

  • 3.广播数据构造,与1、2设置的厂商和服务参数相关,修改1、2即可。
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设置的厂商和服务参数相关。
    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

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

image.png

5.2 广播管理

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

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

    enum AdvertisingState {
        STARTED = 1, // 开启广播
        ENABLED = 2, // 继续广播
        DISABLED = 3,// 暂停广播
        STOPPED = 4  // 停止广播
    }
 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)开启广播。
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)实现
  public async disableAdvertising() {
    //构造临时停止广播参数
    let advertisingDisableParams: ble.AdvertisingDisableParams = {
      advertisingId: this.advHandle // 使用首次启动广播时获取到的广播标识ID
    }
    try {
      await ble.disableAdvertising(advertisingDisableParams);
    } catch (err) {
     ....
    }
  }
  • 3、继续广播,临时暂停广播后继续广播调用ble.enableAdvertising(advertisingEnableParams)
public async enableAdvertising(enableDuration: number) {
    let advertisingEnableParams: ble.AdvertisingEnableParams = {
      advertisingId: this.advHandle, // 使用首次启动广播时获取到的广播标识ID
      duration: enableDuration
    }
    try {
      await ble.enableAdvertising(advertisingEnableParams);  // 再次启动
    } catch (err) {
        ...
    }
  • 4、停止广播,与临时暂停广播不同,直接关闭广播会释放资源,不能够继续使用首次广播的advHandle进行再次广播,需要重新开启新的广播流程。
  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,欢迎大家提意见,等待星闪API开放继续添加其调试功能!

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部