Android 蓝牙4.0 BLE 串口模块最佳实践

mac2024-12-12  27

本文较长,阅读大约15分钟,难得的精品文章。

很多同学对BLE感兴趣,确实,作为主流的蓝牙协议,它还是很有竞争性的,但是BLE没有硬件也不好调试,所以我买了一块开发板来给大家写这篇文章,讲解各种细节和思路,希望你看完这篇文章能对BLE有一个更加清晰的认识。

强烈推荐我的慕课网新课

Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固

点击阅读原文直达或点击下方地址查看详情:

从零打造一款跟随灵魂的高性能社交App

一.蓝牙模块

首先介绍一下这块开发板,型号是HC-08,相关开发包和工具我也会在文末给大家提供,模块采用 TI 的 CC2540F256 芯片,配置 256K 字节空间,支持 AT 指令,用户可根据 需要更改角色(主、从模式)以及串口波特率、设备名称等参数,使用灵活。

         

再说一下指令,也就是协议,其实就是约定的字段,和接口文档类似,我们来看下一些基本的指令吧:

指令

响应

属性

AT

OK

测试

AT+RX

相关属性,如Name

获取模块的基本参数

AT+DEFAULT

OK

恢复出厂设置

AT+RESET

OK

重启模块

AT+VERSION

HC-xxx

获取软件版本和发布日期

AT+ROLE=x

Master/Slave

设置主机/从机

AT+NAME=x

OKsetNAME

设置蓝牙名称

AT+CLEAR

OK

清除记忆地址

AT+LED=x

OK+LED=x

LED操作

?:查询

0  :关闭

1  :打开

大家看下大概能明白就行,指令很多,就不一一列举出来了,连接BLE设备然后通信,这其实和我们所认知的C/S架构是一样的,不过,模块是支持身份互相转换的,我们默认即手机为从设备,模块为主设备,如图:

              

所以只要建立连接,然后按照协议去收发即可了,那么首先,我们需要来进行一些模块的初始化动作,先将模块插入USB,指示灯亮起,并且电脑识别到设备即可,然后打开HID传串口小助手,如图:

 

             

 

在这一步我们应该做的就是点击选择模块选择合适的模块,比如我这里选择的就是HC-08,然后紧接着就可以测试了,如下图,我们首先点击了一个测试指令,实际上他发送的就是AT,然后返回OK,说明我们的串口通信没问题,对吧,如图:

 

            

那么接下来就要设置波特率了,这里输入115200点击设置即可,可以看到控制台返回OK115200,NONE字样,首先OK115200是成功的意思,大家都知道,然后你可以看到下表中其实有一个模块波特率设置失败的提示,这其实是我们已经是115200波特率了,你也可以点击模块波特率查询查看,NONE的意思见下图:

 

               

我们查看协议文档,可知最后的是校验位代号,而NONE代表无校验。 

               

接着我们再来设置下蓝牙名称,输入LGL后点击设置即可,同样的控制台也输出设置成功了。 

              

对比一下协议你就清楚我们三方的关系了,现在你最起码会觉得这玩意其实没有想象中的那么难对吧,那么我们就可以来开始我们的Android实践了。 

               

二.认识BLE API

我们做一个抽丝剥茧的应用,首先是搜索,对吧,搜索到对应的BLE设备,比如我们这个蓝牙模块之后建立通信,然后开始协议的发送和接口,所以我们的开发步骤:

 

1.搜索设备

2.连接设备

3.发送指令

 

那么你肯定得懂他的方法和回调,而之前的蓝牙方法是不支持BLE的,低功耗蓝牙的传输方面是有自己的一套理论体系,且听我慢慢分析

1.权限

在BLE之前,我们只需要添加两条权限就够了

<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

但是现在还需要增加几条权限

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/><uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

分别是定位权限和声明BLE的标记,如果没有这些权限是无法使用BLE的,Android6.0还需要动态申请运行时权限

2.判断支持性

我们需要检查运行设备是否能支持BLE,这就刚好用到了刚才在清单文件定义的标志位了

//判断是否支持BLEif(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "设备不支持BLE", Toast.LENGTH_SHORT).show(); finish();}

有些设备是不支持BLE的

3.BluetoothAdapter

本地蓝牙适配器,可以从中获取到本地蓝牙的相关信息,比如名称,地址等,我们可以通过两种方式来获取本地适配器

//1.获取本地蓝牙适配器BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();//2.获取本地蓝牙适配器mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

可以看到,我们可以通过实例化BluetoothManager来获取,也可以通过BluetoothAdapter的静态方法getDefaultAdapter来获取。

 

如果翻阅BluetoothManager的源码你就会发现,实际上getAdapter中获取的适配器对象就是BluetoothManager 初始化的时候调用BluetoothAdapter.getDefaultAdapter()获取的,所以两者并没有什么区别。

             

4.BluetoothDevice

蓝牙设备类,代表远程端的设备信息

5.BluetoothManager

蓝牙的管理类,我们一般用他来获取我们本地的BluetoothAdapter。

6.BluetoothLeScanner

蓝牙的搜索类,负责搜索和停止搜索。

7.ScanCallback

BluetoothLeScanner搜索的结果回调,可以获取远程设备

8.BluetoothGatt

BLE协议的执行者,通过他可以连接设备,发现设备以及收发数据,而他其实是继承自BluetoothProfile,这个类就是一套通用的数据交互规范类,我们通过BluetoothGatt就可以发送和接收数据了。

9.BluetoothGattService

Gatt服务

10.BlueBluetoothGattCharacteristic

Gatt的特性

11.BluetoothGattDescriptor

Gatt描述

12.BluetoothGattCallback

Gatt回调

//连接状态发生变化//newState://连接成功:BluetoothProfile.STATE_CONNECTED//连接失败:BluetoothProfile.STATE_DISCONNECTEDonConnectionStateChange(BluetoothGatt gatt, int status, int newState)//UUID搜索服务成功回调onServicesDiscovered(BluetoothGatt gatt, int status)//读取数据//status:成功:BluetoothGatt.GATT_SUCCESSonCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)//写入数据//status:成功:BluetoothGatt.GATT_SUCCESSonCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)//收到数据onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic character)

三.初始化

对于初始化,我们会分为两部分,第一部分是给搜索做准备的,第二部分是给连接做准备的,我们先来看下搜索的初始化,如下代码:

            

我们首先判断是否支持BLE,再到获取本地蓝牙适配器,以及判断蓝牙是否打开。

四.搜索与停止搜索

我们在初始化中获取到了BluetoothLeScanner的对象就可以实现搜索和停止搜索

             

从图中的代码可以看到,我写了一个scanBleDev的方法,如果传true,则startScan,反之则stopScan,这里我在搜索的时候做了一个定时关闭的处理,这是因为蓝牙的搜索实际上是非常耗电的,如果不及时关闭,则会消耗设备的电量,所以我们会规定一段时间后停止搜索,而他们所传递的都是同一个回调mScanCallback

五.搜索结果处理

ScanCallback的回调onScanResult中会有参数ScanResult返回,我们来看下这个类的参数:

            

我们可以看到,mDevice就是我们所需要的远程设备,并且我们还能获取到比如mRssi信号,mEventType事件类型等参数,而mDevice则是我们熟悉的BluetoothDevice,我们可以通过他获取想要的信息,如蓝牙名称,蓝牙地址等。

六.搜索实现

我们现在就可以去实现搜索了,我在menu中新建了两个item,效果如图:

          

然后在内部通过RecyclerView实现搜索结果的列表,来看下对应的代码

 

1.Menu的创建

             

 

2.数据源的填充

              

来看下实现效果:

 

             

七.连接设备

现在我们的列表已经搜索出来了,我们就可以根据地址去连接了,所以当我们点击某个Item的时候我们就跳转到另一个收发消息的界面:

           

我们的主要数据还是蓝牙地址,我们可以通过远程设备去连接,如图:

              

这样我们就能得到一个mBluetoothGatt对象以及一个mGattCallback回调了,我们的数据传输之旅就从这里开始了。

八.GattCallback

我们一起的起点是从这里开始,终点亦是从这里开始,先来看下第一个回调吧。

            

当我们调用connect并且成功开始连接之后,onConnectionStateChange就能收到回调了,并且其中有一个参数newState就是用来判断状态的

 

连接成功

BluetoothProfile.STATE_CONNECTED

连接失败

BluetoothProfile.STATE_DISCONNECTED

当我们里连接成功之后,就可以调用discoverServices去搜索服务,那么在我们的onServicesDiscovered中就能接到通知,这个时候我们就要去从众多服务中拿到我们的BluetoothGattCharacteristic对象了,只有拿到了它,我们才能去发送消息,来看代码:

             

这就是比较关键的代码了,我们通过Gatt客户端是可以获取到一个BluetoothGattService

的List,然后去遍历之后得到的BluetoothGattService再去获取对应BluetoothGattCharacteristic的List,然后再去根据他的UUID是否与我们初定的UUID想匹配,这个UUID是模块的:

//模块的UUIDpublic static String HEART_RATE_MEASUREMENT = "0000ffe1-0000-1000-8000-00805f9b34fb";

如果想匹配就可以确定他对应的BluetoothGattCharacteristic特性则使我们需要的数据对象了,现在我们还有两个回调,对应的就是读写的操作了

九.指令的读写

我们读写操作分为三部曲

 

发送数据

发送数据结果回调

接收数据

首先是发送数据,也就是我们的发送按钮的点击事件

              

我们每次发送只支持20字节,所以我们需要分包

            

最终封装我们从onServicesDiscovered中获取到的BluetoothGattCharacteristic对象由Gatt客户端来进行数据的写入,也就是

//发送数据mBluetoothGatt.writeCharacteristic(target_chara);

 

那么我们的二部曲则是发送数据结果回调,来看下代码

             

当你发送成功,也就是status == BluetoothGatt.GATT_SUCCESS的时候,我就向UI写入一个发送成功,那么现在还有三部曲,也就是我们的收,收的话也简单:

              

在onCharacteristicChanged中就能得到values值,这样我们的通信就畅通无阻了。

十.测试

我们现在来测试一下:

 

需求:手机App连接蓝牙模块,然后发送一个Hello,串口接收到之后返回一个 Hi Android.

 

这个需求很简单吧,我们来看下运行效果

              

首先连接成功,然后发送一个Hello,也显示发送成功,这说明onCharacteristicWrite的status=BluetoothGatt.GATT_SUCCESS,那么来看下我们的串口

             

 

串口也收到了这个Hello,现在我们发送一个Hi Android回去

              

可以看到onCharacteristicChanged已经收到了,并且UI上也显示了

              

那么串口是如何发送消息的呢?

 

             

 

在输入框中输入指令,然后点击数据发送的按钮即可。

十一.注意事项

1.本次案例是基于蓝牙窗口通信的Android案例,在实际开发过程中,串口端是有自己的协议的,比如我Android端发送AT,则串口返回OK

 

2.关于数据字节码这块,首先发送字节是需要分包的,这个要注意下,其次是接收的字节,我只是简单的转换成String,数字和字母是没问题的,如果你想支持中文,还需要另外的解码,因为字节是以十六进制进行发送的,网上也能找到对应的资料。

 

最后来张模块的真机图片吧:

 

             

 

源码&PPT&蓝牙模块资料包

https://pan.baidu.com/s/1epSh2ScCSbhq6xmStywTQw

密码:t04q

强烈推荐我的新课

Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固

点击查看:

从零打造一款跟随灵魂的高性能社交App

也可以点击阅读原文直达

扫描二维码

加入知识星球

Hi Android

最新回复(0)