- Published on
低功耗蓝牙 BLE 的前世今生
共5519字,28分钟
- Authors
- Name
- YouxingZ
- @youxingz
这系列文章总结了我之前在蓝牙开发中(偏软件方面)遇到的各种问题, 网上资料很杂乱,最初接触的时候理解起来也比较费时,所以有了写作动机。
总体包含如下几个方面:
- 蓝牙的基本介绍,工作方式及常见的开发平台(即本篇下文)
- 蓝牙协议的更替、
BLE5.0
协议在Android
Windows
Linux
mscOS
等不同操作系统下的使用方式 - 蓝牙协议(
HCI
L2CAP
) - 常见的蓝牙服务
- DIS
- BAT
- HID
- 如何自定义一个新的蓝牙服务(以
nordic
esp32
为例) - 什么是 Mesh 组网
- 番外篇:影响蓝牙传输效率的几个重要因素
- 番外篇:关于蓝牙的功耗问题
- 番外篇:对比 WiFi、Zigbee 等协议
(上述内容我会在接下来的时间逐步完成,时常记录是一个好习惯)
那么,我们这里就从这里开始:
蓝牙的基本介绍
我们先看一下 Wikipedia 的简单介绍:
蓝牙(Bluetooth)是一种无线通信技术标准,用来让固定与移动设备, 在短距离间交换资料,以形成个人局域网(PAN)。其使用短波特高频(UHF)无线电波, 经由 2.4 至 2.485 GHz 的 ISM 频段来进行通信。1994 年由电信商爱立信(Ericsson)发展出这个技术。 它最初的设计是希望建立一个 RS-232 数据线的无线通信替代版本。 它能够链接多个设备,以克服同步的亦被干扰等问题。
蓝牙技术目前由蓝牙技术联盟(SIG)负责维护其技术标准,其成员已超过三万, 分布在电信、电脑、网络与消费性电子产品等领域。 IEEE曾经将蓝牙技术标准化为 IEEE 802.15.1,但是这个标准已经不再继续使用。
—— Wikipedia - 蓝牙
有相当多的资料都可以直接在这个网站直接获取:
蓝牙技术联盟的官网1:https://www.bluetooth.com
关于蓝牙的发展历史过于细碎,感兴趣的可以去百科或者联盟的网站上获取,上面写的必然仔细。 在这里只针对偏软件开发方向做一些必要性的总结:
发展历史
历史上蓝牙在 1999 年由爱立信正式公布 1.0
版本,确定其传输频段在 2.4GHz,最高速率为 1Mbps, 主要适用于近距离无线通讯。相比于红外传输有着更高的速度,而且也无需指向性地对接设备(比如你家遥控器如果是红外的,你就得拿着遥控器对着设备方向才能使用), 所以拥有了市场一席之地。
在 1.1
版本推出后,蓝牙 1.1
版正式被 IEEE
收录,标准为 IEEE 802.15.1
, 并连续推出后续几个版本 1.2
2.0
,其中 2.0
版本便是我们往年最常使用地经典蓝牙版本,其传输速率可达 2Mbps。 那些年大家掏出诺基亚寻找蓝牙设备然后配对设备的这一些列操作,都规范在经典蓝牙协议中。
由于 2.0
版本随后配合 EDR
(Enhanced Data Rate) 技术,其传输速率最高能达到 3Mbps, 另外又有了 A2DP
(Advanced Audio Distribution Profile) 技术,用于立体声耳机的空音轨分配技术等, 蓝牙的多媒体能力得到了极大扩充,一股蓝牙风潮全面袭来。
蓝牙 3.0
于 2009 年推出,结合 3.0+HS
将传输速率提升到了 24Mbps,但实际上使用的场景很少。
从 4.0
版本开始,之前的版本都被归入 Classic
(经典)蓝牙版本,自 4.0
起我们都称之为 BLE
(Bluetooth Low Energy,低功耗蓝牙)。 该版本支持了巨多的服务协议,比如什么是音频传输协议、电池电量信息、设备基础信息、心率服务信息等,每一种服务都由一系列 UUID 来配合指定, 如果弄清楚了这套规范,在某种意义上来讲是极大地简化了开发的繁琐流程。 当然 4.0
还有一个重大的改革就是功耗降低,所以也叫低功耗蓝牙。 配合这些服务协议,蓝牙模块间的通讯会变得更有规律,比如电池服务(读取电量信息传给其他设备),只需要最多 1 秒发一次电量数据,或者等到电池电量有变化后再发即可, 其他的时间完全可以放心地进入低功耗休眠,而不用时刻与其他设备保持活跃的连接状态。
在软件开发角度来讲,其实蓝牙只有两个版本,那就是经典版和低功耗版。 因为新的低功耗版本制定了很多协议规范,方便了软件开发也方便了不同厂家设备之间的兼容, 而经典蓝牙的传输方式其实与串口读写无异(类似于 Java 里的一个文件流处理过程)。
最新推出的蓝牙 5.0
5.1
5.2
5.3
版本其实在软件开发层面上来说没有太多差异,甚至绝大多数场景根本不需关心蓝牙低功耗版本的差异。 在第五代版本中蓝牙速度最高能达到 24Mbps,支持 Beacon
、室内定位等技术,主要针对物联网做了很多优化。
值得一提的是,A2DP
协议在 4.0
时由于低功耗协议改版所以只支持到 3.x
版本,然而在 5.2
版本 (2020) 中新增了对 LE Audio
的支持。 如果想开发音频相关的功能,除了经典蓝牙之外,如今 5.2
及更高版本的蓝牙也都给了足够的支持。
BTW,上面地大部分数据都是公开数据,但实际上我在自己的项目开发中从来没有将蓝牙速度跑到 24Mbps 的,这方面数据我还是持怀疑态度。
技术特点
还是得画个表:
版本 | 传输速率 | 传输距离 | 频道 | 功耗 | 网络拓扑 | |
---|---|---|---|---|---|---|
经典蓝牙 | 2.0 (2004) | 2Mbps | <10 m | 79 个频道 | TX ≤100 mW (+20dBm) | P2P (点对点连接) |
2.1 + EDR (2007) | 3Mbps | |||||
3.0 + HS (2009) | 24Mbps | |||||
低功耗蓝牙 | 4.0 (2010) - 4.2 (2014) | 24Mbps (通常限制在 2Mbps,默认 1Mbps) | <50 m | 40 个频道 (2Mbps) 3 个广播频道 + 37 个数据频道 | TX ≤100 mW (+20dBm) | 点对点 (P2P)、广播 (Broadcast)、网状(Mesh) |
5.0 (2016) - 5.4 (2023) | <300 m |
在使用 Nordic 的 nRF52832
芯片2实测中,该芯片支持 BLE5.4
版本,广播时功耗可达到 10mA * 3.3V = 33mW (+0dBm), 工作状态中往往会达到更低的功耗(因为数据传输不是时时刻刻进行,大部分时间可以对蓝牙休眠,只需要保持连接状态就行)。
而其传输速率,理论上限是 2Mbps,除去蓝牙协议本身结构,能交给用户传输的带宽实际上最大只有 1.4Mbps 左右, 然后受传输距离、周围干扰、天线设计、功耗、设备本身工作性能影响,大部分的场景下传输速率都会对半打折甚至更低。
工作方式
蓝牙有很多工作的模式,比如主从模式、广播模式、Mesh 组网等,分类的方法也有很多种,我们以开发者视角来看,总体可由一个状态表(见下表)来表示。
下文中 主设备
(Master) 一般指用于连接的发起创建的设备,运行在手机、电脑、iPad 等“中心”设备上; 从设备
(Slave; 一般也称外设, Peripheral) 一般会广播自己的信息,接受并回复主设备发起的请求,运行在边缘设备上,比如耳机、鼠标、Apple Pencil 等。
下表如无特殊说明,一般都只限于支持 BLE4.0 及以上协议的设备上,经典蓝牙协议十分简单,后文讲述协议部分时会针对讨论这部分内容。
模式 | 网络拓扑 | 角色 | 描述 |
---|---|---|---|
广播状态 | 一对多 | 从设备 (广播者) | 会在固定频道广播自己的设备信息(如设备名称、功率、服务类型等) |
主设备 (扫描者) | 主设备没有广播状态,即不会广播自己的信息(如果主设备也广播,那这时候我们称其为主从一体设备)。 当用户发起“搜寻设备”这类指令的时候会在该广播频道查询附近的蓝牙设备信息,找到匹配的设备后再与从设备在其他频道进行数据通讯 | ||
工作状态 | 一对一 一对多 | 从设备 (外设) | 接收主设备发起的请求并响应。比如某从设备支持了电池电量服务,那么主设备可访问该设备并要求“订阅”该服务, 之后从设备会按时间间隔不断发送电池电量信息给相应的主设备 |
主设备 (主机/客户端) | 向从设备请求服务,与之交互。一般可发起的操作主要分为:“读取,写入,订阅通知” 这几类。 比如某主设备发现附近有一台支持电量服务的设备,便可向其订阅电量信息,之后每隔一定时间就会接受到更新后的电池电量数据 | ||
组网状态 | 多对多 | 节点设备 | 大家按照统一的协议进行通讯互传,彼此之间互相平等 |
一般业务中,都是从设备先进行广播,等到主设备搜寻到自己并发起连接建立的请求后记录主设备的信息(MAC 地址
等), 随后与之保持连接状态(会有心跳包在彼此之间活跃)。
实例
我们以一个蓝牙鼠标为例。需求需要满足:
- 能够在电脑或其他支持鼠标的设备(后文简称上位机)上搜索到该鼠标
- 能够将鼠标的移动指令、各个按键的点击指令传给上位机
- 能够将电池余量信息传给上位机
下位机(鼠标)
我们鼠标部分的实现方案如下:(鼠标硬件部分此处不考虑)
在蓝牙模块上定义/声明一个 HID Service 服务3(HID: Human Interface Device,即人机接口设备),并在自己的描述参数内表明自己支持鼠标相关的功能
HID 的 UUID 是
0x1812
,下属支持鼠标类型特征符的 UUID 是0x03C2
,指明后即可被任何支持蓝牙协议的上位机视作鼠标设备当然,你也可以同时指定 HID 下的其他特征类型,比如键盘
0x03C1
,这样你的设备就会被上位机视为同时支持鼠标和键盘的设备
在蓝牙模块上定义一个 BAS 服务4(Battery Service)
该服务的 UUID 是
0x180F
,下属支持电池电量的特征的 UUID 是0x2A19
,上位机可对该特征进行读取(或接受通知)以获取电量数据当然,你也可以同时指定
0x180F
下的其他特征,比如电池健康状态0x2BEA
,上位机便可获取到电池健康状态信息
在鼠标上电开机后,检测某按键是否处于长按状态(这个判断逻辑可根据实际情况任意设计),据此判断是否进入广播状态
广播自己支持的服务 UUID
当有上位机发起连接请求时,保存其
MAC 地址
信息,并与之保持连接状态,同时关闭广播(已无必要让其他上位机发现自己,因为鼠标只需要支持一个上位机使用即可。当然,你也可以单鼠标多上位机,如果有这种应用场景的话)捕获传感器数据(比如光线传感器的运动位置、按键是否触发等),处理后以蓝牙 HID 服务的指定格式发送给上位机
捕获电池电量数据,处理后以蓝牙 BAT 服务的指定格式发送给上位机
重复上述两个步骤,直至用户主动断开连接或电量不足自动关机
上位机(如个人电脑)
上位机一般都是默认支持 HID 及 BAT 服务的,此处为了演示这个过程,我们假设我们需要在一台古老的机器上支持我们的服务:
购买/设计制作一个支持蓝牙 BLE 的模块,可将蓝牙数据转换为上位机可处理的格式(比如串口、USB 等其他协议)
上位机向蓝牙模块发起搜索设备的指令,搜到合适的设备后用户确认并进行连接
连接建立成功后订阅其相应的服务,比如 HID 下的
0x03C2
及 BAT 下的0x2A19
,以实时获取鼠标传过来的指令及电池的电量根据接收到的数据进行解码处理,然后调用系统 API 来运行鼠标位置及左右键等按键的功能触发
重复上述两个步骤,直至用户主动断开连接或鼠标电量不足自动关机导致失去连接状态
备注
关于蓝牙服务的全部 UUID,可参考文档:Bluetooth Assigned Numbers 5
其实真实情况下因为很多电脑对蓝牙的支持并不完善,而鼠标厂商并不想为此冒险流失用户, 所以我们购买的蓝牙鼠标都会配送一个 USB 蓝牙模块,以此让用户获得良好的体验。 不过值得一提的是苹果 (Apple, Inc) 的产品因为硬件的可控性,他的鼠标、键盘都是不需要额外配置这种转接模块的。 同样的,Android 系统由于其良好的可控性,大多数厂家为了紧随系统的版本更新节奏,也都不会对蓝牙这部分协议进行过分修改, 所以绝大多数蓝牙鼠标也都能直接连接 Android 设备(除鼠标厂商自己特别设计了新的蓝牙协议,于是需要安装特别的驱动,或者只能使用他的蓝牙模块之外)
常用的开发平台
下位机
国内最好用的可支持蓝牙的芯片无非乐鑫的 ESP32 了,一块芯片几块钱的成本还是乐于接受的。 ESP32 有很多系列:
ESP8266,最早发售的一款芯片,因为其强大的处理性能及低廉的售价迅速获得市场的欢迎
ESP32,经典款 32 位处理器,同上
ESP32Cx 系列,C 系列是面向低功耗的系列,芯片体积小,支持 BLE、Zigbee、Wi-Fi 等多种协议,可玩性很高
ESP32Sx 系列,S 系列是双核版的 C 系列,同时也支持了更强的算力,RAM更是扩大到直接可支持 32M 级别,在单片机领域已经是十分给力了
ESP32 系列的产品对比可参考乐鑫官网:Espressif Product Selector 6
但如果你需要更低功耗的芯片,且不需要 Wi-Fi 这么高的传输速率,Nordic 绝对是个更不错的选择:
nRF52832,经典款蓝牙芯片,售价也很低,功耗(同比蓝牙功耗)比 ESP32 低接近 5 - 10 倍
nRF52833,nRF52840,都是 52 系列的较新款,价格偏高,但支持了更多能力,比如更快的 SPI 接口以及 QSPI 协议,在蓝牙方面与 nRF52832 几乎一致
下表源自 Nordic Semiconductor 官网的 BLE 系列产品介绍 7
SoC | nRF5340 | nRF52840 | nRF52833 | nRF52832 | nRF52820 | nRF52811 | nRF52810 | nRF52805 |
---|---|---|---|---|---|---|---|---|
Bluetooth | 5.4 | 5.4 | 5.4 | 5.4 | 5.4 | 5.4 | 5.4 | 5.4 |
Thread | Yes | Yes | Yes | Yes | Yes | |||
Matter | Yes | Yes | ||||||
Zigbee | Yes | Yes | Yes | Yes | ||||
Bluetooth Mesh | Yes | Yes | Yes | Yes | Yes | |||
Flash | 1MB + 256KB | 1MB | 512KB | 512/256KB | 256KB | 192KB | 192KB | 192KB |
RAM | 512KB + 64KB | 256KB | 128KB | 64/32KB | 32KB | 24KB | 24KB | 24KB |
CPU | 128 MHz Arm Cortex-M33 + 64 MHz Arm Cortex-M33 | 64 MHz Arm Cortex-M4 with FPU | 64 MHz Arm Cortex-M4 with FPU | 64 MHz Arm Cortex-M4 with FPU | 64 MHz Arm Cortex-M4 | 64 MHz Arm Cortex-M4 | 64 MHz Arm Cortex-M4 | 64 MHz Arm Cortex-M4 |
High-Speed SPI | Yes | Yes | Yes | |||||
QSPI | Yes | Yes | ||||||
USB | Yes | Yes | Yes | Yes | ||||
PWM, PDM, I2S | Yes | Yes | Yes | Yes | PWM, PDM | PWM, PDM | ||
ADC, Comp | Yes | Yes | Yes | Yes | COMP | ADC, COMP | ADC, COMP | ADC |
Temp Range | -40 to 105 °C | -40 to 85 °C | -40 to 105 °C | -40 to 85 °C | -40 to 105 °C | -40 to 85 °C | -40 to 85 °C | -40 to 85 °C |
Supply voltage range | 1.7 to 5.5V | 1.7 to 5.5V | 1.7 to 5.5V | 1.7 to 3.6V | 1.7 to 5.5V | 1.7 to 3.6V | 1.7 to 3.6V | 1.7 to 3.6V |
Packages | aQFN94 | aQFN73 | aQFN73 | QFN48 | QFN40 | QFN48 | QFN48 | WLCSP28 |
GPIOs | 48 | 48 | 18-42 | 32 | 18 | 15-32 | 15-32 | 10 |
ESP32 和 Nordic 的芯片算是行业内比较出众的,因我个人用过的蓝牙芯片并不算多,如果有其他特别好用值得推荐的蓝牙芯片,可以在评论区留言备注。
上位机
上位机领域主要有几个常用平台:
Windows 阵营,最新的 Windows 10、11 操作系统对蓝牙的支持已经较为良好,使用 C# 即可调用系统提供的绝大部分蓝牙 GATT 服务,对 BLE 协议支持较为完善。 但由于历史原因,在早些年的系统上对蓝牙几乎不提供支持,而且由于硬件是由不同厂家生产制造,所以尽管系统已经更新到 Windows11 这种最新版本,但仍可能未安装合适的蓝牙模块。 所以很多支持蓝牙设备的外设厂商都会自配一个用于 Windows 设备的蓝牙 USB 模块,以提供稳定传输质量的服务。
Android 系统,大部分 Android 手机都已支持超过 Android 8.0 版本,所以对蓝牙 BLE 协议的支持是非常完善的。 而且手持设备一般都默认安装了蓝牙及 Wi-Fi 这类无线模块,所以不必过分担心用户的蓝牙会出现连接质量问题。 但实测中发现,不同厂商(尤其是国内)的蓝牙连接是有些微差异的,有的厂家的产品默认传输包大小非常小,有的默认就比较大, 如果不对代码进行手动设置,其在不同厂家设备上运行表现会出现很多不一致的情况。
苹果系列,比如 macOS、iOS 系统对蓝牙的支持都是非常完善的,毕竟这家公司既生产软件也制作硬件,所以可控性非常好,同时也是蓝牙联盟的核心成员,自然会大力支持蓝牙标准的推进。 在 iOS 这类系统中,蓝牙默认就有着尽可能高的传输效率设定,开发者往往不必过分担忧其底层协议参数的配置,调试好一台设备后在其他系列的设备上运行也都往往有着一样的体验。
之后的文章会从简入深着手,先讲述在客户端的蓝牙使用,再探讨下位机的蓝牙设计开发,以及相关的一些测试方法,逐步让读者更好地理解并掌握关于蓝牙的开发范式和设计经验。