分析依据:
zephyrproject-rtos/mcumgr(原mcu-tools/mcumgr,2026-08 版本)源码与
protocol.md、transport/smp-console.md、transport/smp-bluetooth.md,以及
mcu-tools/mcuboot的boot/boot_serial实现。
适用对象:MCUboot 串口恢复(serial recovery)、mcumgr / newtmgr 设备管理、Zephyr/Mynewt 的 mcumgr 子系统。
1. 概述与定位
SMP(Simple Management Protocol)是 mcumgr 的设备管理协议,前身是 Apache Mynewt 的
NMP(Newt Manager Protocol)。它定义了一套命令-响应式的管理通道,命令包括:
镜像管理(列出/上传/擦除/复位)、系统信息、统计、配置、日志、崩溃转储、文件系统等。
- 与 MCUboot 的关系:MCUboot 本身不做传输,它的官方配套是 mcumgr;MCUboot 的
串口恢复模式(boot_serial)实现了 SMP 的一个最小子集(镜像 state/upload +
reset/echo),上位机用mcumgr或newtmgr命令即可通过串口给 bootloader 传固件。 - 设计目标:二进制头(固定 8 字节)+ CBOR 载荷,极小、可移植、支持多传输
(串口 / BLE / UDP),对 flash/内存占用友好(典型串口场景只需几 KB)。 - 传输与校验分离:SMP 头里的
len用于分帧重组,串口传输另有 CRC16;
镜像是否可信由 MCUboot 安装时的 SHA-256 + 签名校验 决定,SMP 本身不做加密。
2. 报文格式(8 字节头 + CBOR 载荷)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| OP | 保留(5bit)| Flags | Length(16bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group ID(16bit) | Sequence(8bit) | Command ID(8bit)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CBOR 载荷(Length 字节) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
对应源码结构(mgmt/mgmt.h):
#define MGMT_HDR_SIZE 8
struct mgmt_hdr {
uint8_t nh_op:3; /* 操作码,低 3 位 */
uint8_t _res1:5; /* 保留 */
uint8_t nh_flags; /* 标志,当前恒为 0 */
uint16_t nh_len; /* 载荷长度(CBOR 部分),大端 */
uint16_t nh_group; /* 命令组 ID,大端 */
uint8_t nh_seq; /* 序号,响应原样回显 */
uint8_t nh_id; /* 组内命令 ID */
};
要点:
- 大端序(>8bit 字段),头固定 8 字节。
nh_len只统计 CBOR 载荷,不含 8 字节头。- 响应头 = 请求头基础上
op + 1、seq原样回显、len填响应载荷长度。
操作码(nh_op)
| 值 | 名称 | 方向 |
|---|---|---|
| 0 | MGMT_OP_READ | 请求:读取 |
| 1 | MGMT_OP_READ_RSP | 响应:读结果 |
| 2 | MGMT_OP_WRITE | 请求:写入/执行 |
| 3 | MGMT_OP_WRITE_RSP | 响应:写结果 |
命令组(nh_group)
| 组 ID | 名称 | 用途 |
|---|---|---|
| 0 | MGMT_GROUP_ID_OS | 系统:echo、复位、任务统计等 |
| 1 | MGMT_GROUP_ID_IMAGE | 镜像管理(OTA 核心) |
| 2 | MGMT_GROUP_ID_STAT | 统计 |
| 3 | MGMT_GROUP_ID_CONFIG | 配置 |
| 4 | MGMT_GROUP_ID_LOG | 日志 |
| 5 | MGMT_GROUP_ID_CRASH | 崩溃转储 |
| 6 | MGMT_GROUP_ID_SPLIT | 双核 split 镜像 |
| 7 | MGMT_GROUP_ID_RUN | 运行时命令 |
| 8 | MGMT_GROUP_ID_FS | 文件系统 |
| 9 | MGMT_GROUP_ID_SHELL | shell |
| 64+ | MGMT_GROUP_ID_PERUSER | 用户自定义组 |
组内命令(nh_id)
OS 组(cmd/os_mgmt):
| ID | 名称 | 说明 |
|---|---|---|
| 0 | OS_MGMT_ID_ECHO | 回显 |
| 1 | OS_MGMT_ID_CONS_ECHO_CTRL | 控制台回显开关 |
| 2 | OS_MGMT_ID_TASKSTAT | 任务统计 |
| 3 | OS_MGMT_ID_MPSTAT | 内存池统计 |
| 4 | OS_MGMT_ID_DATETIME_STR | 日期时间 |
| 5 | OS_MGMT_ID_RESET | 复位设备 |
IMAGE 组(cmd/img_mgmt):
| ID | 名称 | 说明 |
|---|---|---|
| 0 | IMG_MGMT_ID_STATE | 查询镜像列表/状态,或 test/confirm |
| 1 | IMG_MGMT_ID_UPLOAD | 上传镜像分片(OTA 关键命令) |
| 2 | IMG_MGMT_ID_FILE | 上传文件 |
| 3 | IMG_MGMT_ID_CORELIST | 列出崩溃转储 |
| 4 | IMG_MGMT_ID_CORELOAD | 读取崩溃转储 |
| 5 | IMG_MGMT_ID_ERASE | 擦除镜像 |
错误码(响应 CBOR 中的 rc 字段)
| 值 | 名称 | 含义 |
|---|---|---|
| 0 | MGMT_ERR_EOK | 成功 |
| 1 | MGMT_ERR_EUNKNOWN | 未知错误 |
| 2 | MGMT_ERR_ENOMEM | 内存不足 |
| 3 | MGMT_ERR_EINVAL | 参数非法 |
| 4 | MGMT_ERR_ETIMEOUT | 超时 |
| 5 | MGMT_ERR_ENOENT | 不存在 |
| 6 | MGMT_ERR_EBADSTATE | 状态不允许 |
| 7 | MGMT_ERR_EMSGSIZE | 响应过大 |
| 8 | MGMT_ERR_ENOTSUP | 不支持 |
| 9 | MGMT_ERR_ECORRUPT | 数据损坏 |
| 256 | MGMT_ERR_EPERUSER | 用户自定义起始 |
多请求拼接
一个 SMP 报文可以拼接多个请求,每个请求头 + 载荷连续存放,每个请求起始偏移必须是
4 的倍数(不足补填充字节)。服务器按顺序处理,每个请求单独回一帧;一旦某个请求出错,
剩余请求不再处理。
3. CBOR 载荷
载荷是 CBOR(RFC 7049) 编码的键值映射。选 CBOR 是因为它比 JSON 紧凑、编码解码
代码量小(tinycbor),且天然支持二进制数据。
- 键通常是文本字符串,如
"images"、"off"、"len"、"data"、"rc"、"hash"。 - 数组/映射常用不定长编码(首字节
0x9f/0xbf,结束符0xff)。 - 二进制数据(镜像分片)用 base64 编码成字符串放入
"data"字段(保持文本帧兼容)。
例:image list 响应(组=1,id=0,op=READ_RSP)
抓包字节(来自 mcumgr protocol.md):
01 01 00 7b 00 01 00 00 bf 66 69 6d 61 67 65 73 9f bf 64 73 6c 6f 74 00
67 76 65 72 73 69 6f 6e 65 30 2e 33 2e 30 64 68 61 73 68 58 20 d2 4c b3
05 13 54 17 2b b5 10 9f 9c b4 ae 78 61 d9 6d 6a fd fc 46 db 48 2c eb 2d
34 a8 a7 8e d0 68 62 6f 6f 74 61 62 6c 65 f5 67 70 65 6e 64 69 6e 67 f4
69 63 6f 6e 66 69 72 6d 65 64 f5 66 61 63 74 69 76 65 f5 ff ff 6b 73 70
6c 69 74 53 74 61 74 75 73 00 ff
解析:
- 头:
01(op=READ_RSP)01(flags)00 7b(len=123)00 01(group=IMAGE)00(seq)00(id=STATE) - CBOR:
bf不定长 map →"images"→ 不定长数组 → 每项{slot, version, hash(32B), bootable, pending, confirmed, active}→"splitStatus":0 - 这是 MCUboot / mcumgr 判断“当前固件、待更新固件”的标准查询。
4. 传输层
4.1 串口(console / MCUboot serial recovery)—— 重点
帧格式(transport/smp-console.md + MCUboot boot_serial.c):
首帧:
0x06 0x09 <- 帧起始标记(SHELL_NLIP_PKT_START1/2)
base64( <16bit 包长度> <8字节SMP头> <CBOR载荷> <16bit CRC16> )
0x0A <- 换行结束
续帧(长度超过 127 字节时):
0x04 0x14
base64( <续传数据> <16bit CRC16>(若为最后一帧) )
0x0A
- 包长度:大端 16bit,值 = 未编码的 body + 2 字节 CRC 的总长度。
- CRC16:多项式
0x1021、初值0、无反射(CRC16-CCITT 变体),覆盖“未编码的
SMP 头 + 载荷”,以大端附加在末尾;接收端对“长度字段之后、含 CRC 的整段”再算一次,
结果为 0 即通过(MCUboot 的校验方式)。 - 分帧:整帧(含头、CRC、换行)不超过 127 字节,超长拆成多个续帧。
- base64:整帧(除 06 09 / 0A 外)做 base64,使协议对任意终端/中转都安全。
- MCUboot 的
boot_serial是单帧简化实现:直接发06 09 + base64(整包) + \n\r,
接收缓冲 512 字节,不做续帧;帧内校验逻辑与上述一致。
4.2 UDP
- 端口 133(newtmgr/mcumgr 约定),每个 UDP 数据报 = 8 字节 SMP 头 + CBOR 载荷,
无额外帧格式,请求-响应一对一。 - 校验:UDP 数据报自带 16bit 校验和;链路可靠性靠超时重发(应用层或 UDP 之上)。
- 典型用法:Zephyr 的
smp_udp传输 + lwIP/原生 IP 栈,上位机mcumgr -c udp。
4.3 BLE
- GATT 服务 UUID:
8D53DC1D-1DB7-4CD3-868B-8A527460AA84 - 特征 UUID:
DA2E7828-FBCE-4E01-AE9E-261174997C48 - 请求用 Write / WriteWithoutResponse,响应用同一特征的 Notification。
- 大报文直接按 GATT 命令切片,无额外分帧(BLE 保证有序)。
- 校验:链路层 CRC + 可选配对加密(应用层不额外加 CRC)。
| 传输 | 分帧 | 校验 | 典型延迟/吞吐 |
|---|---|---|---|
| 串口 console | 06 09 / 04 14 + base64 + 127B 分帧 | CRC16-CCITT | 115200bps,几十 KB/s |
| MCUboot serial recovery | 06 09 + base64 单帧 | CRC16-CCITT | 同上 |
| UDP | 无(一报一帧) | UDP 校验和 + 重发 | 取决于网络 |
| BLE | GATT 切片 | 链路层 + 可选加密 | 取决于 MTU/连接间隔 |
5. 固件上传流程(image upload,组 1 / id 1)
上传请求是 WRITE(op=2)命令,CBOR 载荷:
{
"off" : <本片在镜像中的偏移>,
"len" : <镜像总长度>, // 仅 off==0 的首片携带
"data" : <base64 编码的本片二进制>,
"sha" : <base64 编码的镜像 SHA-256>, // MCUboot serial recovery 可选校验
}
服务器响应(WRITE_RSP,op=3):
{ "off" : <已接收到的下一偏移>, "rc" : 0 }
流程:
- 上位机按 MTU(如 512B/片)把签名固件分片,逐片发
image upload(off 递增)。 - 每片回
off(= 收到的累计偏移);上位机用返回值推进,实现流控与续传。 - 首片带
len,服务器据此知道总大小;可选带sha做整镜像摘要校验。 - 最后一片的
off + 片长 == len时上传完成,服务器落盘到 UPDATE 分区。 - 上位机发
image state(WRITE)携带"confirm":true(或"test":true)激活镜像;
再发 OS 组reset(组 0 / id 5)让设备重启。 - 重启后 MCUboot 对 UPDATE 分区做 SHA-256 完整性 + 签名认证,通过则 swap/安装并
按需回滚,实现“确认成功后才固化”。
MCUboot serial recovery 只实现了这个流程的最小子集(state、upload、reset、echo),
其余组返回空响应;CDDL 定义见 boot/boot_serial/src/serial_recovery.cddl。
6. 报文实例(手工构造,便于抓包对照)
6.1 image list 请求 → 串口帧
请求头:op=READ(0)、flags=0、len=0、group=1(IMAGE)、seq=0、id=0(STATE)
SMP 报文 : 00 00 00 00 00 01 00 00
包长度 : 0x000A(8 头 + 2 CRC)
CRC16 : D8A8
串口帧 : 06 09 41 41 6F 41 41 41 41 41 41 41 45 41 41 4E 69 6F 0A
= 06 09 + base64("00 0A 00 00 00 00 00 01 00 00 D8 A8") + 0A
= 06 09 + "AAoAAAAAAAEAANio" + 0A
6.2 image upload 首片请求(off=0,len=16,16 字节数据)
SMP 头 : 02 00 00 2A 00 01 00 01 (op=WRITE, len=0x2A, group=1, seq=0, id=UPLOAD)
CBOR : A3 63 6F 66 66 00 63 6C 65 6E 10 64 64 61 74 61 4C 18
41 41 45 43 41 77 51 46 42 67 63 49 43 51 6F 4C 44 41 30 4F 44 77 3D 3D
= { "off":0, "len":16, "data":bstr("AAECAwQFBgcICQoLDA0ODw==") }
包长度 : 0x0034(头 8 + CBOR 42 + CRC 2)
CRC16 : 9AA5
解码验证:
A3= map(3);63 6F 66 66= 文本(3) “off”;00= 063 6C 65 6E= 文本(3) “len”;10= 1664 64 61 74 61= 文本(4) “data”;4C 18= 字节串(24),
解码AAECAwQFBgcICQoLDA0ODw==得00 01 02 ... 0F
7. 校验与安全机制总结
| 层 | 机制 | 作用 |
|---|---|---|
| 串口传输层 | 06 09 帧同步 + base64 + CRC16-CCITT | 检错/防误码,重发靠上位机 |
| UDP 传输层 | UDP 16bit 校验和 + 应用超时重发 | 检错 |
| BLE 传输层 | 链路 CRC + 可选配对加密 | 检错/可选机密性 |
| SMP 应用层 | len/off 校验、rc 错误码 | 逻辑正确性 |
| 镜像层(MCUboot) | SHA-256 完整性 + ECDSA/Ed25519/RSA 签名 | 最终信任根 |
注意:SMP 本身没有加密和认证,它假定传输链路可信(串口物理可信、UDP 走可信网络、
BLE 可加配对)。真正的安全边界在 MCUboot 的镜像签名校验——即使传输被篡改,签名验证
也会失败。
8. 调试与工具
- 上位机:
mcumgr(apache/mynewt-mcumgr-cli)或旧版newtmgr- 串口:
mcumgr -c serial -l DEBUG image list - UDP:
mcumgr -c udp image upload -n <image> - 加
-l DEBUG可看到请求/响应头字段与原始字节(如 protocol.md 中的抓包输出)。
- 串口:
- 抓包:串口用带时间戳的串口记录/逻辑分析仪;UDP 用 Wireshark 过滤
udp.port==133。 - 参考实现源码:
mcumgr-master\protocol.md—— 协议笔记与抓包mcumgr-master\mgmt\include\mgmt\mgmt.h—— 头结构、组、错误码mcumgr-master\smp\src\smp.c—— 请求处理/多请求拼接mcumgr-master\transport\smp-console.md、smp-bluetooth.md—— 传输规范...\mcumgr-master\cmd\img_mgmt—— 镜像上传实现mcuboot-master\boot\boot_serial\src\boot_serial.c—— MCUboot 串口恢复(帧收发 + CRC)

407

被折叠的 条评论
为什么被折叠?



