
GATT
定义
GATT协议(Generic Attribute),通用属性协议
它是一种用于在低功耗蓝牙(BLE,Bluetooth Low Energy)设备之间传输数据的协议,定义了一套通用的属性和服务框架。
通过GATT协议,蓝牙设备可以向其他设备提供服务,也可以从其他设备获取服务。
作用
连接server端读取和写入信息,server端操作services和通知客户端信息。
结构
GATT 事务是建立在嵌套的Profiles(规范、通讯协议), Services(服务) 和 Characteristics(特征值)之上的
profile
profile可以理解为一种规范,一个标准的通信协议,它存在于从机中。蓝牙组织规定了一些标准的profile,例如 HID OVER GATT ,防丢器 ,心率计等。每个profile中会包含多个service,每个service代表从机的一种能力。
service
service可以理解为一个服务,在ble从机中,通过有多个服务,例如电量信息服务、系统信息服务等,每个service中又包含多个characteristic特征值。每个具体的characteristic特征值才是ble通信的主题。比如当前的电量是80%,所以会通过电量的characteristic特征值存在从机的profile里,这样主机就可以通过这个characteristic来读取80%这个数据
characteristic
characteristic特征值,ble主从机的通信均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。
UUID
UUID,统一识别码,我们刚才提到的service和characteristic,都需要一个唯一的uuid来标识整理一下,每个从机都会有一个叫做profile的东西存在,不管是上面的自定义的simpleprofile,还是标准的防丢器profile,他们都是由一些列service组成,然后每个service又包含了多个characteristic,主机和从机之间的通信,均是通过characteristic来实现。
GATT client端操作
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 = '';
gattClient: ble.GattClientDevice = ble.createGattClientDevice('11:22:33:AA:BB:FF');
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) {
console.info(TAG, 'find expected service from server');
return true;
}
}
}
}
console.error(TAG, 'no expected service from server');
return false;
}
// 1. 订阅连接状态变化事件
public onGattClientStateChange() {
if (!this.gattClient) {
console.error(TAG, 'no 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) {
console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
// 2. client端主动连接时调用
public startConnect(peerDevice: string) { // 对端设备一般通过ble scan获取到
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 {
this.onGattClientStateChange(); // 2.2 订阅连接状态
this.gattClient.connect(); // 2.3 发起连接
} catch (err) {
console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
// 3. client端连接成功后,需要进行服务发现
public discoverServices() {
if (!this.gattClient) {
console.info(TAG, 'no gattClient');
return;
}
console.info(TAG, 'discoverServices');
try {
this.gattClient.getServices().then((result: Array<ble.GattService>) => {
console.info(TAG, 'getServices success: ' + JSON.stringify(result));
this.found = this.checkService(result); // 要确保server端的服务内容有业务所需要的服务
});
} 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, 'no gattClient or not connected');
return;
}
if (!this.found) { // 要确保server端有对应的characteristic
console.error(TAG, 'no characteristic from server');
return;
}
let characteristic = this.initCharacteristic();
console.info(TAG, 'readCharacteristicValue');
try {
this.gattClient.readCharacteristicValue(characteristic).then((outData: ble.BLECharacteristic) => {
this.logCharacteristic(outData);
})
} 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, 'no gattClient or not connected');
return;
}
if (!this.found) { // 要确保server端有对应的characteristic
console.error(TAG, 'no characteristic from server');
return;
}
let characteristic = this.initCharacteristic();
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);
}
}
}
let gattClientManager = new GattClientManager();
export default gattClientManager as GattClientManager;