1、传统蓝牙的传输距离几十米到几百米不等,BLE 则规定为 100 米(实际上没有那么远,50米以内比较稳定,和设备发射功率有关)
2、为了实现极低的功耗,BLE 协议设计为:在不必要射频的时候,彻底将空中射频关断(可以在需要的时候快速建立连接进行控制操作)。与传统蓝牙 BR\EDR 相比,BLE 有这三大特性,从而实现低功耗:缩短无线开启间、
快速建立连接、降低收发峰值功耗(具体由芯片决定)。
3、缩短无线开启时间的第一个技巧是只用 3 个“广告”信道(其余信道用于数据传输),第二个技巧是通过优化协议栈来降低工作周期。一个在广告的设备可以自动和一个在搜索的设备快速建立连接,所以可以在 3ms 内完成连接的建立和数据的传输。(实际上首次连接时并没有那么快,因为要进行一些初始化配置)
4、低功耗的设计会带来一些牺牲,例如:音频数据就无法通过 BLE 来进行传输。BLE 仍然是一种非常鲁棒的技术。它依然支持跳频(37 个数据信道),并且采用了一种改进的 GFSK调制方法来提高链路的稳定性。BLE 也仍是非常安全的技术,因为在芯片级提供了 128 bit AES加密(做应用层开发的安全性方面就省去了许多工作量)。
5、安卓的 BLE 标准在 2013 年 7 月 24 日发布,一般搭配Android 4.3 及以上系统的手机都是支持牙 4.0(BLE)的。智能机和平板会带双模蓝牙的基带和协议栈,协议栈中包括 GATT 及以下的所有部分,但是没有 GATT 之上的具体协议。所以,这些具体的协议需要在应用程序中实现,实现时需要基于各个 GATT API 集。这样有利于在智能机端简单地实现具体协议,也可以在智能机端简单地开发出一套基于 GATT 的私有协议。
6、BLE协议栈
PHY层:1Mbps自适应跳频GFSK(高斯频移键控),运行在免证的2.4GHz频段。
连接层 (LL) ) : 控制设备的状态。设备可能有 5 种状态:就绪(standby),广播(advertising),搜索(scanning),初始化(initiating)和连接(connected)。一个处于连接状态的设备会有一个角色:
master 和 slave。初始化这个连接的为 master,接受这个连接请求的为 slave(一般手机或平板作为 master)。
主机控制层 (HCI) ): 为 host 和 controller 之间通过一个标准接口进行通信提供了一些方法。这一层可以通过一个软件 API 或者是硬件接口如 UART,SPI 和 USB来实现设备控制。
逻辑链路控制与适配协议 L2CAP层:为上层提供数据封装服务,允许逻辑上的端到端数据通信。
安全管理层 SM:提供配对和密匙分发服务,实现安全连接和数据交换。
通用访问配置文 件GAP层:直接与应用程序或配置文件(profiles)通信的接口,处理设备发现和连接相关服务。另外还处理安全特性的初始化。
属性协议层(ATT) ): ATT 协议允许一个设备去显示一些数据,对于其他设备称之为“Attribute属性”,在 ATT 中,那些显示这些属性的设备被称为 server,同等的另一个设备称为 client。LL 层的状态 master 和 slave 和 ATT 层的这两个状态无关。
通用属性配置文件 (GATT) ): 是一个服务框架,定义了 ATT 应用的子程序。GATT 指定了profile的结构。在BLE中,由profile或者是服务所使用的所有类型的数据都称为characteristic。发生于两个设备间通过 BLE 连接进行交换的数据都需经过 GATT 子程序处理。因此,app 和
profile 会直接使用 GATT。
7、通用属性配置文件(GATT)
GATT 定义了两个角色:服务器和客户端。GATT 的角色并不一定与特定的 GAP 角色有关联,但可能由更高层级的配置文件指定。GATT 和 ATT 不是传输专用,也可以用于 BR/EDR 和低耗能。但是,由于 GATT 和 ATT 用作发现服务,故必须在低耗能技术中实施。
8、GATT 配置文件层级
该层级的最高层是配置文件。配置文件由实现用例所需的一个或多个服务组成。服务由特征或有关其它服务的引用组成。各项特征包括一个值,还可能包括有关该值的可选信息。服务、特征以及特征的组件(即值和描述符)包含配置文件数据,并全部存储在服务器的属性中。
9、设备角色划分
在 BLE 协议中,有两个角色,周边/外围( Periphery )和中央( Central) ;周边是数据提供者,中央是数据使用/处理者;在 iOS SDK 里面,可以把一个 iOS 设备作为一个周边,也可以作为一个中央;但是在 Android SDK 里面,直到目前最新的 Android4.4.2,Android 手机只能作为中央来使用和处理数据。
一个中央可以同时连接多个周边,但是一个周边某一时刻只能连接一个中央 (Central)BluetoothGattServer 作为周边来提供数据;BluetoothGattServerCallback 返回周边的状态。BluetoothGatt 作为中央来使用和处理数据;BluetoothGattCallback 返回中央的状态和周边提供的数据。一个 Ble 设备某一时刻只能扮演一种角色。
每一个周边 BluetoothGattServer, 包含多个服务 Service, 每一个 Service 包含多个特征 Characteristic ,每一个 Characteristic 又包含多个Descriptor 。
10、为了获取中央 BluetoothGatt, 大致需要如下过程:
获取 BluetoothManager:mBluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
得到 BluetoothAdapter:mBluetoothAdapter =
mBluetoothManager.getAdapter();
扫描 Ble 设备: mBluetoothAdapter.startLeScan( BluetoothAdapter.
LeScanCallback);
从 LeScanCallback 中 得 到 BluetoothDevice : public void
onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{.....}
用 BluetoothDevice 得 到 BluetoothGatt : gatt =
device.connectGatt(this, true, gattCallback);
获取到 BluetoothGatt, 通过调用 BluetoothGatt 的方法,就可以通过
BluetoothGattCallback 和周边 BluetoothGattServer 进行交互。
App 实现过程
Android 官网上有BLE Sample,故而不再粘贴其中代码。
1、 首先检测设备是否支持 BLE,若支持,开启蓝牙并获得初始化蓝牙适配器:
2、在确保 ble 可用及蓝牙已打开后,开始扫描外围设备。扫描过程由回调接口监控着。一旦发现外围 ble 设备,便将其加入 ble 设备列表中。
3、点击搜索出的某一设备后,将尝试与其进行连接。同时将设备的地址放入广播中,最终作为入参传递给 BluetoothAdapter.getRemoteDevice,以获取外围设备 device。
4、成功连接到外围设备后,会返回一个 BluetoothDevice device ,此返回值即代表与手机连接的外围设备。通过利用该外围设备的 connectGatt(Context context,boolean autoConnect, BluetoothGattCallback callback) 方法,可以获取到BluetoothGatt 实例, BluetoothGatt 类是与蓝牙 GATT 配置文件相关的公用 API,它提供了蓝牙 GATT 功能,确保与 BLE 设备的通讯,可以通过它来管理客户端的操作(重连/断开、发现/获取服务、读/写特征及描述等等)。 BluetoothGattCallback 作为连接过程中的回调接口,响应连接过程中的各种状态变化(连接状态的变化、发现服务、读写特征( Characteristic )、读写描述( Descriptor )、特征的 Value 发生变化),体现了观察者设计模式的应用。
5、 连接成功之后, BluetoothGattCallback 中的 onConnectionStateChange 即被触发,所以我们可以在该函数中使用获取到的 BluetoothGatt 实例来进行操作。连接建立之后的第一件事可能就是搜索外围设备所提供的可用服务,所以我们可以使用BluetoothGatt 实例的 discoverServices()方法,与此同时,一旦服务被发现,onServicesDiscovered 将被触发。
6、连接过程中,如果连接状态发生变化,也会自动触发onConnectionStateChange(BluetoothGatt gatt, int status,int newState),所以我们可以在该函数中判断新状态的类型,然后执行相应操作。如果连接断开了,我们只需要在该函数中,再次添加上connectBle(mBluetoothDeviceAddress)便会自动尝试连接了。
7、 在连接状态下,如果想读取一个Characteristic的值,必须先调用BluetoothGatt的readCharacteristic(characteristic)方法,而后触发onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, intstatus)函数,在此函数中去调用characteristic. getValue()方法才可以获取到Characteristic的Value。
8、一般获取到的Value是byte数组,如何解析此Value格式要看你和开发外围Ble设备的硬件工程师之间的约定了。底层传递过来的数据类型,是八进制或十六进制抑或其他类型。根据数据类型,结合 Value 的构成,即可解析出自己需要的字段。