消费电子OTA编程:实现小家电固件的静默升级与用户无感知更新
小家电“静默升级”不是后台偷偷写当前固件,而是运行区不动、新包写备用区、校验过再重启切换。电饭煲煮饭、扫地机回充途中被覆盖Flash,一半断电就变砖。正确架构是A/B双分区 + BootLoader仲裁 + 下载任务后台跑。
一、Flash分区规划
以256KB级MCU为例:
0x0000 BootLoader (只读,校验+跳转)
0x5000 Param区 (版本/启动槽/尝试计数/下载偏移)
0x8000 APP_A (当前运行)
0x... APP_B (备用)
APP_A/B必须等大,Param区单独分出来,按字节写标志,别和代码区混。
二、下载任务:只碰备用区
收到云端版本推送后,先判断自己是不是目标版本、设备是否busy:
typedef struct {
uint8_t active_slot; // 0=A,1=B
uint8_t pending_slot; // 待验证槽
uint8_t try_cnt; // 启动尝试次数
uint32_t dl_offset; // 断点偏移
uint8_t state;
} boot_param_t;
void ota_task(void*p){
if(ota_check_version()==NO_UPDATE) return;
if(device_busy()){ // 加热/电机/充电中不升
return;
}
if(battery_or_vin() < 30%) return; // 电量门槛
uint32_t off = param.dl_offset;
while(off < fw_total){
int n = net_recv_block(fw_buf, BLOCK_SZ, off);
if(n<=0){ vTaskDelay(1000); continue; } // 弱网退避
flash_erase_write(APP_B + off, fw_buf, n);
off += n;
param.dl_offset = off;
save_param(); // 断点持久化
}
}
要点:
写的是APP_B,当前APP_A继续跑业务
块粒度写入,每块落Flash再存offset
网络断重连从dl_offset续传,不重头下
下载任务优先级低于控制业务,阻塞等数据不占CPU
三、校验:下载完别急着重启
整包收完做两层校验:
if(sha256_file(APP_B, fw_total) != hdr.sha256)
goto ota_abort;
if(rsa_verify(hdr.sig, APP_B, fw_total) != OK)
goto ota_abort;
只做CRC不够,量产要签名校验挡掉非法固件。 校验过才写pending:
param.pending_slot = 1; // 下次起B
param.try_cnt = 0;
save_param();
// 不立即重启
“用户无感知”就在这:下载校验都在后台完成,机器照常工作。
四、择机重启 + BootLoader仲裁
别在用户按启动键时重启。等空闲窗口:
设备进入待机
凌晨定时(如2:00~5:00)
连续无操作N秒
if(param.pending_slot!=0xFF && idle_secure()){
sys_soft_reset();
}
BootLoader上电逻辑:
void bootloader_main(void){
read_param();
if(param.pending_slot != 0xFF){
if(image_valid(APP_B)){
jump_slot = APP_B;
}else{
pending_slot = 0xFF; // 校验失败回A
}
}
if(try_cnt_exceed(jump_slot)){
jump_slot = active_slot; // 回滚
}
param.try_cnt++;
save_param();
jump_to(jump_slot);
}
决策权在BootLoader,不在APP。
五、新固件自确认
新固件起来后必须“报到”,否则看门狗超时回滚:
void app_after_ota(void){
if(is_pending_boot()){
self_test(); // 外设/通信/加热自检
mark_slot_valid(); // 写Param:B已确认
param.pending_slot = 0xFF;
param.active_slot = 1;
param.try_cnt = 0;
save_param();
}
}
规则:
启动30秒内没mark valid → 下次BootLoader切回A
看门狗复位try_cnt累加,超3次锁定旧版
回滚原因写日志,上报云端
六、静默体验三红线
不写当前运行区 → 双分区兜底
不用户操作中重启 → busy判断+空闲窗口
不信任未校验固件 → SHA256+签名
附加细节:
版本检测走定时唤醒,别一直连Wi-Fi
差分升级(bsdiff)小家电固件体积能砍80%
灰度先推1%再放量
Param区双备份,掉电可恢复
七、落坑清单
单分区直接擦APP区→断电变砖
下载完立刻重启→用户正用着突然黑屏
只校验CRC不验签名→恶意包可刷入
不存dl_offset→弱网每次重下
新固件不mark valid→每次都回滚
BootLoader不计数→坏固件死循环重启
加热/电机中升级→中断业务逻辑
电量低于30%还写Flash→掉电残片
八、落地顺序
Flash划Boot/Param/APP_A/APP_B
下载任务写备用区,块级落盘+offset
SHA256+签名校验
置pending,不重启
待机/定时窗口软复位
BootLoader校验+跳槽+try_cnt
新固件自检mark valid
超时/崩溃自动回滚A
云端上报版本+灰度
小家电静默OTA的本质就一句:升级过程永远不碰正在跑的那份固件。后台下载到B区、校验过再给BootLoader交棒、新固件自己确认存活——用户侧表现就是“昨晚还在煮饭,今早发现版本号高了”。没双分区、没BootLoader仲裁、没自确认机制,都算Demo级OTA,量产返修率会教你做人。





