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一份下来:
方法很多,但是我们不需要买个都详尽了解。知道个大概就行了,而且大都见名知意。我也是仅仅列出来,在写下去就相当于把谷歌文档搬过来了~有兴趣就自己去看文档更详细了解每个方法的功能和参数。
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步,似乎已经可以成功建立一个服务了。但是里面需要的参数不见得我们都会创建。
那我们来写写。代码参考自:代码连接传送门。感谢代码编写者开源。
代码示例:
这样就能简单的创建一个GATT服务了,手上只有一台4.3以上的手机,测试结果目前还不知道~而且搭建服务器要求API 21以上哦
当然,还不要忘了结束应用或者关闭蓝牙的时候关闭服务哦
关于里面的一些术语在这里也列出来一下,方便大家阅读:
通用属性描述(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)— 一个服务是一些特性的一个集合。例如,你可能会有一个被称为“心率检测器”的服务,它包含如“心率测量”这个特性。
本来写完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一份下来:
| |
---|---|
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 GATTas argument |
int | getConnectionState(BluetoothDevice device) Not supported - please use getConnectedDevices(int)with GATTas argument |
List<BluetoothDevice> | getDevicesMatchingConnectionStates(int[] states) Not supported - please use getDevicesMatchingConnectionStates(int, int[])with GATTas first argument |
BluetoothGattService | getService(UUID uuid) Returns a BluetoothGattServicefrom 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)— 一个服务是一些特性的一个集合。例如,你可能会有一个被称为“心率检测器”的服务,它包含如“心率测量”这个特性。
相关文章推荐
- 关于Android 5.x的低功耗蓝牙BLE开发简介
- Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android-低功耗蓝牙BLE(Bluetooth Low Energy)开发
- 使用BleLib的轻松搞定Android低功耗蓝牙Ble 4.0开发详解
- Android低功耗蓝牙(BLE)开发的一点感受
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android低功耗蓝牙 升级 5.0以上的BLE开发
- 【Android应用开发】Android 蓝牙低功耗 (BLE) ( 第一篇 . 概述 . 蓝牙低功耗文档 翻译)
- Android BLE低功耗蓝牙开发
- Android 5.x的低功耗蓝牙BLE开发简介
- Android 低功耗蓝牙(BLE)开发(3)-- BluetoothDevice详解
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android关于低功耗的蓝牙开发工具类
- Android-BLE低功耗蓝牙开发
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android 低功耗蓝牙(BLE)开发(1)-- 基本概念
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- Android 低功耗蓝牙(BLE)开发(4)-- 蓝牙扫描和连接
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现