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
报错信息参数无效,可能原因:
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 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