结合QBoot与HPatchLite实现高效差分升级(FOTA)

文章目录

        • **一、引言:为何需要差分升级?**
        • **二、工作流程:从制作补丁到设备升级**
          • [**阶段一:在PC端制作QBoot差分升级包 (`.rbl`)**](#阶段一:在PC端制作QBoot差分升级包 (.rbl))
        • [**三、核心机制:原地升级(In-Place Update)与缓冲策略**](#三、核心机制:原地升级(In-Place Update)与缓冲策略)
          • [**策略一:使用Flash作为缓冲区 (Flash Swap)**](#策略一:使用Flash作为缓冲区 (Flash Swap))
          • [**策略二:使用RAM作为缓冲区 (RAM Buffer)**](#策略二:使用RAM作为缓冲区 (RAM Buffer))
        • **四、设备端配置实战**
          • [**1. FAL分区规划**](#1. FAL分区规划)
          • [**2. Kconfig配置**](#2. Kconfig配置)
          • [**3. `rtconfig.h` 宏定义示例 (对应RAM Buffer策略)**](#3. rtconfig.h 宏定义示例 (对应RAM Buffer策略))
        • [**五、深入底层:`hpatchlite-wrapper` 的工作原理**](#五、深入底层:hpatchlite-wrapper 的工作原理)
        • **六、结论**
        • **七、相关链接**
        • **八、后记**
一、引言:为何需要差分升级?

在物联网(IoT)设备的生命周期中,固件空中升级(FOTA)是不可或缺的一环。然而,传统的全量包升级方式意味着每次都需要传输完整的固件,这不仅消耗了大量的网络带宽,延长了升级时间,还对设备的Flash存储空间提出了更高的要求。

**差分升级(Differential Update)**通过只传输新旧固件之间的"差异"部分(即补丁包),极大地减小了升级包的体积,完美地解决了上述痛点。本文将详细介绍如何在RT-Thread操作系统中,利用QBoot引导加载程序和轻量级差分库HPatchLite,构建一套完整、高效且资源可控的差分升级方案。

核心组件架构:
云端/PC端 设备端 (RT-Thread) 输入 生成 patch.bin 打包成 patch.rbl 下载升级包 调用 读写Flash hdiffi (差分工具) package_tool.py (打包工具) 新旧固件.bin 下载服务器 QBoot (Bootloader) FAL (Flash抽象层) hpatchlite-wrapper (差分合并引擎)

图1:差分升级方案的整体架构


二、工作流程:从制作补丁到设备升级

整个差分升级流程分为两大阶段:在PC端制作差分包,以及在设备端执行升级。

阶段一:在PC端制作QBoot差分升级包 (.rbl)

这一阶段的目标是生成一个QBoot可以识别和解析的、包含差分信息的升级包。

步骤 1: 生成原始差分补丁 (patch.bin)

  1. 下载工具 : 从 HPatchLite官方GitHub仓库 下载预编译的二进制工具包 (HPatchLite_vX.X.X_bin_xxxx.zip) 并解压。

  2. 准备文件 : 准备好 old.bin (设备当前固件) 和 new.bin (新版固件)。

  3. 执行命令 : 使用 hdiffi.exe 工具生成原始差分数据。

    bash 复制代码
    # hdiffi.exe <旧固件> <新固件> <输出的原始补丁>
    .\hdiffi.exe old.bin new.bin patch.bin

    (可选)为了进一步减小补丁体积,可以启用tuz压缩算法。这对于带宽受限的设备至关重要。

    bash 复制代码
    # -c-tuz 参数启用了tuz压缩
    .\hdiffi.exe -c-tuz old.bin new.bin patch_compressed.bin

步骤 2: 将原始补丁封装为QBoot格式 (.rbl)

QBoot需要一个带有标准头部信息的升级包。我们使用配套的Python脚本 package_tool.py 来完成封装。

bash 复制代码
# python package_tool.py <原始补丁> <新固件>
python .\package_tool.py patch.bin new.bin

关键点解析

  • 第一个参数 (patch.bin) 是上一步生成的原始补丁 ,它将成为.rbl文件的数据主体。
  • 第二个参数 (new.bin) 不会被打包.rbl文件。它仅用于计算RBL头部所需的元数据,如新固件的总大小 (raw_size) 和CRC校验值 (raw_crc),供QBoot在升级后进行校验。

执行成功后,生成的 patch.rbl 就是我们可以下发给设备的最终升级包。


三、核心机制:原地升级(In-Place Update)与缓冲策略

差分升级最棘手的问题在于"原地升级":我们需要从app分区读取旧数据,同时又要向同一个app分区写入新数据。由于Flash的物理特性(通常不能在读取一个扇区的同时擦写它),我们必须借助一个**缓冲区(Swap/Buffer)**来完成这个"左右手互搏"的操作。

QBoot与hpatchlite-wrapper巧妙地提供了两种缓冲策略,开发者可以根据设备的RAM和Flash资源进行权衡选择。
原地升级挑战 矛盾点 需要读取旧数据 需要写入新数据 app分区 差分合并算法 无法在同一Flash扇区
同时进行读和写/擦除操作

图2:原地升级的内在矛盾

策略一:使用Flash作为缓冲区 (Flash Swap)

此策略在RAM资源极其宝贵时使用。它会利用一个独立的Flash分区作为临时周转空间。

  • Kconfig配置 : QBOOT_HPATCH_USE_FLASH_SWAP
  • 工作流程 :
    1. 当需要修改app分区的某个扇区S时,首先将S的原始内容拷贝swap分区。
    2. 差分算法从swap分区读取旧数据,结合补丁数据,生成新的数据。
    3. 擦除app分区的扇区S
    4. 将新生成的数据从RAM写回 app分区的扇区S
  • 优点: 极大地节省了宝贵的RAM。
  • 缺点: 速度相对较慢(涉及多次Flash读写),对Flash的磨损也更大。
策略二:使用RAM作为缓冲区 (RAM Buffer)

此策略在RAM资源相对充足时使用,速度更快。

  • Kconfig配置 : QBOOT_HPATCH_USE_RAM_BUFFER
  • 工作流程 :
    1. 当需要修改app分区的某个扇区S时,直接将S的原始内容读入一个较大的RAM缓冲区。
    2. 差分算法直接在RAM中,从这个缓冲区读取旧数据,生成新数据。
    3. 擦除app分区的扇区S
    4. 将RAM缓冲区中的新数据写回 app分区的扇区S
  • 优点: 速度快,对Flash的磨损小。
  • 缺点 : 消耗的RAM较多。注意 :此RAM缓冲区的大小必须大于等于app分区所在Flash的最小擦除单元(Sector Size)。
四、设备端配置实战
1. FAL分区规划

一个合理的FAL分区规划是成功的前提。

c 复制代码
/* 示例 fal_cfg.h,Flash扇区大小为128KB */
#define FAL_PART_TABLE                                                      \
{                                                                           \
    {FAL_PART_MAGIC_WORD, "app",      "onchip_flash_128k", 0, 3*128*1024, 0}, \
    {FAL_PART_MAGIC_WORD, "factory",  "onchip_flash_128k", 3*128*1024, 2*128*1024, 0}, \
    {FAL_PART_MAGIC_WORD, "swap",     "onchip_flash_128k", 5*128*1024, 1*128*1024, 0}, \
    {FAL_PART_MAGIC_WORD, "download", "onchip_flash_128k", 6*128*1024, 1*128*1024, 0}, \
}
  • app: 差分升级的目标分区。
  • download: 用于存放下载的 patch.rbl 升级包。
  • swap: 如果使用Flash Swap策略,此分区必须存在 ,且其大小至少要等于app分区的擦除粒度(本例中为128KB)。
2. Kconfig配置

在menuconfig中启用并配置HPatchLite功能。

text 复制代码
RT-Thread online packages --->
    tools packages --->
        Qboot: A cross-platform MCU bootloader framework --->
            [*] using hpatch-lite decompress
            [ ]   Use a FLASH partition as swap buffer  --->  # 策略一
            [*]   Use a RAM buffer (Faster, consumes RAM)      # 策略二
                (4096) RAM buffer size for HPatchLite (must be >= sector size)
3. rtconfig.h 宏定义示例 (对应RAM Buffer策略)
c 复制代码
#define PKG_USING_QBOOT
#define QBOOT_APP_PART_NAME "app"
#define QBOOT_DOWNLOAD_PART_NAME "download"
// ... 其他QBoot配置 ...

/* --- HPatchLite 关键配置 --- */
#define QBOOT_USING_HPATCHLITE
#define QBOOT_HPATCH_USE_RAM_BUFFER
#define QBOOT_HPATCH_RAM_BUFFER_SIZE 4096

五、深入底层:hpatchlite-wrapper 的工作原理

hpatchlite-wrapper 是连接QBoot上层逻辑与HPatchLite核心算法的桥梁。其核心API hpi_patch 通过回调函数机制,实现了高度的灵活性和平台解耦。

c 复制代码
/**
 * @brief 使用 HPatchLite 库执行差分合并操作
 */
hpi_patch_result_t hpi_patch(hpatchi_listener_t *listener, // 用户上下文
                             int patch_cache_size,
                             int decompress_cache_size,
                             hpi_TInputStream_read _do_read_diff, // 读补丁回调
                             read_old_t _do_read_old,             // 读旧固件回调
                             write_new_t _do_write_new);          // 写新固件回调

在QBoot的实现中,_do_read_old_do_write_new 函数内部就包含了对上述**缓冲策略(RAM或Flash)**的复杂处理逻辑,从而对hpi_patch核心算法屏蔽了底层细节。

特别注意:RAM占用

当使用压缩补丁时,hdiffi工具的输出日志会包含一行至关重要的信息:
requirements memory size: (must) 27065 + (custom cache) 32768

  • (must) 27065: 这是解压器(如tuz强制要求 的、必须通过hpi_malloc成功分配的RAM大小。它由补丁数据本身决定。
  • (custom cache) 32768: 这是您在调用hpi_patch时传入的patch_cache_sizedecompress_cache_size

您必须确保设备的堆内存 足以满足这个 (must) 的要求,否则差分升级会因内存分配失败而终止。

六、结论

通过将QBoot的引导框架、FAL的标准化存储接口与HPatchLite的高效差分算法相结合,RT-Thread开发者可以轻松地为项目集成一套健壮、高效且资源可控的差分升级(FOTA)方案。正确理解并选择适合项目资源的缓冲策略 ,以及在制作补丁时关注内存需求,是成功实施此方案的关键。这不仅能极大地提升用户体验,还能显著降低项目的长期运营成本。

七、相关链接

https://github.com/sulfurandcu/hpatchlite-wrapper

https://github.com/qiyongzhong0/rt-thread-qboot/blob/master/src/qboot_hpatchlite.c

八、后记
  • hpatchlite-wrapper这个软件包发布快一年了,论坛上一个文章和提问也没有.估计下载的也没几个.我也是知道有hpatchlite这个差分升级组件,才去软件包内搜索才知道居然有人已经实现了这个功能了.
  • 大家发布软件包都推上去连宣传一下也没有,都没人知道什么时候多了一个好用的软件包,是干嘛用的.
  • 或许RTT官方应该在每个软件包发布或者更新的时候,来论坛发一个帖子给这些软件包增加一点曝光度.
相关推荐
舒一笑8 小时前
用数据照亮成长之路:PandaCoder Git 统计工具窗口
git·后端·intellij idea
陈老师还在写代码8 小时前
android studio,java 语言。新建了项目,在哪儿设置 app 的名字和 logo。
android·java·android studio
消失的旧时光-19438 小时前
Kotlin reified泛型 和 Java 泛型 区别
java·kotlin·数据
czhc11400756638 小时前
JAVA111 HashMap Leecode:1 两数之和 3 无重复字符串的长度
java
凌冰_8 小时前
Java Maven+lombok+MySql+HikariCP 操作数据库
java·数据库·maven
武子康8 小时前
Java-165 Neo4j 图论详解 欧拉路径与欧拉回路 10 分钟跑通:Python NetworkX 判定实战
java·数据库·性能优化·系统架构·nosql·neo4j·图论
代码不停9 小时前
Java二分算法题目练习
java·算法
.格子衫.9 小时前
023数据结构之线段树——算法备赛
java·数据结构·算法
Justin_199 小时前
LVS负载均衡集群理论
java·负载均衡·lvs