算法是一种通过比较新旧版本固件的二进制差异,只传输差异部分来大幅减少升级包体积的技术。这种技术对于嵌入式设备(尤其是通过蜂窝网络/NB-IoT等低带宽环境升级的设备)至关重要。下面从原理到实现展开讲解:
一、为什么需要差分算法?——传统OTA的痛点
- 固件体积大STM32等MCU固件通常比较大,全量传输耗时过长。
- 网络条件差设备在2G/4G/LoRa等网络下传输可靠性低。
- 设备资源受限设备端内存不足以加载完整新固件(如256KB RAM设备无法加载1MB固件)。
传统方案:传输整个新固件 → 100KB固件在NB-IoT(5KB/s)网络下需20秒
差分算法:传输差异部分(约10KB) → 仅需2秒(提升90%速度)
二、核心原理:Delta编码(增量编码)
关键概念:
- Base File:设备当前运行的旧固件(v1.0)
- Target File:待升级的新固件(v2.0)
- Delta File(补丁文件):记录两者差异的二进制描述文件
三、差分算法的工作流程
步骤1:生成差异文件(服务器端)
伪代码示例:使用 bsdiff 算法生成差异文件
import bsdiff
old_fw = read_file("firmware_v1.bin")
new_fw = read_file("firmware_v2.bin")
delta = bsdiff.diff(old_fw, new_fw) # 核心差异计算
save_file("v1_to_v2.delta", delta)
步骤2:设备端合并固件(设备端)
// 嵌入式设备伪代码(基于开源的 HDiffPatch)
void apply_delta_patch() {
uint8_t* old_buf = load_from_flash(0x8000000); // 读取旧固件
uint8_t* delta_buf = download_from_ota(); // 接收差异文件
uint8_t* new_buf = malloc(new_size); // 申请新固件缓冲区
hpatch_Build(new_buf, old_buf, delta_buf); // 合并生成新固件
verify_checksum(new_buf); // 校验完整性
write_to_flash(0x8040000, new_buf); // 写入新固件区
}
四、实现差分的核心算法
- 字符串匹配算法:寻找相同字节块
最长公共子序列(LCS)
定位新旧文件中完全相同的字节序列(避免复制相同内容)。
哈希滚动匹配(Rabin-Karp)
快速找到可变长度的相同数据块(复杂度O(n))。
- 字节级差异处理:处理改动的字节
二进制delta压缩
用三元组表示差异:(偏移量, 旧长度, 新数据)
例:文件偏移0x100处,旧数据"1234" → 新数据"abcd" 记录为 (0x100, 4, “abcd”)
COPY/ADD指令编码
COPY(旧偏移, 长度) :复制旧文件的片段
ADD(新数据) :插入新字节
- 主流差分工具对比
工具 原理 适用场景 特点
bsdiff 后缀排序+LZMA压缩 嵌入式固件 压缩率高,内存消耗大
xdelta Rabin-Karp块匹配 文件/镜像更新 速度快,内存占用低
Courgette 汇编指令重定位(Chrome用) 可执行文件 针对代码段优化
HDiffPatch 内存优化版bsdiff 资源受限嵌入式设备 RAM占用<50KB
五、在嵌入式系统中的关键技术挑战
- 内存优化策略
- 流式处理:按块逐步处理文件(避免同时加载整个旧/新固件)
// 分块处理伪代码
while (has_data()) {
block = read_block(old_fw, delta, block_size); // 每次处理16KB
new_block = merge_block(block);
write_block(new_block);
}
- 重叠写入技术:直接在Flash中"就地"更新(无需双倍存储空间)
- 安全与可靠性机制
- 断点续传:记录已接收的delta块偏移量
- CRC校验:每个delta块单独校验
- 回滚保护:备份bootloader和恢复区
- 数字签名:防止篡改差分文件(ECDSA签名校验)
- 嵌入式端实现示例(FreeRTOS + HDiffPatch)
void ota_task(void *pvParameters) {
while (1) {
if (xEventGroupGetBits(ota_event) & OTA_START) {
// 分片下载差分文件
DeltaStream stream;
delta_stream_init(&stream, "v1_to_v2.delta");
while (!stream.eof) {
uint8_t* chunk = download_chunk(stream.offset);
hpatch_safe_input(&stream, chunk); // 安全合并
xEventGroupSetBits(ota_event, OTA_PROGRESS);
}
verify_signature(); // 验证固件签名
jump_to_new_fw(); // 重启运行新固件
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
六、实际效果对比(1MB固件升级)
指标 全量升级 差分算法升级 优化效果
传输体积 1.0 MB 8~50 KB ↓95%~99%
4G网络耗时(5Mbps) 1.6秒 0.08~0.5秒 ↓95%
NB-IoT网络耗时(5KB/s) 200秒 1.6~10秒 ↓95%
设备RAM峰值占用 1MB+ 10~50KB ↓95%
Flash磨损寿命损耗 100% <5% 延长20倍
七、适用场景与限制
- 推荐场景:✅ 固件版本间差异小(如bug修复)✅ 低速网络环境(NB-IoT/LoRa)✅ 资源受限设备(RAM < 64KB)
- 不适用场景:❌ 首次烧录(无旧固件)❌ 版本跨度极大(如v1.0→v5.0)❌ 固件结构完全重构(差异率>80%)
🔑 终极价值:对于千万级IoT设备,差分OTA升级每年可节省:
- PB级流量费用(降低99%)
- 设备待机功耗(传输时间缩短→射频模块工作时间减少)
- 升级成功率(短时传输更不易被网络中断)
嵌入式开发者应优先选择 HDiffPatch/xdelta等开源库,避免重复造轮子,重点聚焦在安全校验与可靠性设计上,这才是OTA升级的真正挑战。