4、BLE OTA升级流程:从设备发现、连接、固件传输到校验与激活的完整步骤
BLE OTA(Over-The-Air)升级是nRF系列芯片产品生命周期管理中至关重要的一环。它允许开发者通过无线方式修复固件漏洞、添加新功能或更新配置参数,而无需物理接触设备。一个完整的BLE OTA升级流程并非简单的“发文件”,而是一个涉及连接管理、数据分片、流量控制、完整性校验和系统激活的复杂状态机。本章将深入拆解从设备发现到固件激活的每一个关键步骤,帮助开发者构建稳定、可靠且可回滚的升级链路。
上图展示了BLE OTA升级的四个核心阶段及其状态流转。整个流程从设备发现开始,经过参数协商、固件传输,最终到达校验与激活。每个阶段都有其特定的协议操作和失败处理机制,任何一步的疏忽都可能导致升级失败甚至设备变砖。下面我们将逐一深入每个阶段的实现细节。
4.1 设备发现与连接建立:广播、扫描与连接参数
核心原理:BLE OTA升级的第一步是让目标设备(Peripheral)进入可发现状态,同时让升级工具(Central,如手机App或PC工具)能够扫描到它。nRF设备通常通过广播特定UUID来标识其DFU服务。在nRF5 SDK中,DFU服务的广播包通常包含0xFE59或自定义的128-bit UUID。连接建立后,Central需要执行服务发现(Service Discovery)来找到DFU Service的句柄,并协商MTU(Maximum Transmission Unit)大小。
操作流程:
- 广播配置:设备进入DFU模式后,广播间隔建议设置为20ms-30ms(快速广播),以加快发现速度。广播数据中必须包含DFU Service UUID。
- 扫描与过滤:Central扫描到设备后,通过匹配UUID过滤出目标设备。建议同时读取设备的设备名称或MAC地址进行二次确认。
- 连接参数协商:连接成功后,Central应请求较短的Connection Interval(如7.5ms-15ms)和较短的Slave Latency(0),以提高数据传输速率。nRF设备默认的连接参数可能偏向低功耗,不适合高速传输。
- MTU协商:通过GATT Exchange MTU操作,将MTU从默认的23字节提升到247字节(nRF52832/52840支持)。这能显著减少分包数量,例如一个100KB的固件,使用20字节MTU需要5000包,而使用244字节MTU仅需410包。
on_connect回调中主动发起连接参数更新请求,并等待CONNECTION_UPDATED事件确认后再开始传输。
| 参数 | 默认值 | OTA推荐值 | 说明 |
|---|---|---|---|
| Connection Interval | 30-50ms | 7.5-15ms | 越小传输越快,但功耗增加 |
| Slave Latency | 4 | 0 | 必须为0,否则从机可跳过连接事件 |
| Supervision Timeout | 4000ms | 2000ms | 需大于连接间隔的2倍 |
| ATT_MTU | 23 | 247 | 需双方协商,nRF52系列最大支持247 |
4.2 固件传输机制:分片、写入与流控
核心原理:固件传输阶段的核心是可靠地将固件二进制数据分片写入到设备的DFU Data特征值中。nRF的DFU协议定义了两种写入模式:Write with Response(写入后等待设备确认)和Write without Response(写入后不等待确认)。为了提升速度,通常使用Write Without Response,但必须配合流控机制防止数据溢出。
关键机制:
- PRN(Packet Receipt Notification):设备每收到N个数据包后,会向Central发送一个通知,告知已接收的包数量。Central收到通知后继续发送下一批数据。PRN值通常设置为10-20,太小则频繁等待通知降低速度,太大则可能因缓冲区溢出导致丢包。
- 数据包格式:每个Data特征值写入的数据包前两个字节为包的序列号(16-bit),随后是固件数据。序列号用于检测丢包和乱序。
- 超时重传:如果Central在发送完一批数据后,在超时时间(如500ms)内未收到PRN通知,则必须重发该批次数据。
// 伪代码示例:发送固件数据
uint16_t seq = 0;
for (uint32_t offset = 0; offset < firmware_size; offset += chunk_size) {
uint8_t pkt[chunk_size + 2];
pkt[0] = seq & 0xFF;
pkt[1] = (seq >> 8) & 0xFF;
memcpy(&pkt[2], &firmware[offset], chunk_size);
ble_gatts_hvx(conn_handle, data_handle, pkt, sizeof(pkt));
seq++;
if (seq % prn == 0) {
// 等待PRN通知,带超时
wait_for_prn(timeout_ms);
}
}
4.3 校验与激活:从临时存储到固件切换
核心原理:nRF的DFU设计采用双Bank机制(Bank0和Bank1)。Bank0存放当前运行的固件,Bank1作为临时接收区。固件传输完成后,设备执行校验(Verify),确认Bank1中的固件完整无误,然后执行激活(Activate)操作,将Bank1的内容复制到Bank0(或交换Bank指针),最后复位重启。
操作流程:
- 写入Init Packet:在传输固件数据之前,必须先发送Init Packet,其中包含固件大小、CRC、SDK版本、设备类型等信息。设备会校验这些信息是否与当前硬件匹配。
- 发送校验命令:固件数据全部写入后,Central向Control Point特征值写入
Verify命令(0x04)。设备开始对Bank1中的数据进行CRC校验。 - 接收校验结果:校验完成后,设备通过Notification返回结果。成功返回
Success,失败返回错误码(如CRC Error)。 - 发送激活命令:校验成功后,Central写入
Activate命令(0x05)。设备执行Bank切换,将新固件设置为启动目标。 - 系统复位:激活完成后,设备自动复位,Bootloader根据Bank标志位启动新固件。
4.4 实战案例:一次完整的OTA升级排障过程
背景:某物联网项目使用nRF52832作为主控,固件大小为120KB。开发者在测试OTA升级时,发现升级成功率仅约60%,失败时设备无响应,需要重新上电才能恢复。
初始现象与数据:
- 使用手机App(nRF Connect)作为Central,连接正常,MTU协商为247。
- 传输过程中,App显示发送速度约8KB/s,但经常在传输到30%-50%时卡住,随后超时断开。
- 设备端日志显示,在卡住前收到了大量
BLE_GATTS_EVT_WRITE事件,但Flash写入操作频繁返回NRF_ERROR_BUSY。
分析判断过程:
- 初步怀疑是Flash写入速度瓶颈:nRF52832内部Flash页擦除时间约20ms,写入一个页(4KB)约需要50ms。如果数据接收速度超过Flash写入速度,缓冲区会溢出。
- 检查PRN设置:发现App端PRN设置为50,即每50包(约1KB)才通知一次。在MTU=247时,50包约12KB数据,而设备端缓冲区仅4KB,导致溢出。
- 进一步分析:设备端SDK的DFU实现中,默认的缓冲区大小为4KB。当接收数据超过缓冲区容量时,SDK会暂停接收,但此时Central仍在发送,导致丢包。
实际操作步骤:
- 将PRN值从50调整为10(即每10包通知一次,约2.4KB数据)。
- 在设备端DFU初始化中,将
DFU_RX_BUF_SIZE从默认的4KB增加到8KB(需确认RAM足够)。 - 在Central端增加发送间隔控制:每发送20包后延时2ms,给设备Flash写入留出时间。
结果对比:调整后,升级成功率提升至99%以上,传输速度稳定在12KB/s(受限于Flash写入速度)。
风险与代价:增加缓冲区会占用更多RAM,在资源受限的设备上需谨慎。延时发送会降低理论吞吐量,但换取了稳定性。
复盘经验:BLE OTA的瓶颈往往不在无线链路,而在设备端的Flash写入速度。设计时需根据Flash页大小、擦除时间和缓冲区大小,反推合理的PRN值和发送节奏。
4.5 常见误区与总结
误区一:忽略连接参数更新。很多开发者直接使用默认连接参数进行OTA,导致传输速度极慢。正确做法是在DFU开始前主动请求短连接间隔。
误区二:MTU协商失败后仍继续传输。如果MTU协商失败(如对端不支持),应回退到20字节分包模式,而不是直接报错。但需注意,20字节模式下传输时间会成倍增加。
误区三:将PRN设置过大或过小。PRN过大会导致缓冲区溢出,过小则频繁等待通知,降低效率。建议根据MTU和缓冲区大小计算:PRN = 缓冲区大小 / (MTU-3),并留出20%余量。
误区四:忽略Init Packet的校验。Init Packet中的固件大小、CRC和兼容性信息是设备端拒绝错误固件的第一道防线。如果Init Packet错误,设备会直接拒绝接收固件,而不是等到传输完成后再报错。