HarmonyOS 鸿蒙Next低功耗蓝牙开发

HarmonyOS 鸿蒙Next低功耗蓝牙开发 手表支不支持低功耗蓝牙开发

8 回复
  1. 手表支持低功耗蓝牙开发:具体的机型参考机型列表HarmonyOS 5.1 支持机型

  2. 低功耗蓝牙开发连接和传输数据代码示例: 客户端:

import { ble, constant } from '[@kit](/user/kit).ConnectivityKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

const TAG: string = 'GattClientManager';

export class GattClientManager {
  device: string = '';
  gattClient: ble.GattClientDevice | undefined = undefined;
  connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;
  myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';
  myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';
  // 标准协议描述符Client Characteristic Configuration,用于特征值变化通知或指示
  myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB';
  // 自定义描述符
  mySecondDescriptorUuid: string = '00008888-0000-1000-8000-00805F9B34FB';

  myService: ble.GattService | undefined = undefined;
  myCharacteristic: ble.BLECharacteristic | undefined = undefined;
  myFirstDescriptor: ble.BLEDescriptor | undefined = undefined;
  mySecondDescriptor: ble.BLEDescriptor | undefined = undefined;

  foundService: boolean = false;
  foundChar: boolean = false;
  foundFirstDes: boolean = false;
  foundSecondDes: boolean = false;

  // 构造BLEDescriptor
  private initDescriptor(des: string, value: ArrayBuffer): ble.BLEDescriptor {
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: this.myServiceUuid,
      characteristicUuid: this.myCharacteristicUuid,
      descriptorUuid: des,
      descriptorValue: value
    };
    return descriptor;
  }

  // 构造BLECharacteristic
  private initCharacteristic(isWrite: boolean): ble.BLECharacteristic {
    let descriptors: Array<ble.BLEDescriptor> = [];
    let charBuffer = new ArrayBuffer(2);
    if (isWrite) {
      let charValue = new Uint8Array(charBuffer);
      charValue[0] = 21;
      charValue[1] = 22;
    }
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: this.myServiceUuid,
      characteristicUuid: this.myCharacteristicUuid,
      characteristicValue: charBuffer,
      descriptors: descriptors
    };
    return characteristic;
  }

  private logCharacteristic(char: ble.BLECharacteristic) {
    let message = 'logCharacteristic uuid:' + char.characteristicUuid + ', value: ';
    let value = new Uint8Array(char.characteristicValue);
    message += 'logCharacteristic value: ';
    for (let i = 0; i < char.characteristicValue.byteLength; i++) {
      message += value[i] + ' ';
    }
    console.info(TAG, message);
  }

  private logDescriptor(des: ble.BLEDescriptor) {
    let message = 'logDescriptor uuid:' + des.descriptorUuid + ', value: ';
    let value = new Uint8Array(des.descriptorValue);
    message += 'logDescriptor value: ';
    for (let i = 0; i < des.descriptorValue.byteLength; i++) {
      message += value[i] + ' ';
    }
    console.info(TAG, message);
  }

  private checkService(services: Array<ble.GattService>) {
    for (let i = 0; i < services.length; i++) {
      if (services[i].serviceUuid != this.myServiceUuid) {
        continue;
      }
      this.myService = services[i];
      this.foundService = true;
      for (let j = 0; j < services[i].characteristics.length; j++) {
        if (services[i].characteristics[j].characteristicUuid != this.myCharacteristicUuid) {
          continue;
        }
        this.logCharacteristic(services[i].characteristics[j]);
        this.myCharacteristic = services[i].characteristics[j];
        this.foundChar = true;
        for (let k = 0; k < services[i].characteristics[j].descriptors.length; k++) {
          if (services[i].characteristics[j].descriptors[k].descriptorUuid == this.myFirstDescriptorUuid) {
            this.myFirstDescriptor= services[i].characteristics[j].descriptors[k];
            this.foundFirstDes = true;
            continue;
          }
          if (services[i].characteristics[j].descriptors[k].descriptorUuid == this.mySecondDescriptorUuid) {
            this.mySecondDescriptor = services[i].characteristics[j].descriptors[k];
            this.foundSecondDes = true;
            continue;
          }
        }
      }
    }
    console.info(TAG, 'foundService: ' + this.foundService + ', foundChar: ' + this.foundChar +
      ', foundFirDes: ' + this.foundFirstDes + ', foundSecDes: ' + this.foundedSecondDes);
  }

  // 1. 定义连接状态变化回调函数
  onGattClientStateChange = (stateInfo: ble.BLEConnectionChangeState) => {
    let state = '';
    switch (stateInfo.state) {
      case 0:
        state = 'DISCONNECTED';
        break;
      case 1:
        state = 'CONNECTING';
        break;
      case 2:
        state = 'CONNECTED';
        break;
      case 3:
        state = 'DISCONNECTING';
        break;
      default:
        state = 'undefined';
        break;
    }
    console.info(TAG, 'onGattClientStateChange: device=' + stateInfo.deviceId + ', state=' + state);
    if (stateInfo.deviceId == this.device) {
      this.connectState = stateInfo.state;
    }
  };

  // 2. client端主动连接时调用
  public startConnect(peerDevice: string) { // 对端设备一般通过ble查找设备获取到
    if (this.connectState != constant.ProfileConnectionState.STATE_DISCONNECTED) {
      console.error(TAG, 'startConnect failed');
      return;
    }
    console.info(TAG, 'startConnect ' + peerDevice);
    this.device = peerDevice;
    // 2.1 使用device构造gattClient,后续的交互都需要使用该实例
    this.gattClient = ble.createGattClientDevice(peerDevice);
    try {
      // 2.2 订阅连接状态
      this.gattClient.on('BLEConnectionStateChange', this.onGattClientStateChange);

      // 2.3 发起连接
      this.gattClient.connect();
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 3. client端连接成功后,需要进行服务发现
  public async discoverServices() {
    if (!this.gattClient) {
      console.error(TAG, 'gattClient does not exist');
      return;
    }
    console.info(TAG, 'discoverServices');
    try {
      let serverService = await this.gattClient.getServices();
      this.checkService(serverService); // 要确保server端的服务内容有业务所需要的服务
      if (typeof this.myService !== 'undefined') {
        console.info(TAG, 'Service: ' + JSON.stringify(this.myService));
      }
      if (typeof this.myCharacteristic !== 'undefined') {
        this.logCharacteristic(this.myCharacteristic);
      }
      if (typeof this.myFirstDescriptor !== 'undefined') {
        this.logDescriptor(this.myFirstDescriptor);
      }
      if (typeof this.mySecondDescriptor !== 'undefined') {
        this.logDescriptor(this.mySecondDescriptor);
      }
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 4. 在确保拿到了server端的服务结果后,读取server端特定服务的特征值时调用
  public readCharacteristicValue() {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }
    if (!this.foundChar) { // 要确保server端有对应的characteristic
      console.error(TAG, 'server characteristic does not exist');
      return;
    }

    let characteristic = this.initCharacteristic(false);
    console.info(TAG, 'readCharacteristicValue');
    try {
      this.gattClient.readCharacteristicValue(characteristic).then((outData: ble.BLECharacteristic) => {
        this.myCharacteristic = outData;
        this.logCharacteristic(this.myCharacteristic);
      });
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 5. 在确保拿到了server端的服务结果后,写入server端特定服务的特征值时调用
  public writeCharacteristicValue() {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }
    if (!this.foundChar) { // 要确保server端有对应的characteristic
      console.error(TAG, 'server characteristic does not exist');
      return;
    }

    let characteristic = this.initCharacteristic(true);
    console.info(TAG, 'writeCharacteristicValue');
    try {
      this.gattClient.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE, (err) => {
        if (err) {
          console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
          return;
        }
        console.info(TAG, 'writeCharacteristicValue success');
      });
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 6. 在确保拿到了server端的服务结果后,读取server端特定服务的描述符时调用
  public readDescriptorValue() {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }
    if (!this.foundSecondDes) { // 要确保server端有对应的descriptor
      console.error(TAG, 'server descriptor does not exist');
      return;
    }

    let descBuffer = new ArrayBuffer(0);
    let descriptor = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);
    console.info(TAG, 'readDescriptorValue');
    try {
      this.gattClient.readDescriptorValue(descriptor).then((outData: ble.BLEDescriptor) => {
        this.logDescriptor(outData);
      });
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 7. 在确保拿到了server端的服务结果后,写入server端特定服务的描述符时调用
  public writeDescriptorValue() {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }
    if (!this.foundSecondDes) { // 要确保server端有对应的descriptor
      console.error(TAG, 'server descriptor does not exist');
      return;
    }

    let descBuffer = new ArrayBuffer(2);
    let descValue = new Uint8Array(descBuffer);
    descValue[0] = 41;
    descValue[1] = 42;
    let descriptor = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);
    console.info(TAG, 'writeDescriptorValue');
    try {
      this.gattClient.writeDescriptorValue(descriptor, (err) => {
        if (err) {
          console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
          return;
        }
        console.info(TAG, 'writeDescriptorValue success');
      });
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 8. 定义特征值变化回调函数
  onCharacteristicChange = (char: ble.BLECharacteristic) => {
    console.info(TAG, 'onCharacteristicChange: uuid: ' + char.characteristicUuid + ', value: ' + JSON.stringify(new Uint8Array(char.characteristicValue)));
    this.myCharacteristic = char;
    this.logCharacteristic(this.myCharacteristic);
  }

  // 9. 使能或禁用接收服务端端特征值内容变更通知的能力时调用,一般通知或者指示,二选一
  public Notify(enable: boolean) {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }

    if (!this.foundFirstDes) { // 要确保server端有对应的client configuration descriptor
      console.error(TAG, 'server client configuration descriptor does not exist');
      return;
    }

    console.info(TAG, 'Notify ' + this.device + ' enable: ' + enable);
    try {
      // 订阅特征值变化
      this.gattClient.on('BLECharacteristicChange', this.onCharacteristicChange);
      // 设置特征值变化通知能力,enable: true表示启用,false表示禁用
      this.gattClient.setCharacteristicChangeNotification(this.myCharacteristic, enable, (err: BusinessError) => {
        if (err) {
          console.error('setCharacteristicChangeNotification callback failed');
        } else {
          console.info('setCharacteristicChangeNotification callback successful');
        }
      });
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 10. 使能或禁用接收服务端端特征值内容变更指示的能力时调用,一般通知或者指示,二选一
  public Indicate(enable: boolean) {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }

    if (!this.foundFirstDes) { // 要确保server端有对应的client configuration descriptor
      console.error(TAG, 'server client configuration descriptor does not exist');
      return;
    }

    console.info(TAG, 'Indicate ' + this.device + ' enable: ' + enable);
    try {
      // 订阅特征值变化
      this.gattClient.on('BLECharacteristicChange', this.onCharacteristicChange);
      // 设置特征值变化指示能力,enable: true表示启用,false表示禁用
      this.gattClient.setCharacteristicChangeIndication(this.myCharacteristic, enable, (err: BusinessError) => {
        if (err) {
          console.error('setCharacteristicChangeIndication callback failed');
        } else {
          console.info('setCharacteristicChangeIndication callback successful');
        }
      });
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 11.client端主动断开时调用
  public stopConnect() {
    if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      console.error(TAG, 'gattClient does not exist or state not connected');
      return;
    }

    console.info(TAG, 'stopConnect ' + this.device);
    try {
      this.gattClient.disconnect(); // 11.1 断开连接
      this.gattClient.off('BLEConnectionStateChange', this.onGattClientStateChange); // 11.2 取消订阅连接状态
      this.gattClient.off('BLECharacteristicChange', this.onCharacteristicChange); // 11.3 取消订阅特征值变化
      this.gattClient.close() // 11.4 如果应用不再使用此gattClient,则需要close
    } catch (err) {
      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }
}

let gattClientManager = new GattClientManager();
export default gattClientManager as GattClientManager;

服务端:

import { ble, constant } from '[@kit](/user/kit).ConnectivityKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

const TAG: string = 'GattServerManager';

export class GattServerManager {
  device = '';
  gattServer: ble.GattServer | undefined = undefined;
  connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;
  myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';
  myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';
  // 标准协议描述符Client Characteristic Configuration,用于特征值变化通知或指示
  myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34

更多关于HarmonyOS 鸿蒙Next低功耗蓝牙开发的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


HarmonyOS智能手表支持低功耗蓝牙(BLE)开发

HarmonyOS通过@kit.ConnectivityKit模块提供完整的BLE API,支持BLE 5.3协议及扩展广播功能,可满足智能手表与健康设备(如心率带、体脂秤)的数据交互需求。

文档参考地址

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/connectivity-api

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

智能手表完全支持低功耗蓝牙(BLE)开发

@ohos.bluetooth.ble (蓝牙ble模块) 提供了基于低功耗蓝牙(Bluetooth Low Energy)技术的蓝牙能力,支持发起BLE扫描、发送BLE广播报文、以及基于通用属性协议(Generic Attribute Profile,GATT)的连接和传输数据。

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-bluetooth-ble

Watch5系列是支持的。当前Watch5系列已支持ArkTS开发,并且在低功耗蓝牙文档的支持设备中有Wearable设备。

鸿蒙Next低功耗蓝牙开发基于HarmonyOS分布式能力,使用@ohos.bluetooth低功耗API。主要接口包括GattClient和GattServer,支持设备扫描、连接、服务发现及数据读写。需声明ohos.permission.USE_BLUETOOTH权限,遵循BLE协议规范进行数据传输。开发时注意设备适配和功耗优化。

是的,HarmonyOS Next支持低功耗蓝牙(BLE)开发,适用于包括手表在内的多种设备。开发者可以通过HarmonyOS的蓝牙API实现设备扫描、连接、数据传输等功能。具体开发文档和示例代码可参考华为官方开发者文档。

回到顶部