前言:

本文主要描述Android BLE的一些基础知识及相关操作流程,不牵扯具体的业务实现,其中提供了针对广播包及响应包的解析思路,希望对正在或即将面临Android BLE开发的伙伴们有所引导。

注:其中的单模、双模、BR、BT、BLE、蓝牙3.0、蓝牙4.0等概念混在一起可能比较难理解,不知下文描述是否清晰,如果有不理解的地方,欢迎留言交流!

一、相关介绍 1、概述

蓝牙无线技术是一种全球通用的短距离无线技术,通过蓝牙技术能够实现多种电子设备间的相互连接,特别是在小型无线电、耗电量低、成本低、安全性、稳定性、易用性以及特别的联网能力等固有的优势上,蓝牙无线技术发展迅速。

2、分类

蓝牙分为三种:Bluetooth Smart Ready、Bluetooth Smart(Smart是低功耗蓝牙的标识)、以及标准 Bluetooth。根据 Bluetooth SIG的说法,这样是为了要分辨装置间的相容性以及标识各版本的传输频率。基本上来说,Bluetooth Smart Ready适用于任何双模蓝牙4.0的电子产品,而Bluetooth Smart是应用在心率监视器或计步器等使用扭扣式电池并传输单一的装置。Bluetooth Smart Ready的相容性最高,可与Bluetooth Smart及标准蓝牙相通。标准蓝牙则无法与Bluetooth Smart相通。

3、BLE介绍

BLE是Bluetooth Low Energy的缩写,又叫蓝牙4.0,区别于蓝牙3.0和之前的技术。BLE前身是NOKIA开发的Wibree技术,主要用于实现移动智能终端与周边配件之间的持续连接,是功耗极低的短距离无线通信技术,并且有效传输距离被提升到了100米以上,同时只需要一颗纽扣电池就可以工作数年之久。BLE是在蓝牙技术的基础上发展起来的,既同于蓝牙,又区别于传统蓝牙。BLE设备分单模和双模两种,双模简称BR,商标为Bluetooth Smart Ready,单模简称BLE或者LE,商标为Bluetooth Smart。Android是在4.3后才支持BLE,这说明不是所有蓝牙手机都支持BLE,而且支持BLE的蓝牙手机一般是双模的。双模兼容传统蓝牙,可以和传统蓝牙通信,也可以和BLE通信,常用在手机上,android4.3和IOS4.0之后版本都支持BR,也就是双模设备。单模只能和BR和单模的设备通信,不能和传统蓝牙通信,由于功耗低,待机长,所以常用在手环的智能设备上。

二、基本概念 1、Generic Access Profile(GAP)

用来控制设备连接和广播,GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。

2、Generic Attribute Profile(GATT)

通过BLE连接,读写属性类数据的Profile通用规范,现在所有的BLE应用Profile都是基于GATT的。

3、Attribute Protocol (ATT)

GATT是基于ATTProtocol的,ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据,每个属性都有一个唯一的UUID,属性将以characterisTIcs and services的形式传输。

4、CharacterisTIc

CharacterisTIc可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。

5、Descriptor

对CharacterisTIc的描述,例如范围、计量单位等。

6、Service

Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart ratemeasurement”的Characteristic。

7、UUID

唯一标示符,每个Service,Characteristic,Descriptor,都是由一个UUID定义。

三、Android BLE API 1、BluetoothGatt

继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,可以看成蓝牙设备从连接到断开的生命周期。

2、BluetoothGattCharacteristic

相当于一个数据类型,可以看成一个特征或能力,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)。

3、BluetoothGattDescriptor

描述符,对Characteristic的描述,包括范围、计量单位等。

4、BluetoothGattService

服务,Characteristic的集合。

5、BluetoothProfile

一个通用的规范,按照这个规范来收发数据。

6、BluetoothManager

通过BluetoothManager来获取BluetoothAdapter。

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

7、BluetoothAdapter

代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作,一个Android系统只有一个BluetoothAdapter,通过BluetoothManager获取。

BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();

8、BluetoothDevice

扫描后发现可连接的设备,获取已经连接的设备。

BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);

9、BluetoothGattCallback

已经连接上设备,对设备的某些操作后返回的结果。

1 2 3 4   BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback(){ //实现回调方法,根据业务做相应处理 }; BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(this, false, bluetoothGattCallback);

三、操作流程 1、蓝牙开启

在使用蓝牙BLE之前,需要确认Android设备是否支持BLE feature(required为false时),另外要需要确认蓝牙是否打开。如果发现不支持BLE,则不能使用BLE相关的功能;如果支持BLE,但是蓝牙没打开,则需要打开蓝牙。代码示例如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44   //是否支持蓝牙模块 @TargetApi(18) public static boolean isSupportBle(Context context) { if(context != null && context.getPackageManager().hasSystemFeature("android.hardware.bluetooth_le")) { BluetoothManager manager = (BluetoothManager)context.getSystemService("bluetooth"); return manager.getAdapter() != null; } else { return false; } } //是否开启蓝牙 @TargetApi(18) public static boolean isBleEnable(Context context) { if(!isSupportBle(context)) { return false; } else { BluetoothManager manager = (BluetoothManager)context.getSystemService("bluetooth"); return manager.getAdapter().isEnabled(); } } //开启蓝牙 public static void enableBle(Activity act, int requestCode) { Intent mIntent = new Intent("android.bluetooth.adapter.action.REQUEST_ENABLE"); act.startActivityForResult(mIntent, requestCode); } //蓝牙开启过程 if(isSupportBle(mContext)){ //支持蓝牙模块 if(!isBleEnable(mContext)){ //没开启蓝牙则开启 enableBle(mSelfActivity, 1); } } else{ //不支持蓝牙模块处理 } //蓝牙开启回调 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { //判断requestCode是否为开启蓝牙时传进去的值,再做相应处理 if(requestCode == 1){ //蓝牙开启成功后的处理 } super.onActivityResult(requestCode, resultCode, data); }

2、设备搜索

BluetoothAdapter.startDiscovery在大多数手机上是可以同时发现经典蓝牙和Ble的,但是startDiscovery的回调无法返回Ble的广播,所以无法通过广播识别设备,且startDiscovery扫描Ble的效率比StartLeScan低很多。所以在实际应用中,还是StartDiscovery和StartLeScan分开扫,前者扫传统蓝牙,后者扫低功耗蓝牙。

由于搜索需要尽量减少功耗,因此在实际使用时需要注意:当找到对应的设备后,立即停止扫描;不要循环搜索设备,为每次搜索设置适合的时间限制,避免设备不在可用范围的时候持续不停扫描,消耗电量。

通过调用BluetoothAdapter的 startLeScan() 搜索BLE设备。调用此方法时需要传入 BluetoothAdapter.LeScanCallback 参数。具体代码示例如下:

1 2 3 4 5 6 7 8   BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); bluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { //对扫描到的设备进行处理,可以依据BluetoothDevice中的信息、信号强度rssi以及广播包和响应包组成的scanRecord字节数组进行分析 } });

3、设备通信

两个设备通过BLE通信,首先需要建立GATT连接,这里我们讲的是Android设备作为client端,连接GATT Server。连接GATT Server,需要调用BluetoothDevice的connectGatt()方法,此函数带三个参数:Context、autoConnect(boolean)和 BluetoothGattCallback 对象。调用后返回BluetoothGatt对象,它是GATT profile的封装,通过这个对象,我们就能进行GATT Client端的相关操作。如断开连接bluetoothGatt.disconnect(),发现服务bluetoothGatt.discoverServices()等等。示例代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82   BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address); BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback(){ @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); } }; BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(this, false, bluetoothGattCallback); //以下为获得Gatt后的相关操作对应的响应方法 //notification to onCharacteristicChanged; bluetoothGatt.setCharacteristicNotification(characteristic, true); //readCharacteristic to onCharacteristicRead; bluetoothGatt.readCharacteristic(characteristic); //writeCharacteristic to onCharacteristicWrite; bluetoothGatt.wirteCharacteristic(mCurrentcharacteristic); //connect and disconnect to onConnectionStateChange; bluetoothGatt.connect(); bluetoothGatt.disconnect(); //readDescriptor to onDescriptorRead; bluetoothGatt.readDescriptor(descriptor); //writeDescriptor to onDescriptorWrite; bluetoothGatt.writeDescriptor(descriptor); //readRemoteRssi to onReadRemoteRssi; bluetoothGatt.readRemoteRssi(); //executeReliableWrite to onReliableWriteCompleted; bluetoothGatt.executeReliableWrite(); //discoverServices to onServicesDiscovered; bluetoothGatt.discoverServices();

四、数据解析

2、TYPE = 0x02:非完整的16 bit UUID列表

3、TYPE = 0x03:完整的16 bit UUID列表

4、TYPE = 0x04:非完整的32 bit UUID列表

5、TYPE = 0x05:完整的32 bit UUID列表

6、TYPE = 0x06:非完整的128 bit UUID列表

7、TYPE = 0x07:完整的128 bit UUID列表

8、TYPE = 0x08:设备简称

9、TYPE = 0x09:设备全名

10、TYPE = 0x0A:表示设备发送广播包的信号强度

11、TYPE = 0x0D:设备类别

12、TYPE = 0x0E:设备配对的Hash值

13、TYPE = 0x0F:设备配对的随机值

14、TYPE = 0x10:TK安全管理(Security Manager TK Value)

15、TYPE = 0x11:带外安全管理(Security Manager Out of Band),各bit定义如下:

1 2 3 4   bit 0: OOB Flag,0-表示没有OOB数据,1-表示有 bit 1: 支持LE bit 2: 对Same Device Capable(Host)同时支持BLE和BR/EDR bit 3: 地址类型,0-表示公开地址,1-表示随机地址

16、TYPE = 0x12:外设(Slave)连接间隔范围,数据中定义了Slave最大和最小连接间隔,数据包含4个字节:前两字节定义最小连接间隔,取值范围:0x0006 ~ 0x0C80,而0xFFFF表示未定义;后两字节,定义最大连接间隔,取值范围同上,不过需要保证最大连接间隔大于或者等于最小连接间隔。

17、TYPE = 0x14:服务搜寻16 bit UUID列表

18、TYPE = 0x15:服务搜寻128 bit UUID列表

19、TYPE = 0x16:16 bit UUID Service,前两个字节是UUID,后面是Service的数据

20、TYPE = 0x17:公开目标地址,表示希望这个广播包被指定的目标设备处理,此设备绑定了公开地址

21、TYPE = 0x18:随机目标地址,表示希望这个广播包被指定的目标设备处理,此设备绑定了随机地址

22、TYPE = 0x19:表示设备的外观

23、TYPE = 0x1A:广播区间

24、TYPE = 0x1B:LE设备地址

25、TYPE = 0x1C:LE设备角色

26、TYPE = 0x1D:256位设备配对的Hash值

27、TYPE = 0x1E:256位设备配对的随机值

28、TYPE = 0x20:32 bit UUID Service,前4个字节是UUID,后面是Service的数据

29、TYPE = 0x21:128 bit UUID Service,前16个字节是UUID,后面是Service的数据

30、TYPE = 0x3D:3D信息数据

31、TYPE = 0xFF:厂商自定义数据,厂商自定义的数据中,前两个字节表示厂商ID,剩下的是厂商自己按照需求添加,里面的数据内容自己定义。

根据如下数据包,举例说明解析的思路

搜索设备获取的数据包如下: 1   02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35 09 03 E7 FE 12 FF 0F 18 0A 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

根据解析规则,可分成如下部分:

1、广播数据

1   02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35

2、响应数据

1   09 03 E7 FE 12 FF 0F 18 0A 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

3、有效数据

1   02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35 09 03 E7 FE 12 FF 0F 18 0A 18

4、无效数据

1   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

其中的有效数据又可分为如下几个数据单元:

02 01 06

14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23

06 08 48 45 54 2D 35

09 03 E7 FE 12 FF 0F 18 0A 18

根据上面定义的AD Type分别解析如下:

第一组数据告诉我们该设备属于LE普通发现模式,不支持BR/EDR;

第二组数据告诉我们该数据为厂商自定义数据,一般是必须解析的,可根据协议规则进行解析获取对应的所需信息;

第三组数据告诉我们该设备的简称为HET-5,其中对应的字符是查找ASSIC表得出的;

第四组数据告诉我们UUID为E7FE-12FF-0F18-0A18(此处有疑,类型03表示的是16位的UUID,对应的两个字节,而此处有8个字节,估计是设备烧录时把字节位数理解为了字符位数导致的问题).

五、参考链接

1、蓝牙Bluetooth BR/EDR 和 Bluetooth Smart 必需要知道的十个不同点

2、BLE简介和Android BLE编程

3、BLE广播数据解析

Android ble连接过程,Android BLE的一些基础知识及相关操作流程 - 全文相关推荐

  1. Android ble连接过程,Android开发之ble蓝牙

    前言 由于自己工作中需要开发ble的项目,于是在折腾了一段时间后也有所了解,便想写下来分享给大家,同时对自己的知识也是一种巩固 1.BLE介绍 BLE是Bluetooth Low Energy的缩写, ...

  2. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  3. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  4. TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—基础知识篇...

    最近遇到一个线上报警:服务器出现大量TIME_WAIT导致其无法与下游模块建立新HTTP连接,在解决过程中,通过查阅经典教材和技术文章,加深了对TCP网络问题的理解.作为笔记,记录于此.       ...

  5. ble连接过程建立_九点之蓝牙连接

    蓝牙连接 蓝牙连接是如何进入到连接状态呢,首先必须经历前面提到的广播阶段,主端通过扫描到从端的广播来发现这个设备,之后让主端发出连接请求来要求与从端建立连接,便可以进入到连接状态.由于蓝牙连接牵涉的点 ...

  6. android 手动连接wifi,android手动连接wifi的过程

    android手动连接wifi的过程 下面就以手动连接mtk5931的wifi为列,来说明手动连接wifi的过程. 在此之前,先说明下,手动连接的使用场景和作用: a: 在纯linux的环境下,该手动 ...

  7. ble连接过程建立_BLE配对绑定过程梳理

    (一)BLE SM为以下三种procedure提供支持: 1. Pairing; 2. Bondig; 3. Encryption Re-establishment; 区别于传统蓝牙的配对过程,BLE ...

  8. ble连接过程建立_BLE4.0教程一 蓝牙协议连接过程与广播分析

    1.蓝牙简介 什么是蓝牙4.0 蓝牙无线技术是使用范围最广泛的全球短距离无线标准之一,蓝牙4.0版本涵盖了三种蓝牙技术,即传统蓝牙.高速蓝牙和低功耗蓝牙技术,将三种规范合而为一.它继承了蓝牙技术在无线 ...

  9. 简诉android源代码编译过程,Android源码编译过程及原理(二)

    在未来等风也等你 本节主要记录的内容anroid 编译系统的结构 编译中枢中main.mk的基本解析 除非特别说明本节中的目录都是基于android的源码目录 1. android 编译系统的结构 a ...

最新文章

  1. Java使用strategy模式构造程序
  2. aspnet是前端还是后端_项目开发中无法回避的问题:前端和后端如何合作和并行工作?...
  3. 洛谷-省选斗兽场-动态规划1
  4. postman响应html,Postman工具——请求与响应(示例代码)
  5. C语言之局部变量全局变量变量存储方式
  6. 支付验证签名失败_验证码收不到,或许是验证码平台出了问题!
  7. 基于OpenCV的计算机视觉入门(4)线段和形状的绘制
  8. Hive学习之Metastore及其配置管理
  9. Gmail推出视频聊天功能 间接否认欲收购Skype
  10. 地类图斑代码大全_全国第二次土地调查土地分类代码含义.doc
  11. roadrunner中文文档(四)app服务器
  12. 【知识图谱】阿里巴巴电商知识图谱
  13. python爬虫之批量下载小说
  14. 什么是 IP 冲突以及如何解决?
  15. xingtai -飞机大战2。0
  16. python向excel写数据_Python向excel中写入数据的方法 方法简单
  17. Axure RP Chrome插件安装
  18. cmake:add_library生成静态库和动态库
  19. Ubuntu安装flash
  20. 计算机专业英语被动语态举例,高考英语各种时态被动语态总结

热门文章

  1. 计算机科学与技术职业规划怎么写,计算机科学与技术专业职业生涯规划书范文...
  2. 看我们如何“把大象放进冰箱里”
  3. Python3.5 win10环境下导入kera/tensorflow报错的解决方法
  4. 阿里35岁程序员失业,为了房贷车贷想去卖煎饼果子养家,IT行业的活路到底在哪里!
  5. 广东地区轨道交通发展建筑人才需求攀升
  6. sparksteaming---实时流计算Spark Streaming原理介绍
  7. Friendship Cards 友情卡片
  8. aws sql mysql_AWS 数据库(七)
  9. 钓鱼竿是如何做网络推广的呢?
  10. 用JAVA写的电子书切割器 欢迎修改意见至wmm204@126.com