开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器

发布于:2025-08-17 ⋅ 阅读:(20) ⋅ 点赞:(0)

 文章的目的为了记录使用Arkts 进行Harmony app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 Arkts 鸿蒙应用 开发(一)工程文件分析-CSDN博客

开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-CSDN博客

开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-CSDN博客

开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-CSDN博客

开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-CSDN博客

开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-CSDN博客

开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-CSDN博客

开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-CSDN博客

开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-CSDN博客

开源 Arkts 鸿蒙应用 开发(十)通讯--Http-CSDN博客

开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-CSDN博客

开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-CSDN博客

开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-CSDN博客

开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-CSDN博客

开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-CSDN博客

开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-CSDN博客

开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-CSDN博客

开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-CSDN博客

 推荐链接:

开源 java android app 开发(一)开发环境的搭建-CSDN博客

开源 java android app 开发(二)工程文件结构-CSDN博客

开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客

开源 java android app 开发(四)GUI界面重要组件-CSDN博客

开源 java android app 开发(五)文件和数据库存储-CSDN博客

开源 java android app 开发(六)多媒体使用-CSDN博客

开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客

开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客

开源 java android app 开发(九)后台之线程和服务-CSDN博客

开源 java android app 开发(十)广播机制-CSDN博客

开源 java android app 开发(十一)调试、发布-CSDN博客

开源 java android app 开发(十二)封库.aar-CSDN博客

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

本章内容主要演示了蓝牙广播调试应用,主要功能是通过BLE广播发送包含设备ID的心率数据。

1.工程结构

2.源码解析

3.演示效果

4.工程下载网址

一、工程结构:

BluetoothServer.ets - 主界面和业务逻辑

AdvertiseBluetoothViewModel.ets - 蓝牙广播和GATT服务管理

AdvData.ets - 广播数据构造

辅助工具类:ArrayBufferUtils, MathUtils, Logger
 

二、源码解析

2.1  BluetoothServer.ets主界面组件,主要功能:提供UI界面让用户输入ID,管理广播状态,处理权限请求,协调视图模型操作。

函数说明:

toggleAdvertiser() - 切换广播状态

toggleHeartRate() - 开始/停止心率模拟

stringCheck() - 验证用户输入的ID格式



import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { util } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { Logger } from '../utils/Logger';
import advertiseBluetoothViewModel from '../viewmodel/AdvertiseBluetoothViewModel';
import MathUtils from '../utils/MathUtils';
// ble.ts


const MIN_HEART_RATE = 40;
const MAX_HEART_RATE = 200;

const PERMISSION_LIST: Array<Permissions> = [
  'ohos.permission.ACCESS_BLUETOOTH'
];

function reqPermissionFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
  const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  atManager.requestPermissionsFromUser(context, permissions).then((data) => {
    Logger.info(`data:${JSON.stringify(data)}`);
  }).catch((err: BusinessError) => {
    Logger.error(`requestPermissionsFromUser fail: err = ${JSON.stringify(err)}`);
  })
}

@Entry
@Component
export struct BluetoothServer {
  @StorageLink('deviceId') @Watch('onDeviceIdChange') deviceId: string = '';
  @StorageLink('bluetoothEnable') @Watch('onBluetoothEnableChange') bluetoothEnable: boolean = false;
  @State startAdvertiserState: boolean = false;
  @State localName: string = '';
  @State heartRate: number = -1;
  private mIntervalId: number = -1;


  @State myid: string = '' // ID



  private idArray: Uint8Array = new Uint8Array([0x00, 0x00, 0x00,0x0]);


  onDeviceIdChange(): void {
    Logger.info(`onDeviceIdChange: deviced = ${this.deviceId}`);
  }

  onBluetoothEnableChange(): void {
    if (this.bluetoothEnable) {
      this.toggleAdvertiser();
    } else {
      advertiseBluetoothViewModel.stopAdvertiser();
      this.toggleHeartRate(false);
      this.startAdvertiserState = false;
      promptAction.showToast({
        message: $r('app.string.bluetooth_off_Stop_heart_rate_broadcast'),
        duration: 2000
      });
    }
  }

  stringToBytes(val: string): number {
    let that = new util.TextEncoder('utf-8');
    let result = that.encodeInto(val);
    return result?.length ?? 0;
  }

  toggleAdvertiser(): void {
    if (this.startAdvertiserState) {
      advertiseBluetoothViewModel.stopAdvertiser();
      this.toggleHeartRate(false);
      this.startAdvertiserState = false;
      promptAction.showToast({
        message: $r('app.string.ble_heart_rate_broadcast_is_disabled'),
        duration: 2000
      });
    } else {
      let BLEName: string = advertiseBluetoothViewModel.getLocalName();
      if (this.stringToBytes(BLEName) > 22) {
        promptAction.showToast({
          message: $r('app.string.change_bluetooth_name'),
          duration: 2000
        });
        return;
      }
      let ret = advertiseBluetoothViewModel.startAdvertiser(this.idArray);
      if (ret) {
        this.localName = BLEName;
        this.toggleHeartRate(true);
        this.startAdvertiserState = true;
        promptAction.showToast({
          message: $r('app.string.the_ble_heart_rate_broadcast_has_been_enabled'),
          duration: 2000
        });
      }
    }
  }

  toggleHeartRate(open: boolean): void {
    clearInterval(this.mIntervalId);
    if (open) {
      this.mIntervalId = setInterval(() => {
        this.heartRate = MathUtils.getRandomInt(MIN_HEART_RATE, MAX_HEART_RATE);
        if (this.deviceId) {
          advertiseBluetoothViewModel.notifyCharacteristicChanged(this.deviceId, this.heartRate);
        }
      }, 1000)
    }
  }

  aboutToAppear(): void {
    const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    reqPermissionFromUser(PERMISSION_LIST, context);
  }

  aboutToDisappear(): void {
    advertiseBluetoothViewModel.stopAdvertiser();
  }

  stringCheck():boolean {


    // 验证myid
    if (this.myid.length != 8) {
      promptAction.showToast({
        message: '请重新输入,ID为1个字节',
        duration: 2000
      });
      return false;
    }

    return true;
  }

  build() {
    Column() {


      Text('BLE广播调试')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 30 })



      TextInput({ placeholder: 'ID' ,text:'12345678'})
        .width('90%')
        .height(40)
        .margin({ bottom: 10 })
        .onChange((value: string) => {
          this.myid = value
        })



      // 按钮行
      Row() {
        Button('发送')
          .width('45%')
          .height(50)
          .fontSize(18)
          .onClick(() => {

            if(!this.stringCheck()) {
              return;
            }



            for (let i = 0; i < 8; i += 2) {
              let byteStr = this.myid.substr(i, 2); // 截取两位
              this.idArray[i / 2] = parseInt(byteStr, 16); // 按十六进制解析
            }

            this.toggleAdvertiser()
            // 发送按钮点击事件
            console.log('发送按钮被点击')

          })

        Button('停止')
          .width('45%')
          .height(50)
          .fontSize(18)
          .margin({ left: '10%' })
          .onClick(() => {




            this.toggleAdvertiser()
            // 停止按钮点击事件
            console.log('停止按钮被点击')

          })
      }
      .width('90%')
      .justifyContent(FlexAlign.SpaceBetween)




    }
    .width('100%')
    .height('100%')
    .padding(16)
    .backgroundColor('#F5F5F5')
  }
}

2.2  AdvertiseBluetoothViewModel.ets组件实现:蓝牙状态管理,广播的启动和停止,GATT服务管理,连接状态监控

简单函数说明:

startAdvertiser() - 配置并启动BLE广播

stopAdvertiser() - 停止广播

notifyCharacteristicChanged() - 通知客户端特征值变化


 /**
 * 最佳实践:低功耗蓝牙开发实践
 */
// [Start access1]
import { access, ble, connection, constant } from '@kit.ConnectivityKit';
// [End access1]
import { promptAction } from '@kit.ArkUI';
import ArrayBufferUtils from '../utils/ArrayBufferUtils';
import { Logger } from '../utils/Logger';
import { BusinessError } from '@kit.BasicServicesKit';
import advData from '../viewmodel/AdvData';


const uiContext: UIContext | undefined = AppStorage.get('uiContext');


interface CharacteristicModel {
  serviceUuid: string,
  characteristicUuid: string,
  characteristicValue: ArrayBufferLike,
  descriptors: Array<ble.BLEDescriptor>
}

interface NotifyCharacteristicModel {
  serviceUuid: string,
  characteristicUuid: string,
  characteristicValue: ArrayBufferLike,
  confirm: boolean
}

export class AdvertiseBluetoothViewModel {
  private mGattServer: ble.GattServer | undefined;
  private advHandle: number = 0xFF; // 初始的无效值
  private stateChangeFunc = (data: access.BluetoothState): void => {
    if (data === access.BluetoothState.STATE_ON) {
      AppStorage.setOrCreate('bluetoothEnable', true);
    } else if (data === access.BluetoothState.STATE_OFF) {
      AppStorage.setOrCreate('bluetoothEnable', false);
    }
  }
  private connectionStateChangeFunc = (data: ble.BLEConnectionChangeState): void => {
    if (data) {
      if (data.state === constant.ProfileConnectionState.STATE_CONNECTED) {
        let deviceId = data.deviceId;
        AppStorage.setOrCreate('deviceId', deviceId);
      } else if (data.state === constant.ProfileConnectionState.STATE_DISCONNECTED) {
        AppStorage.setOrCreate('deviceId', '');
      }
    }
  }

  isBluetoothEnabled(): boolean {
    const state: access.BluetoothState = access.getState();
    Logger.info(`isBluetoothEnabled: state = ${state}`);
    if (state === access.BluetoothState.STATE_ON || state === access.BluetoothState.STATE_TURNING_ON) {
      return true;
    }
    return false;
  }

  enableBluetooth() {
    try {
      this.onBTStateChange();
      access.enableBluetooth();
    } catch (err) {
      Logger.error(`enableBluetooth: err = ${JSON.stringify(err)}`);
    }
  }

  disableBluetooth() {
    try {
      this.offBTStateChange();
      access.disableBluetooth();
    } catch (err) {
      Logger.error(`disableBluetooth: err = ${JSON.stringify(err)}`);
    }
  }

  getLocalName(): string {
    let localName = '';
    try {
      localName = connection.getLocalName();
    } catch (err) {
      Logger.error(`getLocalName: err = ${JSON.stringify(err)}`);
    }
    return localName;
  }
  // [Start tooth1]
  startAdvertiser(PhoneId: Uint8Array): boolean {
    if (!this.isBluetoothEnabled()) {
      this.enableBluetooth();
      uiContext?.getPromptAction().showToast({
        message: $r('app.string.bluetooth_enabled_please_wait'),
        duration: 2000
      })
      return false;
    }

    /*
    // Create a GattServer instance
    this.mGattServer = ble.createGattServer();
    // [StartExclude tooth1]
    let descriptors: Array<ble.BLEDescriptor> = [];
    const arrayBuffer = ArrayBufferUtils.byteArray2ArrayBuffer([11]);
    const descriptor: ble.BLEDescriptor = {
      serviceUuid: '0000180D-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00002A37-0000-1000-8000-00805F9B34FB',
      descriptorUuid: '00002902-0000-1000-8000-00805F9B34FB',
      descriptorValue: arrayBuffer
    }
    descriptors[0] = descriptor;

    let characteristics: Array<ble.BLECharacteristic> = [];
    const arrayBufferC = ArrayBufferUtils.byteArray2ArrayBuffer([1]);
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: '0000180D-0000-1000-8000-00805F9B34FB',
      characteristicUuid: '00002A37-0000-1000-8000-00805F9B34FB',
      characteristicValue: arrayBufferC,
      descriptors: descriptors
    }
    characteristics[0] = characteristic;
    // [EndExclude tooth1]
    // Define the heart rate beating service
    const service: ble.GattService = {
      serviceUuid: '0000180D-0000-1000-8000-00805F9B34FB',
      isPrimary: true,
      characteristics: characteristics,
      includeServices: []
    }

    try {
      // Add a service
      this.mGattServer.addService(service);
    } catch (err) {
      Logger.error(`addService: err = ${JSON.stringify(err)}`);
    }
    */
    try {
      // The status of the subscription connection service
      this.onConnectStateChange();
      // [StartExclude tooth1]
      let setting: ble.AdvertiseSetting = {
        interval: 160,
        txPower: 1,
        connectable: false
      }
      /*
      let advData: ble.AdvertiseData = {
        serviceUuids: ['0000180D-0000-1000-8000-00805F9B34FB'],
        manufactureData: [],
        serviceData: [],
        includeDeviceName: true
      }
      */
      let recv = advData.CreateData(PhoneId);
      let manufactureValueBuffer: Uint8Array = new Uint8Array([
        0x0,0x0,0x0,0x0,0x0,
        0x0,0x0,0x0,0x0,0x0,
        0x0,0x0,0x0,0x0,0x0,
        0x0,0x0,0x0,0x0,0x0,
        0x0,0x0,0x0
      ]);//比协议多1个字节的,设置不可连接后,长度不够

      for (let i = 0; i < manufactureValueBuffer.length-1; i++) {
        manufactureValueBuffer[i+1] = recv[i];
      }

      let manufactureDataUnit: ble.ManufactureData = {
        manufactureId: 0x0006,
        manufactureValue: manufactureValueBuffer.buffer
      };
      let advPacket: ble.AdvertiseData = {
        serviceUuids: [],
        manufactureData: [manufactureDataUnit],
        serviceData: [],
        includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节,使得广播启动失败
      };


      let advResponse: ble.AdvertiseData = {
        serviceUuids: ['0000180D-0000-1000-8000-00805F9B34FB'],
        manufactureData: [],
        serviceData: []
      }

      // [EndExclude tooth1]
      ble.startAdvertising(setting, advPacket, advResponse);
      return true;


    } catch (err) {
      Logger.error(`startAdvertiser: err = ${JSON.stringify(err)}`);
    }
    return false
  }
  // [End tooth1]
  stopAdvertiser() {
    ble.stopAdvertising();
    /*
    if (this.mGattServer)
    {
      try {
        this.offConnectStateChange();
        ble.stopAdvertising();
        this.disableBluetooth();
      } catch (err) {
        Logger.error(`stopAdvertiser: err = ${JSON.stringify(err)}`);
      }
    }
    * */
  }
  // [Start not_char]
  notifyCharacteristicChanged(deviceId: string, heartRate: number) {
    if (!deviceId) {
      return;
    }
    if (this.mGattServer) {
      try {
        let descriptors: Array<ble.BLEDescriptor> = [];
        let arrayBuffer = ArrayBufferUtils.byteArray2ArrayBuffer([11]);
        let descriptor: ble.BLEDescriptor = {
          serviceUuid: '0000180D-0000-1000-8000-00805F9B34FB',
          characteristicUuid: '00002A37-0000-1000-8000-00805F9B34FB',
          descriptorUuid: '00002902-0000-1000-8000-00805F9B34FB',
          descriptorValue: arrayBuffer
        }
        descriptors[0] = descriptor;

        let arrayBufferC = ArrayBufferUtils.byteArray2ArrayBuffer([0x00, heartRate]);
        let characteristic: CharacteristicModel = {
          serviceUuid: '0000180D-0000-1000-8000-00805F9B34FB',
          characteristicUuid: '00002A37-0000-1000-8000-00805F9B34FB',
          characteristicValue: arrayBufferC,
          descriptors: descriptors
        }

        let notifyCharacteristic: NotifyCharacteristicModel = {
          serviceUuid: '0000180D-0000-1000-8000-00805F9B34FB',
          characteristicUuid: '00002A37-0000-1000-8000-00805F9B34FB',
          characteristicValue: characteristic.characteristicValue,
          confirm: false
        }

        this.mGattServer.notifyCharacteristicChanged(deviceId, notifyCharacteristic, (err: BusinessError) => {
          if (err) {
            Logger.error(`notifyCharacteristicChanged callback failed: err = ${JSON.stringify(err)}`);
          } else {
            Logger.info('notifyCharacteristicChanged callback success')
          }
        })
      } catch (err) {
        Logger.error(`notifyCharacteristicChanged: err = ${JSON.stringify(err)}`);
      }
    }
  }
  // [End not_char]
  // [Start on_bts]
  private onBTStateChange() {
    try {
      access.on('stateChange', (data: access.BluetoothState) => {
        if (data === access.BluetoothState.STATE_ON) {
          AppStorage.setOrCreate('bluetoothEnable', true);
        } else if (data === access.BluetoothState.STATE_OFF) {
          AppStorage.setOrCreate('bluetoothEnable', false);
        }
      })
    } catch (err) {
      Logger.error(`onBTSateChange: err = ${JSON.stringify(err)}`);
    }
  }
  // [End on_bts]
  private offBTStateChange() {
    try {
      access.off('stateChange');
    } catch (err) {
      Logger.error(`offBTSateChange: err = ${JSON.stringify(err)}`);
    }
  }
  // [Start change_State]
  private onConnectStateChange() {
    if (!this.mGattServer) {
      return;
    }

    try {
      this.mGattServer.on('connectionStateChange', (data: ble.BLEConnectionChangeState) => {
        if (data) {
          if (data.state === constant.ProfileConnectionState.STATE_CONNECTED) {
            let deviceId = data.deviceId;
            AppStorage.setOrCreate('deviceId', deviceId);
          } else if (data.state === constant.ProfileConnectionState.STATE_DISCONNECTED) {
            AppStorage.setOrCreate('deviceId', '');
            this.stopAdvertiser();
          }
        }
      })
    } catch (err) {
      Logger.error(`connectInner: err = ${JSON.stringify(err)}`);
    }
  }
  // [End change_State]
  private offConnectStateChange() {
    if (!this.mGattServer) {
      return;
    }

    try {
      this.mGattServer.off('connectionStateChange');
    } catch (err) {
      Logger.error(`offConnectStateChange: err = ${JSON.stringify(err)}`);
    }
  }
}

let advertiseBluetoothViewModel = new AdvertiseBluetoothViewModel();

export default advertiseBluetoothViewModel as AdvertiseBluetoothViewModel;

2.3  AdvData.ets文件负责构造广播数据包:

预定义了一个22字节的数据模板

SetPhoneId()方法将设备ID嵌入到指定位置

CreateData()生成最终的广播数据

// AdvData.ts
export class AdvData {
  private Fanal_DATA: Uint8Array = new Uint8Array([
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0
  ]);


  private SetPhoneId(bytes: Uint8Array): void {
    for (let i = 0; i < 4; i++) {
      this.Fanal_DATA[10 + i] = bytes[i];
    }
  }

  private  toHexString(byteArray: Uint8Array): string {
    if (byteArray === null || byteArray.length < 1) return "";
    let hexString = "";
    for (const byte of byteArray) {
      hexString += " ";
      if ((byte & 0xff) < 0x10) {
        hexString += "0";
      }
      hexString += byte.toString(16);
    }
    return hexString.toLowerCase();
  }

  public CreateData(PhoneId: Uint8Array,): Uint8Array {

    this.SetPhoneId(PhoneId);


    let  mystr ="";
    mystr = this.toHexString(this.Fanal_DATA);
    console.log(`Fanal_DATA_1: ${mystr}`);

    return this.Fanal_DATA;
  }





}

let advData = new AdvData();

export default advData as AdvData;

2.4  MathUtils.ets
 

export default class MathUtils {
  static getRandomInt(min: number, max: number): number {
    return Math.floor(Math.random() * (max - min + 1) + min);
  }
}

2.5  ArrayBufferUtils.ets



export default class ArrayBufferUtils {
  public static byteArray2ArrayBuffer(byteArr: Array<number>): ArrayBufferLike {
    return new Uint8Array(byteArr).buffer;
  }
  public static arrayBuffer2ByteArray(arrayBuffer: ArrayBuffer): Array<number> {
    return [...new Uint8Array(arrayBuffer)];
  }
}

2.6   Logger.ets



import { hilog } from '@kit.PerformanceAnalysisKit';

export class Logger {
  private static domain: number = 0xFF00;
  private static prefix: string = 'BluetoothLowEnergy';
  private static format: string = '%{public}s';

  static debug(...args: string[]): void {
    hilog.debug(Logger.domain, Logger.prefix, Logger.format, args);
  }

  static info(...args: string[]): void {
    hilog.info(Logger.domain, Logger.prefix, Logger.format, args);
  }

  static warn(...args: string[]): void {
    hilog.warn(Logger.domain, Logger.prefix, Logger.format, args);
  }

  static error(...args: string[]): void {
    hilog.error(Logger.domain, Logger.prefix, Logger.format, args);
  }
}

2.7  module.json5权限文件

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ],
    "extensionAbilities": [
      {
        "name": "EntryBackupAbility",
        "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
        "type": "backup",
        "exported": false,
        "metadata": [
          {
            "name": "ohos.extension.backup",
            "resource": "$profile:backup_config"
          }
        ],
      }
    ],
    "requestPermissions": [
      {
        "name": 'ohos.permission.ACCESS_BLUETOOTH',
        "reason": '$string:reason',
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always"
        }
      }
    ]
  }
}

三、演示效果

使用方法:

用户输入ID -> 转换为字节数组 -> 嵌入广播数据 -> 开始广播-> 通过GATT通知发送给客户端

华为 HarmonyNextOS 系统APP界面

使用安卓手机nrf Connect的App来检查数据,名字无法查看,rssi在30左右,点开可以看到数据

四、工程下载网址:https://download.csdn.net/download/ajassi2000/91685808?spm=1001.2014.3001.5503


网站公告

今日签到

点亮在社区的每一天
去签到