我在慕课网的Android新课核心知识点如下:
Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固
手把手完成商业级社交App开发进阶Android高级工程师
很多同学对BLE感兴趣,确实,作为主流的蓝牙协议,它还是很有竞争性的,但是BLE没有硬件也不好调试,所以我买了一块开发板来给大家写这篇文章,讲解各种细节和思路,希望你看完这篇文章能对BLE有一个更加清晰的认识。
一.蓝牙模块
首先介绍一下这块开发板,型号是HC-08,相关开发包和工具我也会在文末给大家提供,模块采用 TI 的 CC2540F256 芯片,配置 256K 字节空间,支持 AT 指令,用户可根据 需要更改角色(主、从模式)以及串口波特率、设备名称等参数,使用灵活。
再说一下指令,也就是协议,其实就是约定的字段,和接口文档类似,我们来看下一些基本的指令吧:
大家看下大概能明白就行,指令很多,就不一一列举出来了,连接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,这就刚好用到了刚才在清单文件定义的标志位了
//判断是否支持BLE
if(!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_DISCONNECTED
onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
//UUID搜索服务成功回调
onServicesDiscovered(BluetoothGatt gatt, int status)
//读取数据
//status:成功:BluetoothGatt.GATT_SUCCESS
onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
//写入数据
//status:成功:BluetoothGatt.GATT_SUCCESS
onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
//收到数据
onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
三.初始化
对于初始化,我们会分为两部分,第一部分是给搜索做准备的,第二部分是给连接做准备的,我们先来看下搜索的初始化,如下代码:
我们首先判断是否支持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是模块的:
//模块的UUID
public 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新课核心知识点如下:
Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固
手把手完成商业级社交App开发进阶Android高级工程师
如果觉得文章不错,点个赞呗~ ❥(^_-)
共同学习,写下你的评论
评论加载中...
作者其他优质文章