OTA 远程升级
目标平台: RK3576 | NPU 算力: 6 TOPS | 文档版本: V1.0
适用板型: C-3576BQ 核心板 + M4-R2 底板
最后更新: 2026-10-23
1. 概述
1.1 功能简介
RK3576 OTA(Over-The-Air)远程升级系统为嵌入式设备提供安全、可靠的固件更新能力。系统基于 A/B 双分区方案,通过 eMMC 控制器(节点名以实际发布的 RK3576 SDK 为准)管理双启动槽,支持多种升级框架:
- RAUC(Robust Auto-Update Controller): 推荐方案,提供 A/B 分区管理、签名验证、自动回滚
- swupdate: 轻量级嵌入式 Linux 软件更新框架,支持 Hawkbit 集成
- Mender: 基于 Yocto 的 OTA 方案,支持增量升级和设备组管理
- sysupgrade(OpenWrt): 适用于 OpenWrt 体系的升级方案
核心能力:
- A/B 分区无缝切换,升级过程无业务中断
- 固件签名验证(RSA-2048/4096),防止未授权固件写入
- 自动回滚机制,升级失败时恢复上一版本
- 断点续传与差量升级(delta update),减少下载数据量
- HTTPS 安全传输,保障固件下载安全
- U-Boot Bootloader 在线升级
1.2 工作原理
┌──────────────────────────────────────────────────────────────────┐
│ RK3576 OTA升级架构 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ OTA服务器 │──▶│ 客户端Agent│──▶│ 签名验证 │──▶│ 写入非活动分区│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ HTTPS/TLS RSA/ECDSA eMMC写入 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ U-Boot │──▶│ 重启选择 │──▶│ 启动验证 │ │
│ │ slot切换 │ │ 新分区启动│ │ 标记good │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ 失败回滚 │ │
│ └──────────┘ │
└──────────────────────────────────────────────────────────────────┘A/B 分区切换原理:
eMMC 提供 boot0/boot1 两个独立启动分区用于 U-Boot SPL 存储,配合 rootfs-A/rootfs-B 两个系统分区实现完整 A/B 方案。U-Boot 通过环境变量 bootpart 选择启动槽,内核通过 rauc 或 mender 标记启动状态。具体分区布局与启动槽机制以实际发布的 RK3576 SDK 为准(待板上确认)。
1.3 升级框架对比
| 特性 | RAUC | swupdate | Mender | sysupgrade |
|---|---|---|---|---|
| 分区方案 | A/B | A/B/单分区 | A/B | 单分区覆盖 |
| 签名验证 | 支持 | 支持 | 支持 | 支持 |
| 差量升级 | 插件支持 | webupdate | 支持 | 不支持 |
| U-Boot升级 | 支持 | 支持 | 支持 | 部分支持 |
| 回滚机制 | 自动 | 手动/自动 | 自动 | 无 |
| 适用场景 | 通用嵌入式 | 轻量级 | Yocto体系 | OpenWrt |
| RK3576推荐度 | 推荐 | 可选 | 可选 | 特定场景 |
2. 硬件准备
2.1 硬件清单
| 序号 | 硬件名称 | 型号/规格 | 数量 | 用途 |
|---|---|---|---|---|
| 1 | 核心板 | C-3576BQ V1.0 | 1 | 主控制器(RK3576 SoC) |
| 2 | 底板 | ShimetaPi M4-R2 V10 | 1 | 外设接口 |
| 3 | eMMC存储 | 32GB(板载,标配) | 1 | A/B双分区固件存储 |
| 4 | 以太网 | 千兆网口(底板 RJ45) | 1 | OTA网络下载 |
| 5 | USB Type-C线 | 数据线 | 1 | OTG烧录恢复(接口位置以原理图 S-04 为准) |
| 6 | 电源 | 5V/2A(Type-C 或 6pin) | 1 | 空载最小 1000mA |
| 7 | 调试串口线 | USB 转 TTL(3.3V) | 1 | 串口调试(1500000波特率) |
2.2 硬件确认
# RK3576 OTA硬件检查
echo "=== eMMC设备 ==="
lsblk | grep mmcblk0
cat /sys/block/mmcblk0/device/name 2>/dev/null
cat /sys/block/mmcblk0/size 2>/dev/null | awk '{printf "容量: %.1f GB\n", $1*512/1024/1024/1024}'
echo "=== 网络接口 ==="
ip -br link show | grep -E "eth0|end0"
echo "=== eMMC分区表 ==="
sudo fdisk -l /dev/mmcblk0 2>/dev/null
echo "=== 启动槽状态 ==="
cat /sys/class/block/mmcblk0boot0/ro 2>/dev/null && echo "boot0只读状态: $(cat /sys/class/block/mmcblk0boot0/ro)"
cat /sys/class/block/mmcblk0boot1/ro 2>/dev/null && echo "boot1只读状态: $(cat /sys/class/block/mmcblk0boot1/ro)"3. 软件准备
3.1 RAUC安装(推荐方案)
# 安装依赖
sudo apt update
sudo apt install -y \
libglib2.0-dev libcurl4-openssl-dev libssl-dev \
libsystemd-dev libdbus-1-dev libjson-glib-dev \
meson ninja-build squashfs-tools
# 编译安装RAUC
git clone --recursive https://github.com/rauc/rauc.git
cd rauc && meson setup builddir
ninja -C builddir && sudo ninja -C builddir install
# 启动RAUC守护进程
sudo systemctl enable raucd
sudo systemctl start raucd
rauc --version3.2 swupdate安装(备选方案)
# 安装swupdate依赖
sudo apt install -y \
libconfig-dev libcurl4-openssl-dev libarchive-dev \
libsystemd-dev libdbus-1-dev libxml2-dev \
lua5.3 liblua5.3-dev
# 编译安装swupdate
git clone https://github.com/sbabic/swupdate.git
cd swupdate
make menuconfig # 启用A/B分区、HTTPS、Hawkbit支持
make -j$(nproc)
sudo make install
# 启动swupdate
sudo systemctl enable swupdate
sudo systemctl start swupdate
swupdate --version3.3 Mender Agent安装(备选方案)
# Mender客户端安装(版本以实际发布为准)
wget https://downloads.mender.io/client/debian/11/arm64/mender-client_4.0.0+debian_arm64.deb
sudo dpkg -i mender-client_4.0.0+debian_arm64.deb
# 配置Mender
sudo tee /etc/mender/mender.conf << 'EOF'
{
"InventoryPollIntervalSeconds": 300,
"UpdatePollIntervalSeconds": 900,
"RetryPollIntervalSeconds": 300,
"RootfsPartA": "/dev/mmcblk0p3",
"RootfsPartB": "/dev/mmcblk0p4",
"ServerURL": "https://mender.example.com",
"TenantToken": "<your-tenant-token>"
}
EOF
sudo systemctl enable mender-client
sudo systemctl start mender-client说明:上述分区编号为通用示例,实际分区布局以板载固件分区表为准。
3.4 签名工具与密钥生成
# 生成RSA-4096签名密钥对
mkdir -p /etc/ota/keys && cd /etc/ota/keys
# 生成私钥(签名服务器使用,设备端不存储)
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:4096
# 生成公钥(设备端验证使用)
openssl rsa -pubout -in private.pem -out public.pem
# 生成CA证书
openssl req -new -x509 -key private.pem -out ca.pem \
-days 3650 -subj "/CN=RK3576 OTA CA/O=ShimetaPi"
# 验证密钥
openssl rsa -in private.pem -check -noout && echo "私钥有效"
openssl rsa -pubin -in public.pem -noout -text | head -24. 操作步骤
4.1 eMMC分区方案
RK3576 eMMC A/B 分区典型布局(实际分区布局以固件分区表为准):
# 分区方案概览(通用示例)
cat << 'EOF'
eMMC A/B分区布局(控制器节点以实际 SDK 为准):
┌──────────┬───────────┬────────┬──────────────────────┐
│ 分区 │ 起始块 │ 大小 │ 用途 │
├──────────┼───────────┼────────┼──────────────────────┤
│ boot0 │ 0x000000 │ 4MB │ U-Boot SPL A │
│ boot1 │ 0x004000 │ 4MB │ U-Boot SPL B │
│ rootfsA │ 0x008000 │ 8GB │ 系统分区A │
│ rootfsB │ 0x208000 │ 8GB │ 系统分区B │
│ data │ 0x408000 │ 剩余 │ 用户数据(不随OTA擦除)│
└──────────┴───────────┴────────┴──────────────────────┘
EOF# 创建A/B分区(警告:会清除所有数据;分区起始偏移以实际分区表为准)
sudo parted -s /dev/mmcblk0 mklabel gpt
sudo parted -s /dev/mmcblk0 mkpart primary 1MiB 5MiB # boot0
sudo parted -s /dev/mmcblk0 mkpart primary 5MiB 9MiB # boot1
sudo parted -s /dev/mmcblk0 mkpart primary 9MiB 8193MiB # rootfsA
sudo parted -s /dev/mmcblk0 mkpart primary 8193MiB 16385MiB # rootfsB
sudo parted -s /dev/mmcblk0 mkpart primary 16385MiB 100% # data
# 格式化系统分区
sudo mkfs.ext4 -L rootfsA /dev/mmcblk0p3
sudo mkfs.ext4 -L rootfsB /dev/mmcblk0p4
sudo mkfs.ext4 -L data /dev/mmcblk0p5
# 格式化boot分区(原始分区,由U-Boot直接访问)
sudo dd if=/dev/zero of=/dev/mmcblk0boot0 bs=4M count=1
sudo dd if=/dev/zero of=/dev/mmcblk0boot1 bs=4M count=14.2 RAUC系统配置
# /etc/rauc/system.conf
[system]
compatible = "shimetapi,rk3576"
variant = ""
bootloader = "custom"
[keyring]
path = "/etc/rauc/keyring.pem"
[slot.bootloader.0]
device = "/dev/mmcblk0boot0"
type = "raw"
bootname = "0"
[slot.bootloader.1]
device = "/dev/mmcblk0boot1"
type = "raw"
bootname = "1"
[slot.rootfs.0]
device = "/dev/mmcblk0p3"
type = "ext4"
bootname = "0"
bootargs = "root=/dev/mmcblk0p3 rw rootwait console=ttyS2,1500000"
[slot.rootfs.1]
device = "/dev/mmcblk0p4"
type = "ext4"
bootname = "1"
bootargs = "root=/dev/mmcblk0p4 rw rootwait console=ttyS2,1500000"
[data]
device = "/dev/mmcblk0p5"
type = "ext4"
[mark-good]
service = "rauc-mark-good.service"说明:
compatible标识需与实际 SDK 固件保持一致;分区编号以实际分区表为准。
4.3 U-Boot Bootloader升级
U-Boot 是 OTA 升级中最关键也最危险的环节。升级 bootloader 需谨慎操作:
# 打包RAUC固件包(含bootloader)
#!/bin/bash
VERSION="$1"
BUILD_DIR="/tmp/rauc-build"
OUTPUT_DIR="/opt/ota/firmware"
mkdir -p $BUILD_DIR/{bootloader,rootfs}
# 复制bootloader镜像
cp u-boot.img $BUILD_DIR/bootloader/
cp idbloader.img $BUILD_DIR/bootloader/
cp trust.img $BUILD_DIR/bootloader/
# 打包rootfs为ext4镜像
sudo dd if=/dev/zero of=$BUILD_DIR/rootfs/rootfs.img bs=1M count=8192
sudo mkfs.ext4 -F $BUILD_DIR/rootfs/rootfs.img
mkdir -p /tmp/rootfs-mount
sudo mount $BUILD_DIR/rootfs/rootfs.img /tmp/rootfs-mount
sudo tar -cf - -C /path/to/rootfs . | sudo tar -xf - -C /tmp/rootfs-mount
sudo umount /tmp/rootfs-mount
# 创建RAUC manifest
cat > $BUILD_DIR/manifest.raucm << EOF
[update]
compatible=shimetapi,rk3576
version=$VERSION
[image.bootloader]
filename=bootloader.img
size=$(stat -c%s $BUILD_DIR/bootloader/u-boot.img)
[image.rootfs]
filename=rootfs.img
size=$(stat -c%s $BUILD_DIR/rootfs/rootfs.img)
EOF
# 生成RAUC bundle
rauc bundle \
--cert=/etc/ota/keys/ca.pem \
--key=/etc/ota/keys/private.pem \
$BUILD_DIR \
$OUTPUT_DIR/update-${VERSION}.raucb4.4 安全OTA传输(HTTPS)
# 使用Let's Encrypt证书配置HTTPS OTA服务器
sudo apt install -y certbot nginx
# 申请证书
sudo certbot certonly --standalone -d ota.example.com
# Nginx OTA服务器配置
sudo tee /etc/nginx/sites-available/ota << 'EOF'
server {
listen 443 ssl;
server_name ota.example.com;
ssl_certificate /etc/letsencrypt/live/ota.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ota.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location /api/v1/check {
proxy_pass http://127.0.0.1:8080/api/v1/check;
}
location /firmware/ {
alias /opt/ota/firmware/;
autoindex off;
add_header Content-Disposition "attachment";
}
}
EOF
sudo ln -s /etc/nginx/sites-available/ota /etc/nginx/sites-enabled/
sudo systemctl reload nginx4.5 差量升级(Delta Update)
差量升级通过比较新旧固件生成 delta 包,减少下载数据量:
# 使用xdelta3生成差量包
sudo apt install -y xdelta3
# 生成差量包(需旧版本固件作为参考)
xdelta3 -e -s old-rootfs.img new-rootfs.img delta.bin
# 在设备端应用差量包
xdelta3 -d -s old-rootfs.img delta.bin recovered-new-rootfs.img
# 验证恢复后的镜像
sha256sum recovered-new-rootfs.img new-rootfs.img
# 两个SHA256值应相同4.6 自动升级与回滚
# /etc/rauc/rauc-hook.sh - 启动成功标记脚本
#!/bin/bash
# 系统启动成功后调用,标记当前分区为good
rauc status mark-good
logger "OTA: 启动成功,已标记当前分区为good"
# /etc/systemd/system/rauc-mark-good.service
[Unit]
Description=Mark boot as good
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/etc/rauc/rauc-hook.sh
[Install]
WantedBy=multi-user.target
# 启用服务
sudo systemctl enable rauc-mark-good.service# 自动回滚检查脚本
#!/bin/bash
# /opt/ota/check-rollback.sh
BOOT_COUNT=$(cat /sys/class/block/mmcblk0boot0/boot_count 2>/dev/null || echo "0")
MAX_BOOT=3
if [ "$BOOT_COUNT" -ge "$MAX_BOOT" ]; then
echo "启动失败次数过多($BOOT_COUNT),执行回滚..."
CURRENT_SLOT=$(rauc status --output-format=compact | grep -o '[01]' | head -1)
NEW_SLOT=$((1 - CURRENT_SLOT))
# 切换U-Boot启动槽
fw_setenv bootpart $NEW_SLOT
# 重启
reboot
fi说明:bootloader 升级具有风险,建议量产前在开发板上完成充分测试(待板上验证)。
5. 代码详解
5.1 RAUC升级客户端
/* /opt/ota/ota_client.c - RK3576 OTA客户端核心逻辑 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <openssl/sha.h>
#include <json-c/json.h>
#define OTA_SERVER "https://ota.example.com"
#define CERT_PATH "/etc/ota/ca.pem"
#define FIRMWARE_DIR "/tmp/ota"
/* 下载进度回调 */
static size_t progress_callback(void *ptr, size_t size, size_t nmemb, void *data) {
double *total = (double *)data;
*total += (double)(size * nmemb);
fprintf(stderr, "\r下载进度: %.1f%%", *total / (1024*1024) * 100);
return size * nmemb;
}
/* HTTPS下载固件 */
int download_firmware(const char *url, const char *output) {
CURL *curl;
CURLcode res;
double downloaded = 0;
curl = curl_easy_init();
if (!curl) return -1;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CAINFO, CERT_PATH);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &downloaded);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
FILE *fp = fopen(output, "wb");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
fclose(fp);
curl_easy_cleanup(curl);
return (res == CURLE_OK) ? 0 : -1;
}
/* SHA-256校验 */
int verify_sha256(const char *file, const char *expected) {
unsigned char hash[SHA256_DIGEST_LENGTH];
unsigned char buf[8192];
SHA256_CTX ctx;
FILE *fp;
SHA256_Init(&ctx);
fp = fopen(file, "rb");
while (fread(buf, 1, sizeof(buf), fp) > 0)
SHA256_Update(&ctx, buf, sizeof(buf));
SHA256_Final(hash, &ctx);
fclose(fp);
char hex[65];
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
sprintf(hex + i*2, "%02x", hash[i]);
hex[64] = '\0';
return (strcmp(hex, expected) == 0) ? 0 : -1;
}
/* 检查更新 */
int check_update(const char *server, char *version, char *url) {
char api_url[256];
snprintf(api_url, sizeof(api_url), "%s/api/v1/check", server);
/* 使用libcurl获取JSON响应并解析 */
/* ... HTTP GET请求省略 ... */
return 0;
}
int main(int argc, char *argv[]) {
curl_global_init(CURL_GLOBAL_DEFAULT);
char version[32], url[512];
if (check_update(OTA_SERVER, version, url) == 0) {
printf("发现新版本: %s\n", version);
char output[256];
snprintf(output, sizeof(output), "%s/update-%s.raucb", FIRMWARE_DIR, version);
if (download_firmware(url, output) == 0) {
/* 调用RAUC安装 */
char cmd[512];
snprintf(cmd, sizeof(cmd), "rauc install %s", output);
system(cmd);
}
}
curl_global_cleanup();
return 0;
}5.2 分区管理程序
/* /opt/ota/partition_mgr.c - eMMC A/B分区管理 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define EMMC_BOOT0 "/sys/class/block/mmcblk0boot0/ro"
#define EMMC_BOOT1 "/sys/class/block/mmcblk0boot1/ro"
/* 获取当前活动槽位 (0=A, 1=B) */
int get_active_slot(void) {
FILE *fp = fopen(EMMC_BOOT0, "r");
if (!fp) return 0;
int ro;
fscanf(fp, "%d", &ro);
fclose(fp);
return ro; /* boot0只读=0表示A活动, =1表示B活动 */
}
/* 切换活动槽位 */
int switch_slot(int new_slot) {
FILE *fp;
int old_slot = 1 - new_slot;
/* 旧槽设为只读 */
fp = fopen(new_slot == 0 ? EMMC_BOOT1 : EMMC_BOOT0, "w");
if (fp) { fprintf(fp, "1"); fclose(fp); }
/* 新槽设为读写 */
fp = fopen(new_slot == 0 ? EMMC_BOOT0 : EMMC_BOOT1, "w");
if (fp) { fprintf(fp, "0"); fclose(fp); }
printf("槽位切换: %d -> %d\n", old_slot, new_slot);
return 0;
}
/* 打印分区状态 */
void print_status(void) {
FILE *fp;
int ro;
printf("=== RK3576 A/B分区状态 ===\n");
printf("当前活动槽位: %d (%s)\n", get_active_slot(),
get_active_slot() == 0 ? "A" : "B");
fp = fopen(EMMC_BOOT0, "r");
if (fp) { fscanf(fp, "%d", &ro); fclose(fp);
printf("boot0 (槽A): %s\n", ro ? "非活动" : "活动"); }
fp = fopen(EMMC_BOOT1, "r");
if (fp) { fscanf(fp, "%d", &ro); fclose(fp);
printf("boot1 (槽B): %s\n", ro ? "非活动" : "活动"); }
}
int main(int argc, char *argv[]) {
if (argc < 2) { print_status(); return 0; }
if (strcmp(argv[1], "switch") == 0 && argc >= 3) {
int slot = atoi(argv[2]);
return switch_slot(slot);
}
return 0;
}5.3 固件签名验证
/* /opt/ota/firmware_verify.c - 固件完整性验证 */
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
/* 验证固件签名 */
int verify_firmware_signature(const char *firmware,
const char *sig_file,
const char *pubkey_pem) {
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
EVP_PKEY *pkey = NULL;
FILE *fp;
unsigned char buf[8192];
size_t len;
int result = -1;
/* 加载公钥 */
fp = fopen(pubkey_pem, "r");
if (!fp) goto cleanup;
pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
fclose(fp);
if (!pkey) goto cleanup;
/* 初始化验证上下文 */
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey);
/* 读取固件并更新摘要 */
fp = fopen(firmware, "rb");
if (!fp) goto cleanup;
while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
EVP_DigestVerifyUpdate(mdctx, buf, len);
fclose(fp);
/* 读取签名 */
unsigned char sig[512];
fp = fopen(sig_file, "rb");
if (!fp) goto cleanup;
int sig_len = fread(sig, 1, sizeof(sig), fp);
fclose(fp);
/* 执行验证 */
result = EVP_DigestVerifyFinal(mdctx, sig, sig_len);
cleanup:
EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(pkey);
return (result == 1) ? 0 : -1;
}
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "用法: %s <固件> <签名文件> <公钥>\n", argv[0]);
return 1;
}
return verify_firmware_signature(argv[1], argv[2], argv[3]) == 0
? (printf("签名验证通过\n"), 0)
: (fprintf(stderr, "签名验证失败\n"), 1);
}6. 常见问题
6.1 OTA升级后系统无法启动
原因: 新固件不兼容,启动槽配置错误。
解决方案:
# 在U-Boot命令行中手动切换启动槽
# 串口终端按任意键中断自动启动
printenv bootpart # 查看当前启动槽
setenv bootpart 0 # 切换到槽A (0或1)
saveenv
reset
# 如果仍无法启动,使用USB OTG恢复
# 连接底板 OTG 接口,使用 RKDevTool 重新烧录6.2 固件签名验证失败
原因: 公钥与签名服务器私钥不匹配,或证书过期。
# 检查RAUC日志
sudo journalctl -u raucd -n 50
# 验证密钥匹配
openssl rsa -pubin -in /etc/rauc/keyring.pem -text -noout | head -5
# 检查证书有效期
openssl x509 -in /etc/rauc/keyring.pem -noout -dates
# 重新导入密钥环
rauc status mark-active6.3 差量升级失败
原因: 参考镜像不匹配,磁盘空间不足。
# 检查当前系统镜像(分区号以实际分区表为准)
md5sum /dev/mmcblk0p3 # 与delta参考对比
# 检查/tmp空间
df -h /tmp
# 清理临时文件后重试
rm -rf /tmp/rauc-*
rauc install --force firmware.raucb6.4 swupdate Web界面无法访问
原因: 网络配置或防火墙问题。
# 检查swupdate进程
ps aux | grep swupdate
# 检查端口监听
ss -tlnp | grep 8080
# 检查防火墙
sudo ufw status
sudo iptables -L INPUT -n
# 手动启动swupdate(带Web界面)
swupdate -l port=8080 -L www6.5 eMMC写入失败
原因: eMMC 坏块、权限不足。
# 检查eMMC健康状态
cat /sys/block/mmcblk0/device/life_time_est_typ_a
cat /sys/block/mmcblk0/device/life_time_est_typ_b
# 检查写保护状态
cat /sys/block/mmcblk0boot0/ro
cat /sys/block/mmcblk0boot1/ro
# 切换为读写模式
echo 0 > /sys/class/block/mmcblk0boot0/ro6.6 网络中断后固件下载不完整
原因: 断点续传未启用或服务器不支持。
# 使用wget断点续传下载
wget -c -O /tmp/firmware.raucb https://ota.example.com/firmware/update.raucb
# 校验下载完整性
sha256sum /tmp/firmware.raucb
# 对比服务器提供的SHA256值7. 相关文档
| 文档名称 | 路径 | 说明 |
|---|---|---|
| 安全启动与加密 | 安全启动与加密 | 固件签名与安全启动 |
| Docker 容器部署 | Docker容器部署 | 容器化应用交付与升级 |
| RTOS 与 Linux 协同 | RTOS与Linux协同 | 多系统分区架构 |
| 网络配置与 SSH | 网络配置与SSH | 网络环境准备 |
| 软件包管理与更新 | 软件包管理与更新 | apt 源与软件更新 |
| 文件系统与存储 | 文件系统与存储 | eMMC 分区与文件系统管理 |
| 系统启动时间优化 | 系统启动时间优化 | 分区与启动链路优化 |
RAUC 官方文档:https://rauc.io/docs/latest/;swupdate 官方文档:https://sbabic.github.io/swupdate/;Mender 官方文档:https://docs.mender.io/
