ArduinoBLE 库的使用
ArduinoBLE 是 Arduino 官方提供的用于低功耗蓝牙(BLE)通信的库。该库支持所有硬件支持 BLE 和 Bluetooth 4.0 及以上版本的 Arduino 开发板,包括:
- Nano 33 BLE - 基于 Nordic nRF52840 的开发板
- Arduino NANO 33 IoT - 集成 WiFi 和 BLE 的开发板
- Uno WiFi Rev 2 - 带有 WiFi 和 BLE 功能的 Uno 开发板
- MKR WiFi 1010 - MKR 系列开发板,支持 WiFi 和 BLE
使用方法
要使用 ArduinoBLE 库,首先需要在代码中包含库的头文件:
#include <ArduinoBLE.h>
BLE 快速入门
什么是 BLE?
Bluetooth 4.0 标准包含两种技术:传统的蓝牙(现在称为 "Bluetooth Classic")和低功耗蓝牙(Bluetooth Low Energy,简称 BLE)。BLE 针对低功耗和低数据速率进行了优化,设计用于从简单的纽扣电池供电运行。
BLE 通信模型
与基于异步串行连接(UART)的标准蓝牙通信不同,BLE 的通信模型更 像是一个社区公告板。连接到它的设备就像社区成员阅读公告板一样。每个设备可以充当公告板(发布者)或读者(订阅者)。
- 外设设备(Peripheral):充当公告板,发布数据供其他设备读取。在 BLE 术语中,外设设备类似于服务器,因为它们包含读者设备请求的信息。
- 中心设备(Central):充当读者,从外设设备读取信息。在 BLE 术语中,中心设备类似于客户端,因为它们从外设读取可用信息。
可以将 BLE 外设设备想象成公告板,中心设备是查看者。中心设备查看服务、获取数据,然后离开。每次交互都很快(几毫秒),因此多个中心设备可以从一个外设获取数据。
服务和特征
外设提供的信息以 服务(Services) 的形式组织,每个服务又细分为 特征(Characteristics)。可以将服务想象成公告板上的通知,特征则是这些通知的各个段落。
- 外设设备:只需在需要时更新每个服务特征,无需担心中心设备是否读取它们。
- 中心设备:连接到外设后,读取所需的数据。
- 读写权限:如果某个特征既可读又可写,那么外设和中心都可以修改它。
Notify 通知机制
BLE 规范包含一种称为 notify(通知) 的机制,可以在数据更改时通知接收方。当启用特征的通知功能并且发送方写入新值时,新值会自动发送给接收方,而无需接收方显式发出读取命令。这通常用于流式数据,例如加速度计或其他传感器读数。
indicate(指示) 是 notify 的一个变体,工作方式类似,但在 indicate 规范中,读取方会发送对推送数据的确认。
BLE 的客户端-服务器结构,结合 notify 特征,通常称为 发布-订阅模型(publish-and-subscribe model)。
更新特征值
外设应该在特征值发生显著变化时更新它们。例如:
- 当开关从关闭变为打开时,更新其特征值
- 当模拟传感器变化达到显著程度时,更新其特征值
虽然可以按固定间隔更新特征值,但如果特征值没有变化,这会浪费处理能力和能量。因此,建议只在值发生有意义的变化时才更新特征。
中心设备和外设设备
- 中心设备(Central):充当客户端。它们从外设设备读取和写入数据。
- 外设设备(Peripheral):充当服务器。它们提供来自传感器的可读特征,并提供可读/可写特征来控制执行器(如电机、灯光等)。
在实际应用中:
- 外设设备:通常是传感器节点、IoT 设备等,负责采集数据并提供给中心设备
- 中心设备:通常是手机、平板电脑或主控制器,负责读取数据并控制外设
服务、特征和 UUID
BLE 外设提供 服务(Services),每个服务又提供 特征(Characteristics)。您可以定义自己的服务,或使用 标准服务。
服务通过称为 UUID(通用唯一标识符) 的唯一数字进行标识。UUID 在其他上下文中也很常见。
- 标准服务:使用 16 位 UUID(例如:
0x180F表示电池服务) - 自定义服务:使用 128 位 UUID(例如:
19B10000-E8F2-537E-4F6C-D104768A1214)
定义服务和特征的能力取决于您使用的无线模块及其固件。大多数现代 BLE 模块都支持自定义服务。
服务设计模式
特征值的最大长度为 512 字节。这是设计服务时的关键约束。考虑到这个限制,您应该考虑如何最有效地为应用程序存储传感器和执行器的数据。
模式 1:一个特征对应一个值(简单模 式)
最简单的设计模式是为每个传感器或执行器值使用一个特征,使用 ASCII 编码值:
| 特征 | 值 |
|---|---|
| Accelerometer X | 200 |
| Accelerometer Y | 134 |
| Accelerometer Z | 150 |
优点:
- 最简单,易于开发和调试
- 每个值独立,易于理解
缺点:
- 内存占用最大
- 读取时间最长(需要多次读取操作)
模式 2:组合多个值到一个特征(高效模式)
当某个传感器或执行器有多个关联值时,可以将读数组合到单个特征中:
| 特征 | 值 |
|---|---|
| Motor Speed, Direction | 150,1 |
| Accelerometer X, Y, Z | 200,133,150 |
优点:
- 更高效,减少读取次数
- 节省内存和带宽
缺点:
- 需要解析数据(例如使用逗号分隔)
- 需要注意不要超过 512 字节限制
例如,上面的加速度计特征作为 ASCII 编码字符串需要 11 字节("200,133,150")。
模式 3:二进制编码(最紧凑模式)
对于需要更高效率的应用,可以使用二进制编码而不是 ASCII:
| 特征 | 值(二进制) | 大小 |
|---|---|---|
| Accelerometer X, Y, Z | [0xC8, 0x86, 0x96] | 3 字节 |
这种方式最紧凑,但需要中心设备和外设设备都支持相同的二进制格式。
读取/写入/通知/指示
中心设备可以对特征执行 4 种操作:
- Read(读取):请求外设发送特征的当前值。通常用于不经常变化的特征,例如用于配置、版本号等的特征。
- Write(写入):修改特征的值。通常用于类似命令的操作,例如告诉外设打开或关闭电机。
- Notify(通知):请求外设持续发送特征的更新值,而无需中心设备不断请求。外设发送数据后不需要确认。
- Indicate (指示):与 Notify 类似,但外设发送数据后需要中心设备确认。
使用场景对比:
| 操作 | 适用场景 | 示例 |
|---|---|---|
| Read | 不经常变化的数据 | 设备版本号、配置参数 |
| Write | 控制命令 | 开关电机、设置参数 |
| Notify | 频繁变化的流式数据 | 加速度计、温度传感器 |
| Indicate | 需要确认的重要数据 | 警报、状态变化 |
广播和 GAP
BLE 设备通过使用 通用广播配置文件(General Advertising Profile,GAP) 进行广播,让其他设备知道它们的存在。广播数据包可以包含:
- 设备名称
- 其他信息(如发射功率、设备类型等)
- 提供的服务列表
广播数据包限制
广播数据包的大小有限制(通常为 31 字节)。您只能在数据包中放入一个 128 位服务 UUID。确保设备名称不要太长,否则可能连设备名称都放不下。
优化建议:
- 设备名称控制在 8-12 个字符以内
- 优先广播最重要的服务 UUID
- 使用 16 位标准 UUID 可以节省空间
非广播服务
您可以提供未在广播中声明的附加服务。中心设备将通过连接/绑定过程了解这些服务。但是,非广播服务不能用于发现设备。
使用场景:
- 您可能有一个带有自定义服务的外设设备
- 在中心设备应用程序中,您可能知道它还提供电池服务和其他标准服务
- 这些标准服务不需要在广播中声明,因为它们可以通过连接后查询获得
GATT 协议
BLE 协议在多个层上运行。通用属性配置文件(General Attribute Profile,GATT) 是定义服务和特征并启用对它们的读取/写入/通知/指示操作的层。
GATT 服务器和客户端
在了解 GATT 时,您可能会遇到 GATT 的"服务器"和"客户端"概念。这些并不总是对应于中心设备和外设设备。但在大多数情况下:
- 外设设备 = GATT 服务器(因为它提供服务和特征)
- 中心设备 = GATT 客户端(因为它读取和写入数据)
GATT 层次结构
GATT Server (外设)
├── Service 1 (服务 1)
│ ├── Characteristic 1.1 (特征 1.1)
│ │ ├── Descriptor 1.1.1 (描述符 1.1.1)
│ │ └── Descriptor 1.1.2 (描述符 1.1.2)
│ └── Characteristic 1.2 (特征 1.2)
└── Service 2 (服务 2)
└── Characteristic 2.1 (特征 2.1)
说明:
- 服务(Service):相关功能的集合
- 特征(Characteristic):实际的数据值
- 描述符(Descriptor):特征的元数据(如单位、格式等)
库结构
ArduinoBLE 库提供了多个类来实现不同类型的 BLE 功能。以下是主要的类及其用途:
主要类
-
BLE:用于启用和初始化 BLE 模块。这是使用库的入口点,需要先调用
BLE.begin()来启动 BLE 功能。 -
BLEDevice:用于获取扫描时发现或连接的设备信息。当作为中心设备扫 描外设时,使用此类来访问设备信息(如设备名称、地址、RSSI 等)。
-
BLEService:用于启用开发板提供的服务,或与远程开发板提供的服务进行交互。每个服务包含一个或多个特征。
-
BLECharacteristic:用于启用开发板在服务中提供的特征,或与远程开发板提供的特征进行交互。这是实际存储和传输数据的地方。
-
BLEDescriptor:用于描述开发板提供的特征。描述符包含特征的元数据,如单位、格式、描述等。
使用流程示例
作为外设设备(Peripheral)
#include <ArduinoBLE.h>
BLEService myService("19B10000-E8F2-537E-4F6C-D104768A1214");
BLECharacteristic myCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214",
BLERead | BLEWrite, 20);
void setup() {
BLE.begin(); // 初始化 BLE
BLE.setLocalName("MyDevice"); // 设置设备名称
BLE.setAdvertisedService(myService); // 广播服务
myService.addCharacteristic(myCharacteristic); // 添加特征到服务
BLE.addService(myService); // 添加服务
BLE.advertise(); // 开始广播
}
作为中心设备(Central)
#include <ArduinoBLE.h>
void setup() {
BLE.begin(); // 初始化 BLE
BLE.scan(); // 开始扫描外设
}
void loop() {
BLEDevice peripheral = BLE.available(); // 检查是否有可用设备
if (peripheral) {
// 连接到设备并读取特征
}
}
应用场景
- IoT 传感器节点:使用外设模式,定期广播传感器数据
- 智能家居控制:使用中心模式,连接并控制多个智能设备
- 可穿戴设备:使用外设模式,传输健康监测数据
- 数据采集系统:使用中心模式,从多个传感器节点收集数据

GetIoT.tech 创始人,独立开发者,Linux 重度用户,开源软件作者,创业者,INTJ
