iOS蓝牙4.0 BLE开发总结
关于具体如何开发 iOS 蓝牙,推荐如下一些博客。
Core Bluetooth框架之一:Central与Peripheral
为什么选择蓝牙技术,目前主流三种无线技术(WIFI,蓝牙,Zigbee)对比
要了解一项技术,最好先了解一下历史。1994年,爱立信一位工程师,为了解除日益增多的连接线缆的烦恼,发明了一种名为蓝牙的短距离无线通信技术。自2001年发布蓝牙1.1版本技术规范,一群诺基亚的工程师就开始畅想,在若干年后,如何让智能移动设备与周边的嵌入式设备连接的时间更长,从而使得智能移动设备成为与周边10m范围内各种电子产品通信的中枢。经过10年的努力,蓝牙技术联盟终于在2011年发布了里程碑版本的蓝牙4.0技术规范,其中最具吸引力的就是低功耗蓝牙技术规范。
在无线技术迅猛发展的今天 ,蓝牙技术到底是一个什么位置,可以通过下图做一个简单的了解。
[图片上传失败...(image-445440-1645415989275)]
目前市场上影响力最大的三种无线技术,蓝牙,WIFI和Zigbee。
[图片上传失败...(image-6a3f3e-1645415989275)]
为什么要选择蓝牙,不用 WIFI(尤其是一些可穿戴设备),很关键的原因是因为蓝牙成本比 WIFI 低,而且非常省电,相信你并不期望你的手环要每天充电一次吧,那蓝牙传输速度慢怎么办呢,对于一些可穿戴设备而言,监控个心跳,看看每天走了多少步,这个有多少大数据要去传输呢?再说蓝牙传输速度也还好,慢是 iOS 系统限制的问题,不是蓝牙标准本身的问题,下文会说明。
什么是蓝牙4.0
蓝牙4.0是蓝牙3.0+HS规范的补充,专门面向对成本和功耗都有较高要求的无线方案,可广泛用于卫生保健、体育健身、家庭娱乐、安全保障等诸多领域。
它支持两种部署方式:双模式和单模式。双模式中,低功耗蓝牙功能集成在现有的经典蓝牙控制器中,或再在现有经典蓝牙技术(2.1+EDR/3.0+HS)芯片上增加低功耗堆栈,整体架构基本不变,因此成本增加有限。
单模式面向高度集成、紧凑的设备,使用一个轻量级连接层(Link Layer)提供超低功耗的待机模式操作、简单设备恢复和可靠的点对多点数据传输,还能让联网传感器在蓝牙传输中安排好低功耗蓝牙流量的次序,同时还有高级节能和安全加密连接。
并不是所有手机都支持蓝牙4.0技术,iPhone需要4S以上的手机,安卓需要系统4.3及以上的版本。
蓝牙4.0的三种模式
[图片上传失败...(image-f36469-1645415989275)]
从上图我们可以发现,全新的蓝牙4.0技术并不是一种技术,而是由传统蓝牙,高速蓝牙和低功耗蓝牙合而为一的一种标准。并且这三种蓝牙可以组合使用,也可以单独使用。
其中,低功耗蓝牙即BLE是蓝牙4.0的核心规范。毫不夸张的说,蓝牙4.0为什么可以成为改变生活的原因,就是因为BLE,低功耗蓝牙的产生。因为一切智能设备,都需要电,电量用完,设备就是一堆废铁。
然而,低功耗是硬件上的事情,跟我们手机 APP 有什么关系,APP 根本就不关心你耗电不耗电,我只写我的客户端就好了啊。实际上,与 APP 还是有关系的,正是因为有 BLE 和传统蓝牙,市场上是有三类蓝牙产品的。如果你不知道你在为哪一类产品开发程序,你会遇到很多麻烦。
比如说,要你用 BLE 传输音频数据,如果你不知道传音频是传统蓝牙的功能,你可能会白辛苦很久而达不到效果。
再举个例子,如果要你监控来电短信或者邮件等其他信息,然后通过 APP 发送到蓝牙设备。如果你不知道这是 ANCS(Apple Notification Center Service)的工作(该工作需要在蓝牙外设上直接根据蓝牙协议来实现,没 APP 什么事情),而自己傻傻的去找检测来电短信的办法,然后还要在程序退到后台依然能起作用,并通过 BLE 发送到设备,那可能你的工作中会充满烦恼。
所以,作为 iOS 工程师,第一件事情,就是要区分你正在开发的 APP 是为单模蓝牙,还是双模蓝牙;仅传统蓝牙是不需要 APP 的,譬如市面上大多数的蓝牙音箱,只要连上电脑或者手机,在电脑或者手机设备上播放音乐,系统底层就可以通过传统蓝牙协议,将音频数据传到蓝牙音箱上播放(这里并没有 APP 什么事情)。
总的来说,就是传统蓝牙部分在APP上不可控,在 iOS 上,更是连判断在设置中是否连接了传统蓝牙都办不到,iOS 可控的部分,系统提供接口去给工程师开发 APP 的部分,只有 BLE 部分(这才是大家在iPhone上使用蓝牙,觉得速度慢的原因,因为iOS系统根本没开放高速蓝牙和传统蓝牙)。
那为什么还要区分单模,双模呢,知道又如何,传统蓝牙部分都无法控制,原因在哪?答案在于这里说的不可控,是指在 iOS 上限制了,但是在 iPhone 硬件上是可以知道的(所以 iPhone 越狱之后,会有一些第三方插件如AIRBLUE SHARING,可以支持两个手机之间用蓝牙传输文件,速度也不慢)。
关于 iPhone 蓝牙开发注意事项
1)iOS 系统本身只开放了 蓝牙 BLE 部分,而且蓝牙 BLE 并不是用来给你传输文件的,指望使用 iOS 蓝牙开发两个手机之间能快速传输文件这种 APP 的同学就算了。
这点你要深切的理解,苹果之所以给他们装上蓝牙不过是让你练练蓝牙耳机啊,蓝牙音箱,玩玩可穿戴设备之类的。为什么苹果要这样限制呢?因为早期非越狱苹果上,用户听音乐都是要从商店花钱购买的,如果你用蓝牙把歌曲传给别人,那不是侵权了么,所以,苹果在硬件上面就把你传输文件给限制了。
当然,也不是完全没办法,越狱安装第三方蓝牙插件,就可以使用蓝牙4.0所有功能,传输文件当然也没压力,甚至 iPhone 和 Android 之间都能配对并且传输文件。
那不越狱,要开发传输文件的功能呢?也能传输,无非是速度奇慢无比,实测只有 1-2k/s,所以要传也只能传输很小的文件,想传输一首 mp3,就算了吧。(有没有私有蓝牙 Api 可以调用的呢,应该有吧,没研究过,被拒风险大)
2)iPhone 手机最多能同时连接多少蓝牙设备
官方文档,以及蓝牙底层协议,说明理论上可以支持到同时连接 7 个,但这 7 个能同时正常工作么?不得而知,毕竟对于 iOS 而言,蓝牙也是一种资源,同时连接和同时使用消耗,占用的资源肯定不同,而且不同手机,性能也不同,说不定 iPhone 5 同时能正常使用 3 个连接,iPhone 6s 能同时支持 5 个,这个需要进一步实际测试。
3)开发完了蓝牙 APP,有什么工具可以协助蓝牙测试
Xcode 模拟器并不能来测试蓝牙,蓝牙需要在真机上调测。
如果你开发的蓝牙 APP 是作为设备端(CBPeripheral),那很幸运,在 Mac 电脑上可以安装一款叫做 LightBlue 的应用,该应用作为中心端,扫描周边设备,同时扫描设备的服务和特征,可以读取和发送任意数据来进行交互测试(注意这里,可以发送任意数据,这样对调测而言是非常方便的)。这样的话,你只需要你的 iPhone 手机装上你的 APP,你的 Mac 电脑装上 LightBlue 软件,你的电脑和手机之间就可以进行测试,一个手机足够。
如果你开发的蓝牙 APP 是作为中心端(CBCentral),那很不幸(实际上大部分蓝牙 APP 都是作为中心端来使用),至少我目前没有发现哪个 APP 可以模拟设备端来提供测试(后续有谁发现了可以分享下)。这时候你只能拿事先准备好的蓝牙外设去对接调测,但这个多少有些麻烦,比如你无法抓包去判断双方通信的协议(可能需要比较洋气的专业蓝牙测试仪器),发送的数据包是否正确,调测比较慢,短时间无法判断是哪里没对接成功。当然你自己开发一个 APP 作为设备端,提供服务,供另外一个手机来调测,这样的话,设备端 APP 可以实时打印收到的消息格式,错误等问题,调测更直观,当然这时候至少需要两个手机才能完成测试。
4)虽然 iOS 系统限制了只能使用蓝牙 BLE,但蓝牙 BLE 的传输速率到底如何呢
- 目前用一个 iPhone 4s(iOS 7.1.2)和一个 iPhone 5(iOS 9.2) 之间做一个文件传输测试,发现平均速率只有 1 k/s,相当缓慢
- 如果用一个 iPhone 6(iOS 9.2) 和一个 iPhone 5(iOS 9.2) 之间做一个文件传输测试,发现平均速率只有 1.8 k/s,相对于上面而言略有提升
该文件传输测试,是在一个特征值上重复写数据来测试了,可能有人会想到如果同时写多个特征值呢,速度会不会提升?答案是不会,同时写 2 个特征值,原有每个特征值的速率会减半,也就是总的速率并不会改变。
5)关于 iOS 上蓝牙 BLE 一次读写(注意是一次读写,不是一共读写)最大能读写多少个字节的疑问
这个在网上搜了一些资料,发现没有一个统一的说法,说法都有些偏差,不知道是不是因为对接了不同的蓝牙外设带来的差异,此处给出一个用两个 iPhone 手机测试,一个做中心端,一个做设备端得到的结论,仅供参考。
目前用一个 iPhone 4s(iOS 7.1.2)和一个 iPhone 5(iOS 9.2) 之间做一个一次读写最大字节数的测试:
- 在 iOS 7 上,一次读的数据最大只能读到 134 个字节(包括 134),大于 134 的,系统直接截断,导致数据丢失
- 在 iOS 9 上,一次读的最大数据只能读到 628 个字节(包括 628),大于 628 的,系统直接截断,导致数据丢失
- 如果是 iOS 9 和 iOS 7 蓝牙互通,iOS 9 上读取数据范围在(156,628)之间的,iOS 7 系统会自己拆分为 157 字节一个包后,再发往 iOS 9 这边,所以 iOS 7 这边会收到多次读请求,但请求里的 offset 不同,所以 iOS 7 这边需要根据 offset 来计算偏移并多次发送数据,iOS 9 那边并不会多次收到读数据回调,只会收到一次回调
- 在 iOS 7 上,一次写的数据最大只能写 155 个字节(包括 155),大于 155 的,系统直接返回错误 Unknown error
- 在 iOS 9 上,一次写的最大数据只能写 512 个字节(包括 512),大于 512 的,系统直接返回错误 Unknown error
- 如果是 iOS 9 和 iOS 7 蓝牙互通,iOS 9 上发送数据范围在(155,512)之间的,iOS 7 系统收到之后会自己拆分为 153 字节一个包再报给上层,所以 iOS 7 这边会收到多次写请求,但请求里的 offset 不同,iOS 7 这边需要根据 offset 来合并数据
6)开发 iOS 蓝牙设备端(CBPeripheral)过程中遇到的问题
相对于开发中心端,从 CBPeripheralManager 相关接口来看,作为设备端,以下几种场景 iOS 并没有提供接口(作为中心端都可以实现)
- 设备端无法知道当前有中心端连接上来了,除非中心端发起订阅,读写请求,才知道有中心端连上来
- 设备端不知道中心端断开,如果是中心端主动发起断开,设备端无法得知
- 设备端不能主动断开与中心端的连接
对于以上几种情况,iOS 没有提供相关的接口来处理,如果是作为设备端,此时要维护和保存一些中心端相关的数据,但又不知道中心端已经断开的情况下,可能会导致设备端资源无法释放和保存,状态也没办法同步。
在系统没有提供接口的情况下,如果要实现该需求,可以参考以下的思路(底层没有接口,只能在上层数据包里自己设计协议来处理)
- 设备端和中心端约定某个可以订阅的特征,中心端连接成功就订阅这个特征,通知设备端中心端连接上来,断开之前取消订阅这个特征
- 不采用订阅特征,也可以采用读或者写某个特征,来通知设备端连上以及断开
7)作为中心端,连接上蓝牙设备之后,如何获取蓝牙设备的 MAC 地址
据说安卓有系统接口直接可以获取蓝牙外设的 MAC 地址,但很遗憾,iOS 没有提供这样的机制,如果要获取外设的 MAC 地址,有两种做法(都需要和外设硬件工程师商量方案):
- 在蓝牙外设广播里,提供 MAC 地址,这样中心端在扫描外设的时候,可以直接读取该广播上的值,从而拿到 MAC 地址
- 可以在外设的某个服务的特征里,提供 MAC 的地址的读取,但前提是要先协商好读取哪个特征,UUID是多少
8)为什么两个 iPhone 手机都打开蓝牙之后,还是相互搜不到对方呢
两台设备,要相互知道对方,唯一的办法,只能是至少有一个人告诉对方,我是谁。如果两台设备都不说话,那肯定不知道对方是蓝牙设备的身份。所以,蓝牙连接的过程,实际上就是一个沟通的过程,在蓝牙设备中,说话是通过广播一段无线信号实现,一台蓝牙设备是不能同时广播和接收广播,因为目前芯片还没那么强大。
在蓝牙通信中,分为中心端和设备端。而通常手机都处在中心端状态,也就是只能接收广播,而自己没有向周围发送广播。所以两台手机之间一般是无法发现对方的(因为大家都是中心端)。除非,你写一段程序,让手机向周围发送广播。
目前,作为中心端的设备,是可以与多个设备端进行连接的,而作为设备端的设备,通常只能与一个中心端进行连接,通常在与一个中心端相连后,设备就会关闭广播。
所以,在开发过程中,可能你的身边有多个蓝牙设备处于运行状态,但是一个也搜索不到,那就是因为别人的手机已经连接了。然而,在蓝牙设备上,并不是在连接之后就必须关闭广播,是可以在芯片上实现让他继续广播的,也就是其它中心端还是可以搜到这个设备,这个取决于应用场景。