目录
- 前言
- 通用属性协议简介
- 关于GATT在HarmonyOS中的应用
- 实现GATT通信
- 自定义GATT
- 结束语
前言
在物联网(IoT)和设备互联的世界中,通用属性协议(Generic Attribute 简称GATT)是蓝牙低功耗(BLE)技术的核心组成部分,用于定义设备如何相互交换数据。而HarmonyOS和其他系统一样,很好的支持蓝牙功能,这就可以使得开发者利用GATT协议开发BLE应用,以及做一些通用属性协议相关的开发工作。那么本文就来详细介绍如何在HarmonyOS应用中使用通用属性协议,包括协议基础、实现步骤和代码示例,方便了解学习使用。
通用属性协议简介
先来介绍一下通用属性协议的概念,它是一种用于在蓝牙低功耗设备之间传输数据的协议,定义了一套通用的属性和服务框架,GATT协议定义了服务(Service)、特性(Characteristic)和描述符(Descriptor)三个核心概念,用于构建BLE应用的数据交换模型:
- 服务(Service):一组特性和描述符的集合,代表设备的特定功能。
- 特性(Characteristic):包含数据的属性,可以被读取、写入或通知。
- 描述符(Descriptor):提供关于特性的额外信息,如用户描述符或客户端配置描述符。
关于GATT在HarmonyOS中的应用
在HarmonyOS中,通过GATT协议,蓝牙设备可以向其他设备提供服务,也可以从其他设备获取服务。鸿蒙开发中使用GATT协议通常涉及到的以下步骤:
1.初始化蓝牙适配器:获取系统蓝牙服务的适配器实例。
2.定义GATT服务和特性:创建服务和特性,定义BLE应用的数据交换模型。
3.实现数据通信:通过GATT特性实现数据的读取、写入和通知。
实现GATT通信
在鸿蒙开发中实际使用GATT通信的时候,主要是从连接server端读取和写入信息、server端操作services和通知客户端信息,这两个部分来使用。具体的实战操作步骤如下所示。
1、连接server端读取和写入信息
关于连接server端读取和写入信息的操作步骤,大概是以下几个操作步骤:
1.import需要的ble模块。
2.创建gattClient实例对象。
3.连接gattServer。
4.读取gattServer的特征值和描述符。
5.向gattServer写入特征值和描述符。
6.断开连接,销毁gattClient实例。
接下来分享一下示例代码,具体如下所示:
import { ble } from '@kit.ConnectivityKit';
import { constant } from '@kit.ConnectivityKit';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
const TAG: string = 'GattClientManager';
export class GattClientManager {
device: string = undefined;
gattClient: ble.GattClientDevice = undefined;
connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;
myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';
myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';
myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB'; // 2902一般用于notification或者indication
mySecondDescriptorUuid: string = '00002903-0000-1000-8000-00805F9B34FB';
found: 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(): ble.BLECharacteristic {
let descriptors: Array<ble.BLEDescriptor> = [];
let descBuffer = new ArrayBuffer(2);
let descValue = new Uint8Array(descBuffer);
descValue[0] = 11;
descValue[1] = 12;
descriptors[0] = this.initDescriptor(this.myFirstDescriptorUuid, new ArrayBuffer(2));
descriptors[1] = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);
let charBuffer = new ArrayBuffer(2);
let charValue = new Uint8Array(charBuffer);
charValue[0] = 1;
charValue[1] = 2;
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 + '\n';
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 + '\n';
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>): boolean {
for (let i = 0; i < services.length; i++) {
if (services[i].serviceUuid != this.myServiceUuid) {
continue;
}
for (let j = 0; j < services[i].characteristics.length; j++) {
if (services[i].characteristics[j].characteristicUuid != this.myCharacteristicUuid) {
continue;
}
for (let k = 0; k < services[i].characteristics[j].descriptors.length; k++) {
if (services[i].characteristics[j].descriptors[k].descriptorUuid == this.myFirstDescriptorUuid) {
return true;
}
}
}
}
return false;
}
// 1. 订阅连接状态变化事件
public onGattClientStateChange() {
if (!this.gattClient) {
return;
}
try {
this.gattClient.on('BLEConnectionStateChange', (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;
}
});
} catch (err) {
}
}
// 2. client端主动连接时调用
public startConnect(peerDevice: string) { // 对端设备一般通过ble scan获取到
if (this.connectState != constant.ProfileConnectionState.STATE_DISCONNECTED) {
return;
}
this.device = peerDevice;
// 2.1 使用device构造gattClient,后续的交互都需要使用该实例
this.gattClient = ble.createGattClientDevice(peerDevice);
try {
this.onGattClientStateChange(); // 2.2 订阅连接状态
this.gattClient.connect(); // 2.3 发起连接
} catch (err) {
}
}
// 3. client端连接成功后,需要进行服务发现
public discoverServices() {
if (!this.gattClient) {
return;
}
try {
this.gattClient.getServices().then((result: Array<ble.GattService>) => {
this.found = this.checkService(result); // 要确保server端的服务内容有业务所需要的服务
});
} catch (err) {
}
}
// 4. 在确保拿到了server端的服务结果后,读取server端特定服务的特征值时调用
public readCharacteristicValue() {
if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
return;
}
if (!this.found) { // 要确保server端有对应的characteristic
return;
}
let characteristic = this.initCharacteristic();
try {
this.gattClient.readCharacteristicValue(characteristic).then((outData: ble.BLECharacteristic) => {
this.logCharacteristic(outData);
})
} catch (err) {
}
}
// 5. 在确保拿到了server端的服务结果后,写入server端特定服务的特征值时调用
public writeCharacteristicValue() {
if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
return;
}
if (!this.found) { // 要确保server端有对应的characteristic
return;
}
let characteristic = this.initCharacteristic();
try {
this.gattClient.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE, (err) => {
if (err) {
return;
}
});
} catch (err) {
}
}
// 6. 在确保拿到了server端的服务结果后,读取server端特定服务的描述符时调用
public readDescriptorValue() {
if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
return;
}
if (!this.found) { // 要确保server端有对应的descriptor
return;
}
let descBuffer = new ArrayBuffer(0);
let descriptor = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);
try {
this.gattClient.readDescriptorValue(descriptor).then((outData: ble.BLEDescriptor) => {
this.logDescriptor(outData);
});
} catch (err) {
}
}
// 7. 在确保拿到了server端的服务结果后,写入server端特定服务的描述符时调用
public writeDescriptorValue() {
if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
return;
}
if (!this.found) { // 要确保server端有对应的descriptor
return;
}
let descBuffer = new ArrayBuffer(2);
let descValue = new Uint8Array(descBuffer);
descValue[0] = 11;
descValue[1] = 12;
let descriptor = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);
try {
this.gattClient.writeDescriptorValue(descriptor, (err) => {
if (err) {
return;
}
});
} catch (err) {
}
}
// 8.client端主动断开时调用
public stopConnect() {
if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {
return;
}
try {
this.gattClient.disconnect(); // 8.1 断开连接
this.gattClient.off('BLEConnectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => {
});
this.gattClient.close() // 8.2 如果不再使用此gattClient,则需要close
} catch (err) {
}
}
}
let gattClientManager = new GattClientManager();
export default gattClientManager as GattClientManager;
2、server端操作services和通知客户端信息
接下来介绍server端操作services和通知客户端信息相关的操作, 主要从下面的操作步骤进行:
1.import需要的ble模块。
2.创建gattServer实例对象。
3.添加services信息。
4.当向gattServer写入特征值通知gattClient。
5.移除services信息。
6.注销gattServer实例。
接下来分享一下示例代码,具体如下所示:
import { ble } from '@kit.ConnectivityKit';
import { constant } from '@kit.ConnectivityKit';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
const TAG: string = 'GattServerManager';
export class GattServerManager {
gattServer: ble.GattServer = undefined;
connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;
myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';
myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';
myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB'; // 2902一般用于notification或者indication
mySecondDescriptorUuid: string = '00002903-0000-1000-8000-00805F9B34FB';
// 构造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(): ble.BLECharacteristic {
let descriptors: Array<ble.BLEDescriptor> = [];
let descBuffer = new ArrayBuffer(2);
let descValue = new Uint8Array(descBuffer);
descValue[0] = 31;
descValue[1] = 32;
descriptors[0] = this.initDescriptor(this.myFirstDescriptorUuid, new ArrayBuffer(2));
descriptors[1] = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);
let charBuffer = new ArrayBuffer(2);
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;
}
// 1. 订阅连接状态变化事件
public onGattServerStateChange() {
if (!this.gattServer) {
return;
}
try {
this.gattServer.on('connectionStateChange', (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;
}
});
} catch (err) {
}
}
// 2. server端注册服务时调用
public registerServer() {
let characteristics: Array<ble.BLECharacteristic> = [];
let characteristic = this.initCharacteristic();
characteristics.push(characteristic);
let gattService: ble.GattService = {
serviceUuid: this.myServiceUuid,
isPrimary: true,
characteristics: characteristics
};
try {
this.gattServer = ble.createGattServer(); // 2.1 构造gattServer,后续的交互都需要使用该实例
this.onGattServerStateChange(); // 2.2 订阅连接状态
this.gattServer.addService(gattService);
} catch (err) {
}
}
// 3. 订阅来自gattClient的读取特征值请求时调用
public onCharacteristicRead() {
if (!this.gattServer) {
return;
}
try {
this.gattServer.on('characteristicRead', (charReq: ble.CharacteristicReadRequest) => {
let deviceId: string = charReq.deviceId;
let transId: number = charReq.transId;
let offset: number = charReq.offset;
console.info(TAG, 'receive characteristicRead');
let rspBuffer = new ArrayBuffer(2);
let rspValue = new Uint8Array(rspBuffer);
rspValue[0] = 21;
rspValue[1] = 22;
let serverResponse: ble.ServerResponse = {
deviceId: deviceId,
transId: transId,
status: 0, // 0表示成功
offset: offset,
value: rspBuffer
};
try {
this.gattServer.sendResponse(serverResponse);
} catch (err) {
}
});
} catch (err) {
}
}
// 4. 订阅来自gattClient的写入特征值请求时调用
public onCharacteristicWrite() {
if (!this.gattServer) {
return;
}
try {
this.gattServer.on('characteristicWrite', (charReq: ble.CharacteristicWriteRequest) => {
let deviceId: string = charReq.deviceId;
let transId: number = charReq.transId;
let offset: number = charReq.offset;
if (!charReq.needRsp) {
return;
}
let rspBuffer = new ArrayBuffer(0);
let serverResponse: ble.ServerResponse = {
deviceId: deviceId,
transId: transId,
status: 0, // 0表示成功
offset: offset,
value: rspBuffer
};
try {
this.gattServer.sendResponse(serverResponse);
} catch (err) {
}
});
} catch (err) {
}
}
// 5. 订阅来自gattClient的读取描述符请求时调用
public onDescriptorRead() {
if (!this.gattServer) {
return;
}
try {
this.gattServer.on('descriptorRead', (desReq: ble.DescriptorReadRequest) => {
let deviceId: string = desReq.deviceId;
let transId: number = desReq.transId;
let offset: number = desReq.offset;
console.info(TAG, 'receive descriptorRead');
let rspBuffer = new ArrayBuffer(2);
let rspValue = new Uint8Array(rspBuffer);
rspValue[0] = 31;
rspValue[1] = 32;
let serverResponse: ble.ServerResponse = {
deviceId: deviceId,
transId: transId,
status: 0, // 0表示成功
offset: offset,
value: rspBuffer
};
try {
this.gattServer.sendResponse(serverResponse);
} catch (err) {
}
});
} catch (err) {
}
}
// 6. 订阅来自gattClient的写入描述符请求时调用
public onDescriptorWrite() {
if (!this.gattServer) {
return;
}
try {
this.gattServer.on('descriptorWrite', (desReq: ble.DescriptorWriteRequest) => {
let deviceId: string = desReq.deviceId;
let transId: number = desReq.transId;
let offset: number = desReq.offset;
if (!desReq.needRsp) {
return;
}
let rspBuffer = new ArrayBuffer(0);
let serverResponse: ble.ServerResponse = {
deviceId: deviceId,
transId: transId,
status: 0, // 0表示成功
offset: offset,
value: rspBuffer
};
try {
this.gattServer.sendResponse(serverResponse);
} catch (err) {
}
});
} catch (err) {
}
}
// 7. server端删除服务,不再使用时调用
public unRegisterServer() {
if (!this.gattServer) {
return;
}
try {
this.gattServer.removeService(this.myServiceUuid); // 7.1 删除服务
this.gattServer.off('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => { // 7.2 取消订阅连接状态
});
this.gattServer.close() // 7.3 如果不再使用此gattServer,则需要close
} catch (err) {
}
}
}
let gattServerManager = new GattServerManager();
export default gattServerManager as GattServerManager;
自定义GATT
上面介绍完通用属性协议使用之后,这里再来介绍一下除了基本的GATT操作之外,我们还可以根据应用的需求自定义GATT,举个例子:
- 自定义服务和特性:根据应用的功能定义特定的服务和特性。
- 实现复杂的数据通信逻辑:根据应用的需求实现更复杂的数据通信逻辑。
结束语
上面详细介绍了通用属性协议的相关内容,可以看出通用属性协议是HarmonyOS开发中实现BLE通信的重要协议。通过本文的介绍,大家应该都了解了如何在HarmonyOS应用中使用GATT协议,也知道了后面在日常开发中关于通用属性协议的使用。随着技术的不断发展,GATT协议将在HarmonyOS生态中扮演越来越重要的角色,给用户带来更加丰富和便捷的设备互联体验,可以好好期待一下!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。