20221217-低功耗蓝牙BLE学习笔记

http://doc.iotxx.com/BLE%E6%8A%80%E6%9C%AF%E6%8F%AD%E7%A7%98

蓝牙4.0 版本分两种模式,单模蓝牙 和双模蓝牙

常见的蓝牙音箱,是典型的双模蓝牙,它需要传输大量的音频数据,而小米手环,蓝牙温度计则属于单模蓝牙,行业里一般不讲单模蓝牙,而是统一称为低功耗蓝牙

蓝牙5.0 已经发布和应用,4倍通信速度,2倍的通信距离以及Mesh 组网特性,将使蓝牙成为物联网领域的重要的技术之一

  1. 蓝牙简介

蓝牙是一种近距离无线通信技术,运行在2.4GHz免费频段,目前已大量应用于各种移动终端,物联网,健康医疗,智能家居等行业。蓝牙4.0以后的版本分为两种模式,单模蓝牙和双模蓝牙。

单模蓝牙 即低功耗蓝牙模式,是蓝牙4.0 中的重点技术,低功耗,块连接,长距离

双模蓝牙,支持低功耗蓝牙的同时还兼容经典蓝牙,经典蓝牙的特点是大数据高速率,例如音频、视频等数据传输。

经典蓝牙支持音频(HFP/HSP, A2DP)和数据(SPP, HID等)两大类协议,在音箱,耳机,汽车电子及传统数传行业,由于苹果对经典蓝牙数据传输接口有限制(需要过MFI认证),加上功耗偏大,因此在目前移动互联应用中慢慢地被边缘化。因此低功耗蓝牙顺势而出,由于可支持苹果4S以上及安卓4.3系统以上的数据传输,且功耗极低,目前正在被越来越多的移动互联设备所采用,但低功耗蓝牙不支持音频协议,并且受数据传输速度限制,其应用也被限制在小数据传输行业。

蓝牙双模则是综合了两者的优缺点,既可以支持音频传输,同样可支持数据传输,并且兼容性也是两者之和,在对功耗要求不苛刻的情况下,是比较理想的选择

2 BLE特点

低功耗蓝牙瞄准多个市场,特别是移动智能终端,智能家居,互联设备等领域,主要特点包括

低功耗
快连接
远距离

为低功耗蓝牙定义了一些标准Profile ,Profile 理解为数据规范,只要遵守该规范,任意厂家的蓝牙设备,均可以相互连接与通信,例如无线蓝牙键盘鼠标,无论是安卓或是iOS还是Windows,均是即插即用,这便是“标准”的力量。低功耗蓝牙支持的标准Profile有:

HID 用于无线鼠标,键盘或其他遥控设备

BatteryServices 电池状态服务,用于告知电池电量状态

HRP 心率计,Profile 用于心率 采集,等等

另外,低功耗蓝牙还可以自定义Profile ,伴随着智能手机的发展和普及,低功耗蓝牙的这个特性得到了发扬光大,同时也拓宽了低功耗蓝牙的应用领域。例如,可以自定义一个开关量的Profile,数据01表示开灯,数据00表示关灯,然后手机发送数据01和00就可以控制灯的亮和灭。类似的应用案例有很多,下面总结应用特点

支持自定义Profile ,可以收发任意格式的数据,如01 和 00
支持自定义设备,支持任意设备的连接和通信,例如 只能蓝牙插座等等

提示:低功耗蓝牙的Profile均基于GATT(通用属性规范,后面会详解)之上,如HID over GATT。也就是说,经典蓝牙中的HID规范与低功耗蓝牙中的HID规范用的是两个不同的通道。

BLE 工作流程
为后面的低功耗蓝牙协议的学习准备基础

角色

BLE 设备角色主要分为两种角色,主机和从机,,当主机和从机建立连接之后才能相互收发数据

主机,主机可以发起对从机的扫描连接,例如手机,通常作为BLE 的主机设备

从机,从机只能广播并等待主机的连接,例如智能手环,是作为BLE 的从机设备

另外还有观察者和广播者,这两种角色不常使用,但也十分有用,例如iBeacon 就可以使用广播者 角色来做,只需要广播特定内容即可

观察者,观察者角色监听空中的广播事件,和主机的区别是不能发起连接,只能持续扫描从机

广播者 广播者可以持续广播信息,和从机的唯一区别是不能被主机连接,只能广播数据

蓝牙协议栈没呀限制设备的角色范围,同一个BLE 设备,可以作为主机,也可以作为从机,我们称之为主从一体,主从一体的好处是,每个BLE 设备都是对等的,可以发起连接,也可以被别人连接,更加实用

广播

广播是指从机没经过一个时间间隔发送一次广播数据包,这个时间间隔称为广播间隔,这个广播动作叫做广播事件,只有当从机处于广播状态时,主机从能发现该从机

在每个广播事件中,广播包会分别在37 38 和 39 三个信道上依次广播,如下所示

广播时间间隔的范围是从20ms到10.24s,广播间隔影响建立连接的时间。广播间隔越大,连接的时间越长。

另外BLE 链路层会在两个广播事件之间添加0-10 ms 的随机演示,保证多个设备广播时,不会一直碰撞广播,也就是说,设置100ms 的广播间隔,实际上两次广播事件的时间间隔可能是100-110 ms 之间的任意时间

广播数据包最多能携带31个字节的数据,一般包含可读的设备名称,设备是否可连接等信息

当主机收到从机广播的数据包后,它可以再发送获取更多数据包的请求,这个时候从机将广播 扫描回应 数据包,扫描回应数据包和广播包一样,可以携带31个字节的数据

提示:蓝牙4.x,广播有效载荷最多是31个字节。而在蓝牙5.0中,通过添加额外的广播信道和新的广播PDU,将有效载荷增加到了255个字节

  1. 扫描

扫描是主机监听从机广播数据包和发送扫描请求的过程,主机通过扫描,可以获取到从机的广播包以及扫描回应数据包,主机可以对已扫描到的从机设备发起连接请求,从而连接从机设备并通信

扫描动作由两个比较重要的时间参数,扫描窗口 和扫描间隔,如果扫描窗口等于扫描间隔,那么主机将一直处于扫描状态中,持续监听从机广播包

被动扫描,主机监听广播信道的数据,当接收到广播包时,协议栈将向上层 也就是应用层,用户可编程 传递广播包

主动扫描,主动扫描除了完成被动扫描的动作外,还会向从机发送一个扫描请求,从机收到该请求时,会再次发送一个称作 扫描回应 的广播包

主动扫描比被动扫描,可以多收到扫描回应数据包

连接

在BLE 连接中,使用调频方案,两个设备在特定时间、特定频道上彼此发送和接收数据,这些设备稍后在新的通道(协议栈的链路层处理通道切换)上通过这个约定的时间相遇,这次用于收发数据的相遇称为连接事件,如果没有有发送或接收的应用数据,则交换链路层数据来维护连接,两个连接事件之间的时间跨度称为连接间隔,是以1.25ms 为单位,范围从最小值 7.5 ms 到最大值 4.0s

连接参数

Connection Interval 连接间隔,两次连接事件之间的时间间隔称为连接间隔,1.25ms 为单位,范围从最小值 7.5 ms 到最大值 4.0s

slave latency 从机延迟,如果从机没有要发送的数据,则可以跳过连接事件,继续保持睡眠节省电量

supervision Time-out 监控超时,是两次成功连接事件之间的最长时间,如果在此时间内没有成功的连接事件,设备将终止连接并返回到未连接状态,该参数值以10ms 为单位,监控超时值可以从最小值10 到3200 超时必须大于有效的连接间隔

连接参数更新请求

连接参数由主机发起连接的时候提供,如果从机对连接参数有自己的要求,例如要求更低的功耗,或者更高的通信速率等,从机可以向主机发送连接参数更新请求

从机可以在连接后的任何时候发起连接参数更新请求,但最好不要在主从建立连接后like发起,建议延迟5s 左右再发送请求

连接参数更新请求可以修改:Connection Interval连接间隔,Slave Latency从机延迟,Supervision Time-out监控超时。

有效连接间隔

effective connection interval 有效连接间隔等于两个连接事件之间的时间跨度,假设从机跳过最大数量的连接事件,且允许从机延迟,

从机延迟表示可以跳过的最大事件数。该数字的范围可以从最小值0(意味着不能跳过连接事件)到最大值499。最大值不能使有效连接间隔(见下列公式)大于16秒。间隔可以使用以下公式计算:

Effective Connection Interval = (Connection Interval) × (1 + [Slave Latency])

Consider the following example:

Connection Interval: 80 (100 ms)
Slave Latency: 4
Effective Connection Interval: (100 ms) × (1 + 4) = 500 ms

当没有数据从从机发送到主机时,从机每500ms 一个连接事件交互一次

ios 对连接参数的要求
不同的平台对有连接间隔有着不同的要求,例如ios 系统对ble 的连接间隔有着如下的要求

Interval Max * (Slave Latency + 1) <=2s
Interval Min >=20ms
Interval Min + 20 ms <= Interval Max
Slave Latency <= 4
SupervisionTimeout <= 6 s
Interval Max * ( Slave Latency + 1) * 3 < SupervisionTimeout

连接参数的优化考量

在许多应用中,从机跳过最大连接事件数,选择正确的连接参数组在低功耗蓝牙设备的功率优化中起重要作用,以下列表给出了连接参数设置中权衡的总体概述

减少连接间隔如下

增加两个设备的功耗
增加双向吞吐量
减少任一方向发送数据的时间

增加连接间隔如下
降低两个设备的功耗
降低双向吞吐量
增加任一方向发送数据的时间

减少从机延迟 或将其设置为零 如下
增加外围设备的功耗
减少外围设备接收从中央设备发送的数据的时间

增加从机延迟如下
在周边没有数据发送期间,可以降低外设的功耗到主机设备
增加外设设备接收从机设备发送的数据的时间

通信

通俗的说,我们将从机具有的数据或者属性特征,称之为profile 可以翻译为配置文件

从机中添加profile 配置文件,作为GATT 的server 端,主机作为GATT 的client 端

profile 包含一个或者多个service 每个service 又包含一个或者多个characteristic 主机可以发现和获取从机的service 和characteristic 然后与之通信,characteristic 是主从通信的最小单元

主机可主动向从机write 写入或read 读取数据

从机可主动向主机notify 通知数据

注意,这里引用了服务 service 和特征值 characteristic 的概念,每个服务和特征值都有自己的唯一标识uuid 标准UUID 为128位,蓝牙协议栈中一般采用16位,也就是两个字节的UUID 格式

一个从机设备包括一个或者多个服务,一个服务中又可以包括一条或者多条特征值,每个特征值都有自己的属性 property 属性的取值有 可读 read 可写 write 以及通知 notify

可读可写的字面意思容易理解,表示该特征值可以被主机读取和写入数据

而通知则表示从机可以主动向主机发送通知数据,这便是主从机之间两个典型的通信方式

下图是一个典型的从机设备,该从机包含有一个profile 两个service 和五个characteristic 我们先来介绍这些特征值的作用,然后介绍如何通过特征值通信

服务0x180A

180A 是蓝牙协议里标准的服务uuid,用来描述设备信息device information,可以通过该服务,来提供从机设备的相关说明,例如硬件版本,软件版本,序列号等信息,这样,主机就可以获取从机的设备信息,上图中我们添加了三个提供具体设备信息的特征值,他们分别是

特征值0x2A24,描述产品型号 Model Number String,例如某智能锁的产品型号为:“DSL-C07”。
特征值0x2A25,描述产品序列号 Serial Number String,例如某智能锁的产品序列号为:“lkjl0016190502500269”
特征值0x2A26,描述产品固件版本号 Firmaware Revision String,例如某智能锁的固件号为:“2.7.2.0”

上述特征值仅有read 属性,因此主机只能读,不能执行写操作

服务0xfff0

FFF0 是我们自定义的服务uuid,它包含两个特征值,用来发送和接收数据

特征值0xfff1 自定义的数据发送通道,具有read 和write 属性,主机可以通过该特征值,向从机发送数据,至于发送的数据的最大长度,可以在profile 中配置
特征值0xfff2 ,自定义的数据接收通道,具有notify 属性,从机可以通过该特征值,主动向主机发送数据

假设主机写特征值的协议栈函数原型为 int GATT_WriteCharValue(uuid_t UUID, uint8 *pValue, uint8 len)

假设从机发送通知的协议栈函数原型为 int GATT_Notification(uuid_t UUID, uint8 *pValue, uint8 len)

那么主机向从机发送Hello,可以这样调用协议栈的函数:GATT_WriteCharValue(0xFFF1,”Hello”,5)

那么从机向主机发送1234,可以这样调用协议栈的函数:GATT_Notification(0xFFF2,”1234”,4)

断开

主机或从机都可以发起断开连接请求,对方会收到该请求,然后断开连接恢复连接前的状态

过程演示

现在我们总结一下BLE 的工作流程,使用两个虚拟的ble 硬件来模拟主从机的交互过程

假设有两个ble 设备,使用的是BLE261 低功耗蓝牙模块,假设已经下载了用于交互演示的功能固件,一个是主机,名称为blecentral ,另一个是从机,名称为bleperipheral,如下图所示

步骤1 上电初始化

主机,从机上电后 不分先后顺序,首先进行协议栈初始化和相关功能调用,如下图所示

主机设备,主机初始化时,需要设置设备类型,设置用于扫描的相关参数,初始化 gatt 等协议相关的参数
从机设备,从机初始化时,需要设置设备名称,广播相关参数,从机profile,等,从机一般会立即开启广播,也可以等待一个事件来触发广播,例如按键触发

步骤二 主机扫描从机

按键按下,触发主机扫描从机,此时,主机显示屏打印scanning 正在扫描,此刻的从机仍然处于广播状态

步骤三 发现从机设备

当主机扫描到从机时,可以返回已扫描到的从机相关信息,例如可以提取到下图中的从机设备名称,从机mac 地址,从机的RSSI 信号值等数据,因此,有些应用在从机的广播包或者扫描回应包中添加自定义字段,这样就可以被主机通过扫描的方式拿到数据

步骤四 发送连接请求
当主机扫描到从机后,通过mac 地址向从机发送连接请求,低功耗蓝牙的连接速度非常快,10ms 左右即可成功连接上,如果从机的广播比较大,则会影响连接的速度

从机在未收到连接请求之前仍然处于自由的广播状态

步骤五 成功连接从机

当从机收到连接请求后,双方成功建立连接,此时双方的状态均变为已连接状态

然后主机可以调用协议栈提供的接口函数来获取从机的服务

步骤6 获取从机服务

获取从机服务通常是在连接成功后就立即执行的,因为只有获取从机的服务后,从能与其通信,下图是主机想从机发送获取服务的请求

此刻,从机处于已连接状态,响应服务获取请求是在底层自动完成的,上层无需理会

步骤7 成功获取服务

如下图所示,主机成功获取到从机的服务,例如获取到UUID 为0xfff0的services 该service 有两个特征值,分别是具有读写属性的0xfff1 ,以及具有通知属性的0xfff2

读写属性是指主机可以读写该特征值的内容,而通知属性是指从机可以通过该特征值向主机发送数据

步骤8 主机向从机发送数据

主机通过特征值0xfff1 主动向从机发送自定义数据hello ,当数据成功发送后,主机状态变为:数据已发送,从机将收到主机发来的数据,从机状态变为收到数据

步骤9 从机向主机发送数据

从机可以通过norify 的方式主动向主机发送数据,例如下图,从机通过特征值0xfff2 发送了一条 notify 通知,数据内筒为1234

步骤10 发送断开请求

主机和从机任何一方均可以发起断开连接的请求,对方收到后,状态将变为已断开

步骤11 成功断开连接

从机收到主机发来的断开请求,此刻状态变为已断开

4 BLE 协议栈

BLE 协议栈一般是指芯片厂家,以及Bluetooth SIG 发布的Bluetooth core specification 核心协议的实现的代码固件,并提供函数接口,有芯片内部程序调用,可实现上节BLE 工作流程等相关功能

常见的协议栈有 德州仪器 TI 的ble-stack 和nordic 的softdevice

功能框图

在本节中,我们列举两家典型的蓝牙芯片厂家,TI 和noridc 来深入了解低功耗蓝牙协议栈

下图是TI的CC26系列芯片协议栈结构图,

下图是Nordic的nRF52系列芯片的协议栈结构图。

协议栈结构

从上节的两张协议栈功能框图中可以看出,无论是哪个芯片厂商实现的ble 协议栈,其结构都非常的相似,均三个部分

底层 controller
中层 host
顶层 application

然后每一层又分成若干个子模块,我们现在由下而上,逐层介绍

我们将位于顶层的应用层application 也归到协议栈中描述,其实,应用层application 不属于协议栈,它是用来调用协议栈提供的接口,然后实现蓝牙的功能

控制器 controller

physical layer 简称 PHY,物理层,PHY层用来指定BLE 所用的无线频段,调制解调方式和方法等,phy 层做得好不好,直接决定整个ble 芯片的功耗,灵敏度以及selectivity 等频射指标

link layer 简称 LL,链路层,ll层是整个ble 协议栈的核心,也是ble 协议栈的难点和重点,像nordic 的ble 协议栈能同时支持20个link 连接,就是ll 层的功劳,ll 层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK 如何接收,如何进行重传,以及如何对链路进行管理和控制等等,LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP 或者ATT

host controller interface 简称 HCI,协议栈应用开发中,我们会经常看到HCI 的身影,它对上host 提供controller 的功能接口,所以称作 host controller interface

主控Host

Logical link control adaptation protocol 简称 L2CAP L2CAP 对LL 进行了一次简单封装,LL 只关心传输的数据本身,L2CAP 就要区分加密通道还是普通通道,同时还要对连接间隔进行管理

Attribute Protocol 简称ATT ATT 层用来定义用户命令及命令操作的数据,比如读取某个数据或者写某个数据,BLE 协议栈中,开发者接触最多的就是ATT。BLE 引入了attribute 共年,用来描述一条一条的数据,attribute 除了定义数据,同时定义该数据可以使用的att 命令,因此这一层被称为ATT 层

Security Manager 简称SM SMP 用来管理BLE 连接的加密和安全的,如何保证连接的安全性,同时不影响用户的体验,这些都是SMP要考虑的工作

Generic Access Profile 简称GAP GAP 是对LL 层payload 有效数据包 如何进行解析的两种方式中的一种,而且是最简单的那一种,GAP 简单的对LL payload 进行一些规范和定义,因此gap 能实现的功能极其有限,GAP 目前主要用来进行广播,扫描和发起连接等

Generic Attribute Profile 简称GATT
GATT 用来规范attribute 中的数据内容,并运用group 分组的概念对attribute 进行分类管理,没有GATT BLE 协议栈也能跑,但互联互通就会出现问题,也正是因为有了GATT 和各种各样的应用Profile,BLE 摆脱了zigbee等无线协议的兼容性困境,成了出货量最大的2.4G 无线通信产品

应用application

应用层是用户开发实际蓝牙应用的地方,包含必要的协议栈参数设置,以及各种功能函数的调用,我们分别从蓝牙从机和蓝牙主机两种设备来分析

蓝牙从机

相关硬件和基础服务初始化

设置广播参数,广播数据,广播间隔,扫描回应等参数或者数据

设置profile 添加从机服务,特征是,还有设置回调函数用于接收主机数据等
设置绑定管理参数 可选

启动广播,开始运行

等待相关事件,及事件处理,例如收到主机发来的数据,被链接等

蓝牙主机

相关硬件和基础服务初始化

设置扫描参数

设置连接参数

设置绑定管理参数 可选

启动协议栈,开始运行

等待相关事件,及事件处理,例如扫描事件,从机的notify 事件等

  1. GAP 和GATT

蓝牙协议分为两类结构,控制器 和主机,每个类别都有子类别,这些子类别执行特定的角色,我们将要研究的两个子类别是,通用访问配置文件 GAP 和通用属性配置文件 GATT

GAP是Generic Access Profile的缩写,中文含义是:通用访问配置文件。
GATT是Generic Attribute Profile的缩写,中文含义是:通用属性配置文件。

5.1 GAP 和gatt 区别

区分GAP 和gatt 很重要

GAP 定义了BLE 网络堆栈的一般拓扑

GATT 详细描述了一旦设备建立连接后如何传输属性数据

GATT 特别关注如何根据其描述的规则格式化打包和发送数据,在BLE 网络堆栈中,属性协议,ATT 与GATT 紧密对齐,GATT 直接位于ATT 的顶部,GATT
实际上使用ATT 来描述如何从两个连接的设备交换数据

通用访问配置文件 GAP

BLE 设备可以使用两种机制与外界通信,广播或连接,这些机制受通用访问配置文件 GAP 准则的约束,GAP 定义了启用BLE 的设备如何使其自身可用,以及两个设备如何直接相互通信

建立联系

设备可以通过采用 GAP 中指定的以下角色来加入BLE 网络

A、广播(Broadcasting):这些角色不必显式地相互连接即可传输数据。

广播者(Broadcaster):广播公共数据包的设备,例如可以广播按下按钮的时间。
观察者(Observer):侦听广播者发送的广告包中数据的设备。广播者和观察者之间没有任何连接。/2、/2
B、连接(Connecting):这些角色必须显式连接和握手才能传输数据。这些角色比广播角色更常用。

从机设备(Peripheral): 通过广播,告知其他设备自己的存在,以便主机设备可以建立连接。连接后,从机设备不再向其他主机设备广播数据,而是保持与主机设备的连接。
从机设备功耗低,因为它们只需要定期发送信标即可。主机设备负责开始与从机设备的通信。
手环是BLE外设的一个示例。
主机设备(Central):一种通过侦听广播包来启动与从机设备的连接的设备。主机设备可以连接到许多其他从机设备。
当主机设备要连接时,它将请求连接数据包发送到从机设备。如果从机设备接受来自主机设备的请求,则建立连接。
当您的手机连接到手环时,就是BLE Central设备的一个示例。

连接后

主机设备可以更新连接参数,主机设备通常在设备与其自身之间建立连接参数,只有主机设备能修改连接参数,但是,采集设备可以要求主机设备更改连接参数,及从机发送更新参数请求

从机设备或主机设备可以终止连接,连接可能由于多种原因而终止,例如设备的电池可能耗尽或网络干扰可能导致连接失败,设备还可以主动与对等设备断开连接

通用属性配置文件 GATT

GATT 分为两种类型,注意与从机或主机无关

客户端,客户端可以发送请求给GATT 服务端,客户端可以读写 服务端的属性,通过属性可以通信数据

服务端,服务端是用来存储属性的,每当客户端发送请求时,服务端会响应这些请求

客户端与服务端的关系

一个示例如下,手环采集了心跳信息,希望计算机读取该信息,手环充当服务端并提供信息,手机充当客户端,读取该信息

GAP 和GATT 模型角色基本上彼此独立从机设备或主机设备都可以充当服务端或客户端,这取决于数据的流动方式

在一般的主从机通信时,主机可以通过读写从机的属性,实现接收和发送数据给从机,从机可以通过发送通知的方式实现与主机的通信,因此,一般从机是作为GATT 的服务端,主机作为GATT 的客户端

协议栈分层协作
下面以如何发送一个无线数据包的例子来简单阐述协议栈中各分层的作用和必要性,实际上,协议栈的实现可能更加负责,它需要考虑方方面面的因素

发送数据包

假设有设备A 和设备B 设备A 要把自己的电量状态83 % 0x53 发给设备B,该怎么做呢?作为一个开发者,他希望越简单越好,对他而言,它希望调用一个简单的api 就能完成这件事,比如 send 0x53 实际上我们的BLE 协议栈就是这样设计的,开发者只需调用 send 0x53 就可以把数据发送出去了,其余的事情BLE 协议栈帮你搞定,很多人会想,BLE 协议栈是不是直接在物理层就把0x53 发出去,就如下图所示

这种方式初看起来挺美的,但由于很多细节没有考虑到,实际是不可行的,首先,它没有考虑用哪一个射频信道来进行传输,在不更改API 的情况下,我们只能对协议栈进行分层,为此引入 LL 层,开发者还是调用 send 0x53 send 0x53 再调用 send_LL 0x53 2402M 注 2402M 为信道频率,这里还有一个问题,设备B 怎么知道这个数据包是发给自己的还是其他人的,为此BLE 引入 access address 概念,用来指明接受者身份,其中,0x8e89bed6 这个access address 比较特殊,它表示要发给周边所有设备,即广播,如果你要一对一的进行通信,BLE 协议将其称为连接,即设备A 的数据包只能设备B 接收,同样设备B 的数据包只能设备A 接收,那么就必须生成一个独特的随机access address 标识设备A 和设备B 两者之间的连接

广播方式

我们先来看一下简单的广播情况,这种情况下,我们把设备A 叫advertiser 设备B叫 scanner 或者observer 扫描者,广播状态下设备A 的LL 层api 将变成 send_LL 0x53 2402M 0x8e89bed6 由于设备B 可以同时接收到很多设备的广播,因此数据包还必须包含设备A 的device address 0xE1022AAB753B 以确认该广播包来自设备A 为此send LL 参数需要变成
send_LL(0x53,2402M, 0x8E89BED6, 0xE1022AAB753B)
LL 层还要检查数据的完整性,即数据在传输过程中有没有发生窜改,为此引入CRC24 对数据包进行检验,假设为0xb2c7be 同时为了调制解调电路工作更高效,每一个数据包的最前面会加上1个字节的preamble preamble 一般为0x55 或者0xAA 这样,整个空中包变成

空中包用小端模式表示

上面这个数据包还有如下问题

没有对数据包进行分类组织,设备B 无法找到自己想要的数据0x53 为此,我们需要在access address 之后加入两个字段,LL header 和长度字节,LL header 用来表示数据包的LL 类型,长度字节用来指明payload 的长度

设备B 什么时候开启射频窗口以接收空中数据包,如上图case1所示,当设备A 的数据包在空中传输的时候,设备B 包接收窗口关闭,此时通信将失败,同样对case2 来说,当设备A 没有空中发送数据包时,设备B 把接收窗口打开,此时通信也将失败,只有case3的情况,通信才能成功,即设备A的数据包在空中传输时,设备B 正好打开射频接收窗口,此时通信才能成功,换句话说,LL 层还必须定义通信时序

当设备B 拿到数据0x53 后,该如何解析这个数据呢? 它到底表示湿度还是电量,还是别的意思,这个就是GAP 层要做的工作,GAP 层引入了LTV length type value 结构来定义数据,比如020105
02 长度 01 类型,强制字段,表示广播flag ,广播包必须包含该字段,05 值,由于广播包最大只能为31 个字节,它能定义的数据类型极其有限,像这里说的电量,gap 就没有定义,因此要通过广播方式把电量数据发出去,只能使用供应商自定义数据类型 0xff 及04ff590053 其中04 表示长度,ff 表示数据类型,自定义数据,0x0059 是供应商 id 自定义数据中的强制字段,0x53 就是我们的数据,设备双方约定0x53 就是表示电量,而不是其他意思

AAD6BE898E600E3B75AB2A02E102010504FF5900538EC7B2

AA 前导帧
D6BE898E – 访问地址(access address)
60 – LL帧头字段(LL header)
0E – 有效数据包长度(payload length)
3B75AB2A02E1 – 广播者设备地址(advertiser address)
02010504FF590053 – 广播数据
8EC7B2 – CRC24值

有了PHY LL 和gap,就可以发送广播包了,但广播包携带的信息极其有限,而且还有如下几大限制

无法进行一对一双向通信,广播是一对多通信,而且是单方向的通信

由于不支持组包和拆包,因此无法传输大数据

通信不可靠及效率低下,广播信道不能太多,否则将导致扫描端效率低下,为止 ble 只使用37 2402MHZ 38 2426 39 2480 三个信道进行广播和扫描,因此广播不支持调频,由于广播是一对多的,所以广播也无法支持ack,这些都使广播通信变得不可靠

扫描端口功耗高,由于扫描端不知道设备端何时广播,也不知道设备端选用哪个频道进行广播,扫描端只能拉长扫描窗口时间,并同时对37 38 39 三个通道进行扫描,这样功耗就会比较高

而连接则可以很好解决上述问题,下面我们就来看看连接是如何将0x53 发送出去的

6.3 连接方式

到底什么叫连接,像有线uart ,很容易理解,就是用线 rx tx 等,把设备A 和设备B 相连,即为连接,用线把两个设备相连,实际是让两个设备有共同的通信媒介,并让两者时钟同步起来,蓝牙连接有何尝不是这个道理
所谓设备A 和设备B 建立蓝牙连接,就是指设备A 和设备B 两者一对一 同步成功,其具体包含以下几方面

设备A 和设备B 对接下来要用的物理信道达成一致

设备A 和设备B 双方建立一个共同的时间锚点,也就是说,把双方的时间原点变成同一个点

设备A 和设备B 两者时钟同步成功,即双方都知道对方什么时候发送数据包什么时候接收数据包

连接成功后,设备A 和设备B 通信流程如下所示

如上图所示,一旦设备A 和设备B 连接成功(此种情况下,我们把设备A称为Master 或者central ,把设备B 称为slave 或者peripheral),设备A 将周期性以CI connection interval 为间隔向设备B 发送数据包,而设备B 也周期性地以ci 为间隔打开射频接收窗口以接收设备A 的数据包,同时按照蓝牙spec 要求,设备B 收到设备A 数据包150us 后,设备B 切换到发送状态,把自己的数据发给设备A,设备A则切换到接收状态,接收设备B 发过来的数据,由此可见,连接状态下,设备A 和设备B 的射频发送和接收窗口都是周期性地有计划地开和关,而且开的时间非常短,从而大大降低系统功耗并大大提高系统效率

现在我们看看连接状态下是如何把数据0x53 发送出去的,从中大家可以体会到蓝牙协议分层的妙处

对开发者来说,很简单,他只需要调用 send 0x53

GATT层定义数据的类型和分组,方便起见,我们用0x0013表示电量这种数据类型,这样GATT层把数据打包成130053(小端模式!)
ATT层用来选择具体的通信命令,比如读/写/notify/indicate等,这里选择notify命令0x1B,这样数据包变成了:1B130053
L2CAP用来指定connection interval(连接间隔),比如每10ms同步一次(CI不体现在数据包中),同时指定逻辑通道编号0004(表示ATT命令),最后把ATT数据长度0x0004加在包头,这样数据就变为:040004001B130053
LL层要做的工作很多,首先LL层需要指定用哪个物理信道进行传输(物理信道不体现在数据包中),然后再给此连接分配一个Access address(0x50655DAB)以标识此连接只为设备A和设备B直连服务,然后加上LL header和payload length字段,LL header标识此packet为数据packet,而不是control packet等,payload length为整个L2CAP字段的长度,最后加上CRC24字段,以保证整个packet的数据完整性,所以数据包最后变成:
AAAB5D65501E08040004001B130053D550F6
AA – 前导帧(preamble)
0x50655DAB – 访问地址(access address)
1E – LL帧头字段(LL header)
08 – 有效数据包长度(payload length)
04000400 – ATT数据长度,以及L2CAP通道编号
1B – notify command
0x0013 – 电量数据handle
0x53 – 真正要发送的电量数据
0xF650D5 – CRC24值

虽然 开发者只调用了send 0x53 ,但由于低功耗蓝牙协议栈层层打包,最后空中实际传输的数据将变成下图所示的模样,这就既满足了低功耗蓝牙通信的需求,又让客户api 变得简单,可微一箭双雕

蓝牙广播

蓝牙状态切换

蓝牙服务和特性

SPP 协议, MID 协议

HID 协议 等等

MicroPython BLE

NodeMCU-32

蓝牙广播 advertising

常见的广播类型

0x01 设备标识

0x02 16bit uuid

0x07 128 bit uuid

0x19 设备外观

0x08 缩略设备名称

0x09 完整设备名称

0x0A 发射功率

0xff 厂商自定义数据

app nrf connected

蓝牙广播类型

可连接非定向

可连接定向

不可连接非定向

可扫描非定向

扫描响应数据和广播数据格式是一样的

扫描响应数据是非必需的

总结

低功耗蓝牙通信基于特性

常用通信方式 read write notify
手机订阅才能收到 notify

https://www.bilibili.com/read/cv18236129

BF54D165-942B-D6A0-5425-E593CD317472

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2024 TeX_baitu
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~