HarmonyOS 鸿蒙Next setCharacteristicChangeNotification errCode: 401
HarmonyOS 鸿蒙Next setCharacteristicChangeNotification errCode: 401



在监听到设备连接状态为 constant.ProfileConnectionState.STATE_CONNECTED 后绑定通道,报
E [bluetooth_gatt_client.cpp(GetService:740)]failed
E [napi_bluetooth_gatt_client.cpp(CheckSetCharacteristicChange:662)]Not found character
E [napi_bluetooth_gatt_client.cpp(setCharacteristicChangeInner:677)]bluetoothManager napi assert failed.
E errCode: 401, errMessage: BussinessError 401: Invalid parameter., data: undefined
更多关于HarmonyOS 鸿蒙Next setCharacteristicChangeNotification errCode: 401的实战教程也可以访问 https://www.itying.com/category-93-b0.html
报错信息参数无效,可能原因:
1、未指定必填参数;
2、参数类型不正确;
3、参数校验失败。
检查参数信息是否正确。api参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-bluetooth-ble-V5#setcharacteristicchangenotification
附实例demo:
import { ble, constant } from '[@kit](/user/kit).ConnectivityKit';
import { promptAction, router, SegmentButtonOptions } from '[@kit](/user/kit).ArkUI';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { util } from '[@kit](/user/kit).ArkTS';
import { SegmentButton, SegmentButtonItemTuple } from '[@ohos](/user/ohos).arkui.advanced.SegmentButton';
[@Entry](/user/Entry)
[@Component](/user/Component)
struct ClientDetail {
  device: ble.ScanResult = router.getParams() as ble.ScanResult;
  [@State](/user/State) gattClient: ble.GattClientDevice | undefined = undefined;
  [@State](/user/State) deviceName: string = '';
  [@State](/user/State) gattServiceInfo: ble.GattService | undefined = undefined;
  [@State](/user/State) connectSwitch: boolean = false;
  [@State](/user/State) stateListenSwitch: boolean = false;
  [@State](/user/State) connectStateSwitch: boolean = false;
  [@State](/user/State) bleCharChangeSwitch: boolean = false;
  [@State](/user/State) connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;
  [@State](/user/State) characteristicValue: string = '';
  [@State](/user/State) describeValue: string = '';
  private serviceUuid = "00001810-0000-1000-8000-00805F9B34FB";
  [@State](/user/State) writeValueRecording: Array<string> = [];
  [@State](/user/State) cValue: string = '';
  [@State](/user/State) dValue: string = '';
  [@State](/user/State) singleSelectCapsuleOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({
    buttons: [{ text: 'notify通道' }, { text: 'indicate通道' }] as SegmentButtonItemTuple,
  });
  [@State](/user/State) singleCapsuleSelectedIndexes: number[] = [0];
  [@State](/user/State) notifyChannelSwitch: boolean = false;
  aboutToAppear(): void {
    if (!this.gattClient) {
      this.gattClient = ble.createGattClientDevice(this.device?.deviceId);
      this.getDeviceName()
    }
  }
  build() {
    Scroll() {
      Column({ space: 10 }) {
        Text() {
          Span('设备名称: ')
          Span(this.deviceName)
        }
        .itemStyle()
        Row() {
          Text('连接状态监听')
          Blank()
          Toggle({ type: ToggleType.Switch, isOn: this.stateListenSwitch })
            .onChange((isOn: boolean) => {
              if (isOn) {
                this.onBLEConnectionStateChange()
              } else {
                this.offBLEConnectionStateChange()
              }
              console.info('ble server instanceSwitch status:' + isOn)
            })
        }
        .itemStyle()
        Row() {
          Text('连接状态')
          Blank()
          if (this.connectState == constant.ProfileConnectionState.STATE_CONNECTING) {
            LoadingProgress().height(15).width(15)
          } else {
            Toggle({ type: ToggleType.Switch, isOn: this.connectStateSwitch })
              .enabled(false)
          }
        }
        .itemStyle()
        Row() {
          Text('连接开关')
          Blank()
          Toggle({ type: ToggleType.Switch, isOn: this.connectSwitch })
            .onChange((isOn: boolean) => {
              if (isOn) {
                this.connectServer()
              } else {
                this.disconnectServer()
              }
              console.info('ble server instanceSwitch status:' + isOn)
            })
        }
        .itemStyle()
        Column({ space: 10 }) {
          Button('服务发现').onClick((event: ClickEvent) => {
            this.getServices()
          })
            .width('100%')
          Scroll() {
            if (this.gattServiceInfo) {
              Text(JSON.stringify(this.gattServiceInfo))
            } else {
              Text('暂无数据')
            }
          }
          .height(100)
          .scrollBar(BarState.Off)
        }
        .itemStyle()
        Column({ space: 10 }) {
          Button('client端读取蓝牙低功耗设备特定服务的特征值').onClick((event: ClickEvent) => {
            this.readCharacteristicValue()
          })
          Scroll() {
            if (this.characteristicValue) {
              Text(JSON.stringify(this.characteristicValue))
            } else {
              Text('暂无数据')
            }
          }
          .height(50)
          .scrollBar(BarState.Off)
        }
        .itemStyle()
        Column({ space: 10 }) {
          Button('client端读取蓝牙低功耗设备特定服务的描述符').onClick((event: ClickEvent) => {
            this.readDescriptorValue()
          })
          Scroll() {
            if (this.describeValue) {
              Text(JSON.stringify(this.describeValue))
            } else {
              Text('暂无数据')
            }
          }
          .height(50)
          .scrollBar(BarState.Off)
        }
        .itemStyle()
        Row() {
          SegmentButton({
            options: this.singleSelectCapsuleOptions,
            selectedIndexes: $singleCapsuleSelectedIndexes
          })
            .width('70%')
            .enabled(!this.notifyChannelSwitch)
          Toggle({ type: ToggleType.Switch, isOn: this.notifyChannelSwitch })
            .onChange((isOn: boolean) => {
              this.setNotificationChannel(isOn)
              console.info('ble server instanceSwitch status:' + isOn)
            })
        }
        .width('100%')
        .height(50)
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)
        .borderRadius(10)
        .padding({
          left: 15,
          right: 15,
          top: 8,
          bottom: 8
        })
        Column({ space: 10 }) {
          Row() {
            Text('特征值变化事件')
            Blank()
            Toggle({ type: ToggleType.Switch, isOn: this.bleCharChangeSwitch })
              .onChange((isOn: boolean) => {
                if (isOn) {
                  this.onBleCharacteristicChange()
                } else {
                  this.offBleCharacteristicChange()
                }
                console.info('ble server instanceSwitch status:' + isOn)
              })
          }
          .width('100%')
          Scroll() {
            if (this.writeValueRecording.length > 0) {
              List({ space: 5 }) {
                ForEach(this.writeValueRecording, (item: string) => {
                  ListItem() {
                    Text(JSON.stringify(item))
                  }
                })
              }
            } else {
              Text('暂无数据')
            }
          }
          .height(100)
          .scrollBar(BarState.Off)
        }
        .itemStyle()
        Row() {
          TextInput({ text: this.cValue })
            .onChange((value: string) => {
              this.cValue = value
            })
            .width('75%')
            .height(40)
            .borderRadius(0)
          Button('写特征值').onClick((event: ClickEvent) => {
            this.writeCharacteristicValue()
          })
            .type(ButtonType.Normal)
            .width('25%')
        }
        Row() {
          TextInput({ text: this.dValue })
            .onChange((value: string) => {
              this.dValue = value
            })
            .width('75%')
            .height(40)
            .borderRadius(0)
            .maxLength(2)
          Button('写描述符').onClick((event: ClickEvent) => {
            this.writeDescriptorValue()
          })
            .type(ButtonType.Normal)
            .width('25%')
        }
      }
      .width('100%')
      .padding({
        left: 15,
        right: 15,
        top: 20,
        bottom: 20
      })
      .backgroundColor(Color.Gray)
    }
    .scrollBar(BarState.Off)
  }
  /**
   * client获取远端蓝牙低功耗设备名
   */
  getDeviceName() {
    if (this.gattClient) {
      this.gattClient.getDeviceName((err: BusinessError, data: string) => {
        console.info('device name err ' + JSON.stringify(err));
        console.info('device name' + JSON.stringify(data));
        this.deviceName = data
      })
    }
  }
  /**
   * client端获取蓝牙低功耗设备的所有服务,即服务发现
   */
  getServices() {
    if (this.gattClient) {
      this.gattServiceInfo = undefined;
      this.gattClient.getServices().then((result: Array<ble.GattService>) => {
        console.info('getServices successfully:' + JSON.stringify(result));
        result.filter(item => {
          if (item.serviceUuid == this.serviceUuid) {
            this.gattServiceInfo = item;
            let decoder = util.TextDecoder.create('"utf-8"');
            decoder.decodeWithStream(new Uint8Array(item?.characteristics[0]?.characteristicValue));
          }
        })
      });
    }
  }
  /**
   * client端发起连接远端蓝牙低功耗设备
   */
  connectServer() {
    if (this.gattClient && this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
      this.gattClient.connect()
    }
  }
  /**
   * client端断开与远端蓝牙低功耗设备的连接
   */
  disconnectServer() {
    if (this.gattClient && this.connectState != constant.ProfileConnectionState.STATE_DISCONNECTED) {
      this.gattClient.disconnect()
    }
  }
  /**
   * client端订阅蓝牙低功耗设备的连接状态变化事件
   */
  onBLEConnectionStateChange() {
    if (this.gattClient) {
      this.gattClient.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {
        console.log('BluetoothPage bluetooth connect state changed');
        let deviceId = state.deviceId;
        console.log('BluetoothPage deviceId connectState ' + deviceId);
        this.connectState = state.state;
        if (this.connectState == constant.ProfileConnectionState.STATE_DISCONNECTED) {
          this.connectStateSwitch = false;
        }
        if (this.connectState == constant.ProfileConnectionState.STATE_CONNECTED) {
          this.connectStateSwitch = true;
        }
        console.log('BluetoothPage bluetooth connectState ' + JSON.stringify(this.connectState));
      })
      this.stateListenSwitch = true;
    }
  }
  /**
   * client端订阅蓝牙低功耗设备的连接状态变化事件
   */
  offBLEConnectionStateChange() {
    if (this.gattClient) {
      this.gattClient.off('BLEConnectionStateChange')
      this.stateListenSwitch = false;
    }
  }
  /**
   * client端读取蓝牙低功耗设备特定服务的特征值
   */
  readCharacteristicValue() {
    if (!this.gattServiceInfo) {
      this.characteristicValue = '';
      console.log('BluetoothPage bluetooth gattServiceInfo is undefined ');
      return
    }
    let services: ble.GattService = this.gattServiceInfo;
    let descriptors: Array<ble.BLEDescriptor> = [];
    let bufferDesc = new ArrayBuffer(8);
    let descV = new Uint8Array(bufferDesc);
    descV[0] = 11;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: services.characteristics[0].descriptors[0].serviceUuid,
      characteristicUuid: services.characteristics[0].descriptors[0].characteristicUuid,
      descriptorUuid: services.characteristics[0].descriptors[0].descriptorUuid,
      descriptorValue: bufferDesc
    };
    descriptors[0] = descriptor;
    let bufferCCC = new ArrayBuffer(8);
    let cccV = new Uint8Array(bufferCCC);
    cccV[0] = 1;
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: services.characteristics[0].serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      characteristicValue: bufferCCC,
      descriptors: descriptors
    };
    try {
      if (this.gattClient) {
        this.gattClient.readCharacteristicValue(characteristic, (err, bleCharacteristicDataOut) => {
          if (err != null) {
            console.error('readCharacteristicValue error, code = ' + (err as BusinessError).code)
            this.characteristicValue = '';
            return;
          }
          const decoder = util.TextDecoder.create('"utf-8"');
          const str = decoder.decodeWithStream(new Uint8Array(bleCharacteristicDataOut.characteristicValue));
          console.info('bluetooth readCharacteristicValue = ' + str);
          this.characteristicValue = '特征值:' + str;
        });
      }
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }
  /**
   * client端读取蓝牙低功耗设备特定的特征包含的描述符
   */
  readDescriptorValue() {
    if (!this.gattServiceInfo) {
      this.characteristicValue = '';
      console.log('BluetoothPage bluetooth gattServiceInfo is undefined ');
      return
    }
    let services: ble.GattService = this.gattServiceInfo;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: services.serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      descriptorUuid: services.characteristics[0].descriptors[0].descriptorUuid,
      descriptorValue: services.characteristics[0].descriptors[0].descriptorValue,
    };
    try {
      if (this.gattClient) {
        this.gattClient.readDescriptorValue(descriptor, (code: BusinessError, BLEDescriptor: ble.BLEDescriptor) => {
          if (code != null) {
            console.error('readCharacteristicValue error, code = ' + (code as BusinessError).code)
            this.describeValue = '';
            return;
          }
          let decoder = util.TextDecoder.create('utf-8');
          const str = decoder.decodeToString(new Uint8Array(BLEDescriptor.descriptorValue));
          //const str = Utils.ArrayBuffer2String(BLEDescriptor.descriptorValue);
          console.info('bluetooth read descriptorValue = ' + str);
          this.describeValue = '描述符:' + str;
          console.log('bluetooth descriptor value: ' + str);
        });
      }
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }
  /**
   * client端向低功耗蓝牙设备特定的描述符写入二进制数据-
   * 描述符的写入不能使用00002902-0000-1000-8000-00805F9B34FB的descriptorUuid-
   * 所以在广播的时候增加了00002903-0000-1000-8000-00805F9B34FB的descriptorUuid-
   * 用来做描述符的写入
   *
   * descriptorValue值只能是Arraybuffer(2),2位的Arraybuffer
   */
  writeDescriptorValue() {
    if (!this.gattServiceInfo) {
      this.characteristicValue = '';
      console.log('BluetoothPage bluetooth gattServiceInfo is undefined ');
      return
    }
    let services: ble.GattService = this.gattServiceInfo;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: services.serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      descriptorUuid: '00002903-0000-1000-8000-00805F9B34FB',
      descriptorValue: strToArrayBuffer(this.dValue)
    };
    try {
      if (this.gattClient) {
        console.log('writeDescriptorValue ' + JSON.stringify(descriptor))
        this.gattClient.writeDescriptorValue(descriptor);
        promptAction.showToast({
          message: '描述值写结束'
        })
        console.log('writeDescriptorValue success')
      }
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }
  /**
   * 向服务端发送设置通知此indicate征值请求
   */
  setNotificationChannel(isOn: boolean) {
    if (!this.gattServiceInfo) {
      this.characteristicValue = '';
      console.log('BluetoothPage bluetooth gattServiceInfo is undefined ');
      return
    }
    let services: ble.GattService = this.gattServiceInfo;
    let descriptors: Array<ble.BLEDescriptor> = [];
    let arrayBuffer = new ArrayBuffer(8);
    let descV = new Uint8Array(arrayBuffer);
    descV[0] = 11;
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: services.serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      descriptorUuid: services.characteristics[0].descriptors[0].descriptorUuid,
      descriptorValue: arrayBuffer
    };
    descriptors[0] = descriptor;
    let arrayBufferC = new ArrayBuffer(8);
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: services.serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      characteristicValue: arrayBufferC,
      descriptors: descriptors
    };
    try {
      if (this.gattClient) {
        if (this.singleCapsuleSelectedIndexes[0] == 0) {
          this.gattClient.setCharacteristicChangeNotification(characteristic, isOn)
        } else {
          this.gattClient.setCharacteristicChangeIndication(characteristic, isOn);
        }
        this.notifyChannelSwitch = isOn;
        console.log('BluetoothPage setCharacteristicChangeNotification finish');
      }
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }
  /**
   * 订阅蓝牙低功耗设备的特征值变化事件
   */
  onBleCharacteristicChange() {
    try {
      if (this.gattClient) {
        this.gattClient.on('BLECharacteristicChange', (characteristicChangeReq: ble.BLECharacteristic) => {
          let serviceUuid: string = characteristicChangeReq.serviceUuid;
          let characteristicUuid: string = characteristicChangeReq.characteristicUuid;
          let value: Uint8Array = new Uint8Array(characteristicChangeReq.characteristicValue);
          let decoder = util.TextDecoder.create('utf-8');
          console.log('BluetoothPage BLECharacteristicChange value = ' + JSON.stringify(characteristicChangeReq));
          this.writeValueRecording.push(`${new Date().getTime()}_变化: ` +
          decoder.decodeToString(new Uint8Array(characteristicChangeReq.characteristicValue)))
        })
        this.bleCharChangeSwitch = true;
        console.log('BluetoothPage bleCharacteristicChange ' + this.bleCharChangeSwitch);
      }
    } catch (err) {
      console.error('bleCharacteristicChange errCode: ' + (err as BusinessError).code + ', errMessage: ' +
      (err as BusinessError).message);
    }
  }
  /**
   * 取消订阅蓝牙低功耗设备的特征值变化事件
   */
  offBleCharacteristicChange() {
    try {
      if (this.gattClient) {
        this.gattClient.off('BLECharacteristicChange')
        this.bleCharChangeSwitch = false;
        console.log('BluetoothPage bleCharacteristicChange ' + this.bleCharChangeSwitch);
      }
    } catch (err) {
      console.error('bleCharacteristicChange errCode: ' + (err as BusinessError).code + ', errMessage: ' +
      (err as BusinessError).message);
    }
  }
  writeCharacteristicValue() {
    if (!this.gattServiceInfo) {
      this.characteristicValue = '';
      console.log('BluetoothPage bluetooth gattServiceInfo is undefined ');
      return
    }
    let services: ble.GattService = this.gattServiceInfo;
    let descriptors: Array<ble.BLEDescriptor> = [];
    let descriptor: ble.BLEDescriptor = {
      serviceUuid: services.serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      descriptorUuid: services.characteristics[0].descriptors[0].descriptorUuid,
      descriptorValue: services.characteristics[0].descriptors[0].descriptorValue
    };
    descriptors[0] = descriptor;
    let characteristic: ble.BLECharacteristic = {
      serviceUuid: services.serviceUuid,
      characteristicUuid: services.characteristics[0].characteristicUuid,
      characteristicValue: strToArrayBuffer(this.cValue),
      descriptors: descriptors
    };
    try {
      if (this.gattClient) {
        this.gattClient.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE);
        promptAction.showToast({
          message: '特征值写结束'
        })
        console.log('BluetoothPage writeCharacteristicValue finish');
      }
    } catch (err) {
      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }
}
[@Styles](/user/Styles)
function itemStyle() {
  .width('100%')
  .padding({
    left: 15,
    right: 15,
    top: 8,
    bottom: 8
  })
  .backgroundColor(Color.White)
  .borderRadius(10)
}
function strToArrayBuffer(str: string) {
  let buf = new ArrayBuffer(str.length * 2);
  let bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return bufView.buffer;
}
更多关于HarmonyOS 鸿蒙Next setCharacteristicChangeNotification errCode: 401的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html
打印出来有多条,其中一条是对应的
characteristic=====> {"serviceUuid":"15F1E600-A277-43FC-A484-DD39EF8A9100","characteristicUuid":"15F1E601-A277-43FC-A484-DD39EF8A9100","characteristicValue":{},"descriptors":[{"serviceUuid":"15F1E600-A277-43FC-A484-DD39EF8A9100","characteristicUuid":"15F1E601-A277-43FC-A484-DD39EF8A9100","descriptorUuid":"15F1E601-A277-43FC-A484-DD39EF8A9100","descriptorValue":{}}],"properties":{"write":true,"read":true}}
针对您提到的HarmonyOS鸿蒙系统中调用setCharacteristicChangeNotification方法时遇到的errCode: 401错误,这通常表示权限不足或未正确授权。在鸿蒙系统开发中,设备特征(Characteristic)的通知权限需要被明确授予,否则系统将拒绝设置通知请求。
401错误码具体指向未经授权的访问尝试。在蓝牙BLE开发中,这往往意味着应用或服务尝试访问或修改一个它没有权限的特征值。解决此问题的方法包括:
- 检查权限声明:确保您的应用已在
manifest.json或其他配置文件中声明了必要的蓝牙权限。 - 用户授权:在运行时请求用户授权,确保用户已授予应用访问蓝牙设备的权限。
 - 特征值权限:确认您的应用或服务是否具有修改该特征值通知状态的权限。这可能需要在设备或服务层面进行配置。
 
请检查上述方面是否已正确设置。如果所有配置均无误,但问题依旧存在,可能是系统或API层面的bug。此时,建议您查阅最新的鸿蒙开发者文档或更新您的开发环境至最新版本。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html
        
      
                  
                  
                  
