内容简介:想了解蓝牙通信之前,需要先了解蓝牙两个最基本的协议:GAP 和 GATT。GAP是通用访问配置文件的首字母缩写,主要控制蓝牙连接和广播。GAP使蓝牙设备对外界可见,并决定设备是否可以或者怎样与其他设备进行交互。GAP定义了多种角色,但主要的两个是:中心设备 和 外围设备。
想了解蓝牙通信之前,需要先了解蓝牙两个最基本的协议:GAP 和 GATT。
1、GAP(Generic Access Profile)简介
GAP是通用访问配置文件的首字母缩写,主要控制蓝牙连接和广播。GAP使蓝牙设备对外界可见,并决定设备是否可以或者怎样与其他设备进行交互。
GAP定义了多种角色,但主要的两个是:中心设备 和 外围设备。
中心设备:可以扫描并连接多个外围设备,从外设中获取信息。
外围设备:小型,低功耗,资源有限的设备。可以连接到功能更强大的中心设备,并为其提供数据。
2、GAP广播数据
GAP 中外围设备通过两种方式向外广播数据:广播数据 和 扫描回复。 每种数据最长可以包含 31 byte。
广播数据是必需的,因为外设必需不停的向外广播,让中心设备知道它的存在。
扫描回复是可选的,中心设备可以向外设请求扫描回复,这里包含一些设备额外的信息。
外围设备会设定一个广播间隔。每个广播间隔中,它会重新发送自己的广播数据。广播间隔越长,越省电,同时也不太容易扫描到。
3. 广播的网络拓扑结构
外设通过广播自己让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。但有些情况是不需要连接的,只要外设广播自己的数据即可。目的是让外围设备,把自己的信息发送给多个中心设备。因为基于 GATT 连接的方式的,只能是一个外设连接一个中心设备。
4. GATT(Generic Attribute Profile)简介
GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。
BLE设备通过叫做 Service 和 Characteristic 的东西进行通信
GATT使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service, Characteristic对应的数据保存在一个查询表中,次查找表使用 16 bit ID 作为每一项的索引。
GATT 连接是 独占 的。也就是一个 BLE 外设同时只能被一个中心设备连接。一旦外设被连接,它就会马上停止广播,这样它就对其他设备不可见了。当外设与中心设备断开,外设又开始广播,让其他中心设备感知该外设的存在。而中心设备可 同时 与多个外设进行连接。
5.GATT 通信
中心设备和外设需要 双向通信 的话,唯一的方式就是建立 GATT 连接。
GATT 通信的双方是 C/S 关系。外设作为 GATT 服务端(Server),它维持了 ATT 的查找表以及 service 和 characteristic 的定义。中心设备是 GATT 客户端(Client),它向 外设(Server) 发起请求来获取数据。
6.GATT 结构
Profile:并不是实际存在于 BLE 外设上的,它只是一个被 Bluetooth SIG 或者外设设计者预先定义的 Service 的集合。例如心率Profile(Heart Rate Profile)就是结合了 Heart Rate Service 和 Device Information Service。
Service:包含一个或者多个 Characteristic。每个 Service 有一个 UUID 唯一标识。
Characteristic: 是最小的逻辑数据单元。一个Characteristic包括一个单一value变量和0-n个用来描述characteristic变量的Descriptor。与 Service 类似,每个 Characteristic 用 16 bit 或者 128 bit 的 UUID 唯一标识。
实际开发中,和 BLE 外设打交道,主要是通过 Characteristic。可以从 Characteristic 读取数据,也可以往 Characteristic 写数据,从而实现双向的通信。
UUID 有 16 bit 、32bit 和 128 bit 的。16 bit 的 UUID 是官方通过认证的,需要花钱购买。 Bluetooth_Base_UUID定义为 00000000-0000-1000-8000-00805F9B34FB
若16 bit UUID为xxxx,转换为128 bit UUID为0000xxxx-0000-1000-8000-00805F9B34FB
若32 bit UUID为xxxxxxxx,转换为128 bit UUID为xxxxxxxx-0000-1000-8000-00805F9B34FB
二、中心设备与外设通讯
简单介绍BLE开发当中各种主要类和其作用:
BluetoothDeivce:蓝牙设备,代表一个具体的蓝牙外设。
BluetoothGatt:通用属性协议,定义了BLE通讯的基本规则和操作
BluetoothGattCallback:GATT通信回调类,用于回调的各种状态和结果。
BluetoothGattService:服务,由零或多个特征组构成。
BluetoothGattCharacteristic:特征,里面包含了一组或多组数据,是GATT通信中的最小数据单元。
BluetoothGattDescriptor:特征描述符,对特征的额外描述,包括但不仅限于特征的单位,属性等。
1、连接
对扫描到的蓝牙可以用集合形式进行缓存,也可只保存其mac地址,存储到字符集合中,用于后续的连接。
1.1、根据mac地址获取到BluetoothDeivce用于连接
BluetoothManager bluetoothmanager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothmanager.getAdapter(); //获取蓝牙设备对象进行连接 mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(macAddressStr) 复制代码
1.2、蓝牙gatt回调:
实现BluetoothGattCallBack类,监听蓝牙连接过程中各种回调的监听。
蓝牙Gatt回调方法中都不可以进行耗时操作,需要将其方法内进行的操作丢进另一个线程,尽快返回。
//定义子线程handle,用于在BluetoothGattCallback中回调方法中的操作抛到该线程工作。 private Handler mHandler; //定义handler工作的子线程 private HandlerThread mHandlerThread; 初始化handler mHandlerThread = new HandlerThread("daqi"); mHandlerThread.start(); //将handler绑定到子线程中 mHandler = new Handler(mHandlerThread.getLooper()); //定义蓝牙Gatt回调类 public class daqiBluetoothGattCallback extends BluetoothGattCallback{ //连接状态回调 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); // status 用于返回操作是否成功,会返回异常码。 // newState 返回连接状态,如BluetoothProfile#STATE_DISCONNECTED、BluetoothProfile#STATE_CONNECTED //操作成功的情况下 if (status == BluetoothGatt.GATT_SUCCESS){ //判断是否连接码 if (newState == BluetoothProfile.STATE_CONNECTED) { }else if(newState == BluetoothProfile.STATE_DISCONNECTED){ //判断是否断开连接码 } }else{ //异常码 } } //服务发现回调 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, 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 onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } } 复制代码
1.3、连接设备:
调用BluetoothDevice#connectGatt()进行ble连接,第二个参数默认选择false,不自动连接。并定义BluetoothGatt变量,存储BluetoothDevice#connectGatt()返回的对象。
//定义Gatt实现类 private BluetoothGatt mBluetoothGatt; //创建Gatt回调 private BluetoothGattCallback mGattCallback = new daqiBluetoothGattCallback(); //连接设备 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback, BluetoothDevice.TRANSPORT_LE); } else { mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback); } 复制代码
1.4、连接异常处理:
蓝牙连接时,不一定百分百连接成功。连接出错时,会返回异常码进行错误描述。
对于大多数异常码,可以通过重连来达到连接成功的目的。
错误代码:
133 :连接超时或未找到设备。
8 : 设备超出范围
22 :表示本地设备终止了连接
//定义重连次数 private int reConnectionNum = 0; //最多重连次数 private int maxConnectionNum = 3; public class daqiBluetoothGattCallback extends BluetoothGattCallback{ //连接状态回调 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); // status 用于返回操作是否成功,会返回异常码。 //操作成功的情况下 if (status == BluetoothGatt.GATT_SUCCESS){ }else{ //重连次数不大于最大重连次数 if(reConnectionNum < maxConnectionNum){ //重连次数自增 reConnectionNum++ //连接设备 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback, BluetoothDevice.TRANSPORT_LE); } else { mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback); } }else{ //断开连接,返回连接失败回调 } } } //其他回调方法 } 复制代码
2、发现服务
连接成功后,触发BluetoothGattCallback#onConnectionStateChange()方法。
2.1、发现服务:
public class daqiBluetoothGattCallback extends BluetoothGattCallback{ //连接状态回调 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); // status 用于返回操作是否成功,会返回异常码。 //操作成功的情况下 if (status == BluetoothGatt.GATT_SUCCESS){ //判断是否连接码 if (newState == BluetoothProfile.STATE_CONNECTED) { //可延迟发现服务,也可不延迟 mHandler.post(() -> //发现服务 mBluetoothGatt.discoverServices(); ); }else if(newState == BluetoothProfile.STATE_DISCONNECTED){ //判断是否断开连接码 } } } //其他回调方法 } 复制代码
当发现服务成功后,会触发BluetoothGattCallback#onServicesDiscovered()回调:
//定义需要进行通信的ServiceUUID private UUID mServiceUUID = UUID.fromString("0000xxxx-0000-1000-8000-00805f9b34fb"); //定义蓝牙Gatt回调类 public class daqiBluetoothGattCallback extends BluetoothGattCallback{ //服务发现回调 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { mHandler.post(() -> //获取指定uuid的service BluetoothGattService gattService = mBluetoothGatt.getService(mServiceUUID); //获取到特定的服务不为空 if(gattService != null){ }else{ //获取特定服务失败 } ); } } } 复制代码
2.2、发现服务失败:
发现服务时,会存在发现不了特定服务的情况。或者说,整个BluetoothGatt对象中的服务列表为空。
BluetoothGatt类中存在一个隐藏的方法refresh(),用于刷新Gatt的服务列表。当发现不了服务时,可以通过反射去调用该方法。
3、修改特征值并监听外设特征值改变
3.1、读写特定特征:
//定义需要进行通信的ServiceUUID private UUID mServiceUUID = UUID.fromString("0000xxxx-0000-1000-8000-00805f9b34fb"); //定义需要进行通信的CharacteristicUUID private UUID mCharacteristicUUID = UUID.fromString("0000yyyy-0000-1000-8000-00805f9b34fb"); //定义蓝牙Gatt回调类 public class daqiBluetoothGattCallback extends BluetoothGattCallback{ //服务发现回调 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { mHandler.post(() -> //获取指定uuid的service BluetoothGattService gattService = mBluetoothGatt.getService(mServiceUUID); //获取到特定的服务不为空 if(gattService != null){ //获取指定uuid的Characteristic BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(mCharacteristicUUID); //获取特定特征成功 if(gattCharacteristic != null){ //写入你需要传递给外设的特征值(即传递给外设的信息) gattCharacteristic.setValue(bytes); //通过GATt实体类将,特征值写入到外设中。 mBluetoothGatt.writeCharacteristic(gattCharacteristic); //如果只是需要读取外设的特征值: //通过Gatt对象读取特定特征(Characteristic)的特征值 String readValue = mBluetoothGatt.readCharacteristic(gattCharacteristic); } }else{ //获取特定服务失败 } ); } } } 复制代码
当成功读取特征值时,会触发BluetoothGattCallback#onCharacteristicRead()回调。
@Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //获取读取到的特征值 characteristic.getValue() } } 复制代码
当成功写入特征值到外设时,会触发BluetoothGattCallback#onCharacteristicWrite()回调。
@Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //获取写入到外设的特征值 characteristic.getValue() } } 复制代码
当写入完特征值后,外设会修改自己的特征值,手机会触发BluetoothGattCallback#onCharacteristicChanged()方法,进行 双向通信 。
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { if (status == BluetoothGatt.GATT_SUCCESS) { //获取外设修改的特征值 String value = characteristic.getValue() //对特征值进行解析 } } 复制代码
4、断开连接
断开连接的操作分为两步:
1、mBluetoothGatt.disconnect();
2、mBluetoothGatt.close();
调用disconnect()后,会触发手机会触发BluetoothGattCallback#onConnectionStateChange()的回调,回调断开连接信息,newState = BluetoothProfile.STATE_DISCONNECTED。但接着马上调用close(),会终止BluetoothGattCallback#onConnectionStateChange()的回调,可以看情况将两个进行拆分调用,但一般都是 成对 出现。
例如:
需要在外设修改特征值触发BluetoothGattCallback#onCharacteristicChanged()时,断开连接。可以先在BluetoothGattCallback#onCharacteristicChanged()中调用disconnect(),并等调用BluetoothGattCallback#onConnectionStateChange()回调,返回断开连接信息后,再调用close()对Gatt资源进行关闭。
当和外设进行ble通信时,如出现任何意外情况,马上调用断开连接操作。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 网络通信2:TCP简单通信
- 网络通信3:TCP交互通信
- 如何在ASP.NET Core中使用SignalR构建与Angular通信的实时通信应用程序
- Electron 使用串口通信
- 进程间通信---共享内存
- 微服务通信策略
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JSP应用开发技术
柳永坡 / 人民邮电出版社 / 2005-9 / 52.00元
本书全面系统地介绍了JSP应用开发技术,包括JSP预备知识和环境配置、JSP编程基础、JSP应用开发进阶、在JSP中使用数据库、Servlet技术、标签库和表达式语言、Web编程模式和应用框架等几个方面的内容。本书不但由浅入深地介绍了JSP程序设计的原理、方法和技术,还提供了大量的JSP应用开发实例,给出了相应的实用技巧、操作步骤及优化思路。 本书着重于JSP技术的应用性和可操作性,......一起来看看 《JSP应用开发技术》 这本书的介绍吧!