您的位置:首页 > 移动开发 > Android开发

Android BLE低功耗蓝牙开发(上)关于GATT服务器的理论与搭建

2017-09-14 14:09 651 查看
前言

本来写完Android开发之BlueTooth--最简单的Andorid传统蓝牙通讯Demo之后,我打算写一篇Android开发之BlueTooth--最简单的Andorid低功耗(BLE)蓝牙通讯Demo的。后来看了看官方的文档,我的天,谷歌给给出的sample里面只有客户端的交互(就是所谓的中央设备),而且代码算是比较老了。关于建立服务端(外围设备)只字未提,这样子我要写个demo不容易啊。因为我本身穷屌丝一个,不会为了一个demo去买个蓝牙设备回来测试,打算在手机建立个服务行不~既然谷歌官方没说到,那我找找国内大神,于是打开scdn搜索
 Android BLE...

不得不说结果很多,但是绝大多数都是相当于翻译谷歌文档,代码都不是自己整理的,也几乎没有关于怎么建立服务器的(就看到寥寥几个提到),那只能自己硬着头皮去看谷歌的类导航了(真的,作为一个小白没有sample,学习还是有点困难的)。

关于GATT服务类

GATT服务相关的类:BluetoothGattServer

继承关系:

public final class BluetoothGattServer 

extends Object implements BluetoothProfile

java.lang.Object继承

   ↳ android.bluetooth.BluetoothGattServer

官方是这么描述的:

这个类提供了扮演蓝牙GATT服务器角色的功能,允许应用程序创建蓝牙智能服务和characteristics。BluetoothGattServer是通过工控机控制蓝牙服务的代理对象。使用openGattServer(Context, BluetoothGattServerCallback)得到这个类的一个实例。

这个类的主要方法我也copy一份下来:


Public methods

boolean
addService(BluetoothGattService service)

Add a service to the list of services to be hosted.
void
cancelConnection(BluetoothDevice device)

Disconnects an established connection, or cancels a connection attempt currently in progress.
void
clearServices()

Remove all services from the list of provided services.
void
close()

Close this GATT server instance.
boolean
connect(BluetoothDevice device,
boolean autoConnect)

Initiate a connection to a Bluetooth GATT capable device.
List<BluetoothDevice>
getConnectedDevices()

Not supported - please use 
getConnectedDevices(int)
 with 
GATT
 as
argument
int
getConnectionState(BluetoothDevice device)

Not supported - please use 
getConnectedDevices(int)
 with 
GATT
 as
argument
List<BluetoothDevice>
getDevicesMatchingConnectionStates(int[]
states)

Not supported - please use 
getDevicesMatchingConnectionStates(int,
int[])
 with 
GATT
 as first
argument
BluetoothGattService
getService(UUID uuid)

Returns a 
BluetoothGattService
 from
the list of services offered by this device.
List<BluetoothGattService>
getServices()

Returns a list of GATT services offered by this device.
boolean
notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic,
boolean confirm)

Send a notification or indication that a local characteristic has been updated.
void
readPhy(BluetoothDevice device)

Read the current transmitter PHY and receiver PHY of the connection.
boolean
removeService(BluetoothGattService service)

Removes a service from the list of services to be provided.
boolean
sendResponse(BluetoothDevice device,
int
e124
requestId, int status, int offset, byte[] value)

Send a response to a read or write request to a remote device.
void
setPreferredPhy(BluetoothDevice device,
int txPhy, int rxPhy, int phyOptions)

Set the preferred connection PHY for this app.
方法很多,但是我们不需要买个都详尽了解。知道个大概就行了,而且大都见名知意。我也是仅仅列出来,在写下去就相当于把谷歌文档搬过来了~有兴趣就自己去看文档更详细了解每个方法的功能和参数。

GATT服务器搭建流程

首先需要说明一点每一个周边BluetoothGattServer,可以包含多个服务Service,每一个Service也包含多个Characteristic。

1.新建一个Characteristic:

character = new BluetoothGattCharacteristic(UUID.fromString(characteristicUUID),BluetoothGattCharacteristic.PROPERTY_NOTIFY,

BluetoothGattCharacteristic.PERMISSION_READ);

2.新建一个GATT服务:

service = new BluetoothGattService(UUID.fromString(serviceUUID),

BluetoothGattService.SERVICE_TYPE_PRIMARY);

3.把Characteristic添加到服务:

service.addCharacteristic(character);

4.获取BluetoothManager:

manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

5.获取/打开周边:

BluetoothGattServer server = manager.openGattServer(this,

new BluetoothGattServerCallback(){...}); 

6.把service添加到周边:server.addService(service);

7.开始广播通知我这里有个service已经开启,可以提供服务了:

BluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);

经过这7步,似乎已经可以成功建立一个服务了。但是里面需要的参数不见得我们都会创建。

那我们来写写。代码参考自:代码连接传送门。感谢代码编写者开源。

代码示例:

package cn.small_qi.bluetoothtest.gattserver;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.ParcelUuid;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import cn.small_qi.bluetoothtest.R;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class GattServerActivity extends AppCompatActivity {

private BluetoothAdapter mBluetoothAdapter;
private BluetoothManager mBluetoothManager;
private BluetoothGattServer mBluetoothGattServer;
private Set<BluetoothDevice> mRegisteredDevices = new HashSet<>();

//先定几个服务类型的UUID
/* Current Time Service UUID */
public static UUID TIME_SERVICE = UUID.fromString("00001805-0000-1000-8000-00805f9b34fb");
/* Mandatory Current Time Information Characteristic */
public static UUID CURRENT_TIME    = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb");
/* Optional Local Time Information Characteristic */
public static UUID LOCAL_TIME_INFO = UUID.fromString("00002a0f-0000-1000-8000-00805f9b34fb");
/* Mandatory Client Characteristic Config Descriptor */
public static UUID CLIENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gatt_server);
openAndInitBt();//初始化需要一定时间
createGattServer();//所以以下这两个方法在这里直接运行是错误的,一定要在蓝牙正确开启,并且支持BLE在执行
startAdvertising();//我卸载这里只是为了展示调用顺序。切记切记!!
}

//1.初始化并打开蓝牙
private void openAndInitBt(){
mBluetoothManager=(BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter==null){return;}//不支持蓝牙
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
return ;//不支持ble蓝牙
}
//.判断蓝牙是否打开
if (!mBluetoothAdapter.enable()) {
//没打开请求打开
Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(btEnable, 100);
}
}
//2.创建GATT服务
private void createGattServer() {
//2.1.新建一个服务
BluetoothGattService service = new BluetoothGattService(TIME_SERVICE,BluetoothGattService.SERVICE_TYPE_PRIMARY);
//2.2 新建一个Characteristic
BluetoothGattCharacteristic currentTime = new BluetoothGattCharacteristic(CURRENT_TIME,
//Read-only characteristic, supports notifications
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ);
//2.3 新建特性描述并配置--这一步非必需
BluetoothGattDescriptor configDescriptor = new BluetoothGattDescriptor(CLIENT_CONFIG,
//Read/write descriptor
BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
currentTime.addDescriptor(configDescriptor);
//2.4 将特性配置到服务
service.addCharacteristic(currentTime);
//2.5 打开外围设备  注意这个services和server的区别,别记错了
mBluetoothGattServer = mBluetoothManager.openGattServer(this, mGattServerCallback);
if (mBluetoothGattServer == null) {
return;
}
mBluetoothGattServer.addService(service);
}

//3.通知服务开启
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startAdvertising() {
BluetoothLeAdvertiser mBluetoothLeAdvertiser= mBluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser == null) {
//创建失败
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.build();

AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.setIncludeTxPowerLevel(false)
.addServiceUuid(new ParcelUuid(TIME_SERVICE))//绑定服务uuid
.build();
mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}

private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.i("", "LE Advertise Started.");
}
@Override
public void onStartFailure(int errorCode) {
Log.w("", "LE Advertise Failed: "+errorCode);
}
};

/**
* Callback to handle incoming requests to the GATT server.
* 所有characteristics 和 descriptors 的读写请求都在这里处理
* 这里我忽略了处理逻辑,这个根据实际需求写
*/
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {

@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
//连接状态改变
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("", "BluetoothDevice CONNECTED: " + device);
mRegisteredDevices.add(device);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("", "BluetoothDevice DISCONNECTED: " + device);
mRegisteredDevices.remove(device);
}
}

@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattCharacteristic characteristic) {
//请求读特征 如果包含有多个服务,就要区分请求读的是什么,这里我只有一个服务
if(CURRENT_TIME.equals(characteristic.getUuid())){
//回应
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,0,"随便回应".getBytes());
}

}

@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattDescriptor descriptor) {
if( CLIENT_CONFIG.equals(descriptor.getUuid())){
Log.d("", "Config descriptor read");
byte[] returnValue;
if (mRegisteredDevices.contains(device)) {
returnValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else {
returnValue = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
}
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_SUCCESS,
0,
returnValue);
} else {
Log.w("", "Unknown descriptor read request");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}

}

@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,BluetoothGattDescriptor descriptor,boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
if (CLIENT_CONFIG.equals(descriptor.getUuid())) {
if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, value)) {
Log.d("", "Subscribe device to notifications: " + device);
mRegisteredDevices.add(device);
} else if (Arrays.equals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE, value)) {
Log.d("", "Unsubscribe device from notifications: " + device);
mRegisteredDevices.remove(device);
}

if (responseNeeded) {
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_SUCCESS,
0,
null);
}
} else {
Log.w("", "Unknown descriptor write request");
if (responseNeeded) {
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
}

//这个实际可以用于反向写数据
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
}
};
}


这样就能简单的创建一个GATT服务了,手上只有一台4.3以上的手机,测试结果目前还不知道~而且搭建服务器要求API 21以上哦

当然,还不要忘了结束应用或者关闭蓝牙的时候关闭服务哦

protected void onDestroy() {
if (mBluetoothLeAdvertiser!=null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
if(mBluetoothGattServer!=null) {
mBluetoothGattServer.close();
}
super.onDestroy();
}


关于里面的一些术语在这里也列出来一下,方便大家阅读:

通用属性描述(Generic Attribute Profile (GATT))— GATT是一个通用规范,用于在 BLE 链接上发送和接收被称为“属性”的短数据块。目前所有的低功耗应用程序规范都是基于GATT。蓝牙技术联盟(Bluetooth SIG)为低功耗设备定义了很多规范。一个规范就是对一个设备在特定应用程序上如何运行的具体说明。注意,一个设备可以支持多种规范。例如,一个设备可以包含一个心率检测器和一个电池电压检测器。

属性协议(Attribute Protocol (ATT))—GATT 是建立在 ATT 之上的。它也被称为 GATT/ATT 。ATT在BLE设备上运行表现优异。为此,它尽可能使用少的字节。每个属性(Attribute)都用一个UUID(Universally Unique Idetifier)进行唯一标识。这是一个标准的128位格式字符串ID,用于唯一标识信息。通过 ATT 传送的这些属性(Attribute)被格式化为特性(Characteristics)和服务(Services)。

特性(Characteristic) — 一个特性包含一个单独的值和 0-n 个描述符,这些描述符描述了特性的值。一个特性可以被认为是一个类型,类似于一个类。

描述符(Descriptor)— 描述符被定义为描述一个特性值的属性。例如,一个描述符可能是指定一个人类可读的描述、一个特性值所能接受的范围或一个专门用于特性值的测量单位。

服务(Service)— 一个服务是一些特性的一个集合。例如,你可能会有一个被称为“心率检测器”的服务,它包含如“心率测量”这个特性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: