设备通讯协议(v0.1)
本文为历史版本协议文档,对应
ProtocolVersion = 0.1。新设备或新接入项目请优先参考 设备通讯协议(v1.0);仅在维护仍运行 v0.1 固件的设备时查阅本文。
设备通讯协议(v0.1)
Section titled “设备通讯协议(v0.1)”文档版本: v0.1
最后更新: 2026-03-16
适用对象: 设备固件开发者、第三方硬件集成商
📝 协议更新日志
Section titled “📝 协议更新日志”| 日期 | 更新内容 |
|---|---|
| 2026-03-16 | 1. 设备状态上报新增 sleepTime 字段,用于上报设备休眠时间(单位:分钟) → 设备状态上报 |
| 2026-03-12 | 1. 上报 NN 号调整为在 firmware 固件列表中上报(由 firmware[].nn 承载) → 设备状态上报2. 设备状态上报新增 charging 字段,用于标识设备是否处于充电状态 → 设备状态上报 |
| 2026-02-26 | 1. 打印任务资源列表新增 compression 字段,支持指示资源文件是否启用压缩(none 或 gzip) → 打印任务下发2. 固件升级任务新增 compression 字段,支持指示固件文件是否启用压缩(none 或 gzip) → 固件升级任务 |
| 2026-02-06 | 1. 新增独立的 Pull 主题 devices/{sn}/up/pull,设备主动拉取打印任务或固件升级任务 → 任务拉取请求2. 新增 Pull Ack 主题 devices/{sn}/down/pull/ack,用于回复无可用任务情况 → Pull响应3. 移除打印任务回执中的 pull 状态,拉取任务改用独立 Pull 主题 → 打印任务回执4. 移除设备状态上报中的 firmwareId 字段 → 设备状态上报5. 移除固件升级任务下发中的 firmwareId 字段 → 固件升级任务6. 设备不再在首次状态上报时获取任务,改为主动 Pull 拉取 → 主动拉取任务 7. 设备端负责判断本地错误是否应该 Pull,云端不再在 Pull 时判断错误 → 云端响应逻辑 8. 去除掉定时pull的需求,保留根据资源情况自行pull,其余情况由云端来保证任务的稳定性 → 任务拉取请求 9. 打印任务回执中 seq 字段改为 seqs 数组,支持批量确认多个资源 → seqs字段说明 |
| 2026-02-03 | 1. 设备状态上报新增 avgLatency(平均延时)和 packetLossRate(丢包率)字段 → 设备状态上报2. 新增设备事件上行主题 devices/{sn}/up/event,用于设备上报事件通知 → 设备事件上报3. 新增停止打印事件类型( stop_print),云端收到后会清除该设备的打印队列 → 停止打印事件说明 |
| 2026-01-27 | 1. 新增设备状态错误码 6013 纸张类型错误,用于标识纸张类型与任务要求不匹配的情况 → 设备状态错误码 |
| 2026-01-26 | 1. Bootstrap API 响应新增 keepalive、cleanSession 和 stateReportInterval 字段,支持服务端配置MQTT心跳时间、会话清洁策略和设备状态上报周期 → 响应字段说明2. 设备引导时间戳校验简化,仅校验13位长度,移除±5分钟时间窗口限制,避免设备初次启动时时间不准导致引导失败 → 请求字段说明 3. 设备状态上报新增 paperType 字段(纸张类型) → 设备状态上报4. 设备状态上报错误码字段由 errorCode 调整为 errCode,并改为 int 数组类型(支持多个错误码) → 设备状态上报5. 移除下行控制指令 test_print(测试页改为由云端自行拼接并触发打印) → 设备控制指令 |
| 2025-11-20 | 初始版本发布,定义基础通信协议 |
- Step 1: Bootstrap API - 获取连接参数
- Step 2: 连接 MQTT Broker
- Step 3: 订阅下行主题
- Step 4: 上报设备状态
- Step 5: 主动拉取任务
- Step 6: 处理平台下发的任务
- 主题命名规范
- 通用消息格式
- 上行消息:设备状态上报
- 上行消息:打印任务回执
- 上行消息:固件升级回执
- 上行消息:设备控制指令回执
- 上行消息:设备事件上报
- 上行消息:任务拉取请求(Pull)
- 下行消息:Pull 响应(无可用任务)
- 下行消息:打印任务下发
- 下行消息:固件升级任务
- 下行消息:设备控制指令
- Q1: 设备时间不准确怎么办?
- Q2: 如何处理网络波动导致的消息丢失?
- Q3: 打印任务太大,如何优化下载速度?
- Q4: 固件升级失败后如何回滚?
- Q5: 如何判断是否需要主动拉取任务?
- Q6: 设备 SN 和 Secret 如何获取?
- Q7: 如何调试 MQTT 连接问题?
🚀 快速开始
Section titled “🚀 快速开始”- 设备已向平台注册,获得
SN(序列号)和Secret(密钥) - 设备支持 HTTPS 和 MQTT 协议
- 设备具备基本的文件下载和哈希校验能力
- 建议设备时间与 NTP 服务器同步,误差不超过 5 分钟
- 设备上电 → 调用 Bootstrap API 获取连接参数
- 使用返回的参数连接 MQTT Broker
- 订阅下行主题(接收平台命令和 Pull 响应)
- 上报设备状态
- 主动拉取任务(通过 Pull 主题请求打印或固件升级任务)
- 处理平台下发的打印/升级/控制任务
设备引导与连接时序
Section titled “设备引导与连接时序”sequenceDiagram
autonumber
participant Dev as 设备
participant API as Bootstrap API
participant MQTT as EMQX Broker
Note over Dev,MQTT: 1. 设备引导
Dev->>API: POST /api/v1/bootstrap {sn, ts, sign}
API->>API: 验证签名和时间戳
API-->>Dev: 返回 MQTT 参数和主题配置
Note over Dev,MQTT: 2. MQTT 连接
Dev->>MQTT: CONNECT (clientId, username, password)
MQTT->>MQTT: 验证 JWT Token
MQTT-->>Dev: CONNACK (连接成功)
Note over Dev,MQTT: 3. 订阅主题
Dev->>MQTT: SUBSCRIBE devices/{sn}/down/print (QoS 2)
Dev->>MQTT: SUBSCRIBE devices/{sn}/down/upgrade (QoS 2)
Dev->>MQTT: SUBSCRIBE devices/{sn}/down/control (QoS 2)
Dev->>MQTT: SUBSCRIBE devices/{sn}/down/pull/ack (QoS 1)
MQTT-->>Dev: SUBACK
Note over Dev,MQTT: 4. 上报状态
Dev->>MQTT: PUBLISH devices/{sn}/up/state {状态数据}
MQTT-->>Dev: PUBACK
Note over Dev,MQTT: 5. 主动拉取任务
Dev->>MQTT: PUBLISH devices/{sn}/up/pull {拉取请求}
MQTT-->>Dev: PUBACK
打印任务完整流程
Section titled “打印任务完整流程”sequenceDiagram
autonumber
participant Platform as 平台
participant MQTT as EMQX Broker
participant Dev as 设备
Note over Dev,Platform: 1. 设备主动拉取任务
Dev->>MQTT: PUBLISH up/pull {jobId:"", seq:0, firmware:[...]}
MQTT->>Platform: 转发拉取请求
alt 有固件升级任务
Platform->>MQTT: PUBLISH down/upgrade {固件升级任务}
MQTT->>Dev: 转发固件升级任务
Note over Dev: 执行固件升级流程
else 有打印任务
Platform->>MQTT: PUBLISH down/print {打印任务}
MQTT->>Dev: 转发打印任务
Note over Dev: 2. 立即确认收到的资源
Dev->>MQTT: PUBLISH up/print/ack {status:confirm, seqs:[1,2,3...]}
Note right of Dev: 必须在10秒内发送,seqs包含已收到的资源序号
MQTT->>Platform: 转发确认回执
Note over Dev: 3. 下载资源
Dev->>Dev: 下载 resources[i]
Dev->>Dev: 校验 hash
Note over Dev: 4. 打印进度
Dev->>MQTT: PUBLISH up/print/ack {status:printing, seqs:[1]}
MQTT->>Platform: 转发进度回执
Note over Dev: 5. 判断是否需要拉取更多资源
Dev->>Dev: 检查 seq == totalFileNumber?
alt seq < totalFileNumber
Note over Dev: 6. 拉取更多资源
Dev->>MQTT: PUBLISH up/pull {jobId:"xxx", seq:5}
MQTT->>Platform: 请求更多资源
Platform->>MQTT: PUBLISH down/print {新资源列表}
MQTT->>Dev: 转发新资源
Note over Dev: 循环回到步骤5继续下载
else seq >= totalFileNumber
Note over Dev: 7. 完成当前任务
Dev->>MQTT: PUBLISH up/print/ack {status:success, seqs:[1-10]}
MQTT->>Platform: 转发完成回执
Note over Dev: 8. 拉取下一单打印任务
Dev->>MQTT: PUBLISH up/pull {jobId:"", seq:0, firmware:[...]}
MQTT->>Platform: 请求下一单任务
alt 队列中有待打印任务
Platform->>MQTT: PUBLISH down/print {下一单打印任务}
MQTT->>Dev: 转发新任务
else 队列为空
Platform->>MQTT: PUBLISH down/pull/ack {message:"no_task"}
MQTT->>Dev: 回复无可用任务
end
end
else 无可用任务
Platform->>MQTT: PUBLISH down/pull/ack {message:"no_task"}
MQTT->>Dev: 回复无可用任务
end
固件升级流程
Section titled “固件升级流程”sequenceDiagram
autonumber
participant Platform as 平台
participant MQTT as EMQX Broker
participant Dev as 设备
Note over Dev,Platform: 1. 设备主动拉取任务
Dev->>MQTT: PUBLISH up/pull {jobId:"", seq:0, firmware:[...]}
MQTT->>Platform: 转发拉取请求
Platform->>Platform: 检查固件版本,发现有升级任务
Note over Platform,Dev: 2. 升级任务下发
Platform->>MQTT: PUBLISH devices/{sn}/down/upgrade {升级任务}
MQTT->>Dev: 转发升级任务
Note over Dev: 3. 立即确认
Dev->>MQTT: PUBLISH up/upgrade/ack {status:confirm}
MQTT->>Platform: 转发确认回执
Note over Dev: 4. 下载固件
Dev->>Dev: 下载固件文件
Dev->>MQTT: PUBLISH up/upgrade/ack {status:downloading}
MQTT->>Platform: 转发下载进度
Note over Dev: 5. 校验哈希
Dev->>Dev: 验证 SHA256 hash
Note over Dev: 6. 执行升级
Dev->>MQTT: PUBLISH up/upgrade/ack {status:upgrading}
MQTT->>Platform: 转发升级状态
Dev->>Dev: 应用新固件
Dev->>Dev: 重启设备
Note over Dev: 7. 重启后上报
Dev->>MQTT: PUBLISH up/upgrade/ack {status:success}
MQTT->>Platform: 转发成功回执
Dev->>MQTT: PUBLISH up/state {新固件版本}
MQTT->>Platform: 同步设备状态
Note over Dev: 8. 拉取打印任务
Dev->>MQTT: PUBLISH up/pull {jobId:"", seq:0, firmware:[新版本]}
MQTT->>Platform: 请求打印任务
设备控制指令流程
Section titled “设备控制指令流程”sequenceDiagram
autonumber
participant Platform as 平台
participant MQTT as EMQX Broker
participant Dev as 设备
Note over Platform,Dev: 1. 控制指令下发
Platform->>MQTT: PUBLISH devices/{sn}/down/control {控制指令}
MQTT->>Dev: 转发控制指令
Note over Dev: 2. 立即确认
Dev->>MQTT: PUBLISH up/control/ack {status:confirm}
MQTT->>Platform: 转发确认回执
Note over Dev: 3. 完成操作
Dev->>MQTT: PUBLISH up/control/ack {status:success}
MQTT->>Platform: 转发完成回执
alt 需要重启的操作
Note over Dev: 4. 设备重启
Dev->>Dev: 执行重启
Note over Dev: 5. 重启后上报
Dev->>MQTT: PUBLISH up/state {设备状态}
MQTT->>Platform: 同步设备状态
end
📖 协议概述
Section titled “📖 协议概述”设备 <--HTTPS--> Bootstrap API (获取连接参数)设备 <--MQTT--> EMQX Broker <--Core MQTT Bridge--> Kafka <--> 平台应用- 双向通信: 设备可上报状态,平台可下发任务
- QoS 保证: 上行 QoS1,下行 QoS2(可协商降级到 QoS1)
- 消息幂等: 基于
msgId和jobId实现幂等去重 - TLS 加密: 生产环境强制使用 TLS 1.2+
📚 术语定义
Section titled “📚 术语定义”| 术语 | 说明 | 示例/备注 |
|---|---|---|
| Device(设备) | 物联网打印机或相关硬件设备 | - |
| SN(序列号) | 设备的全局唯一标识符 | SN-XYZ-001,禁止包含 | . 等特殊字符 |
| Secret(密钥) | 平台为设备分配的密钥,用于签名验证 | 32-64字节随机字符串,设备端需安全存储 |
| msgId(消息ID) | 消息的唯一标识符,32字符长度 | 建议使用 UUID v4 去掉连字符,如 a1b2c3d4e5f6789012345678901234ab |
| ProtocolVersion | 协议版本号 | 当前为 0.1 |
| 术语 | 说明 |
|---|---|
| MQTT | 消息队列遥测传输协议,轻量级发布/订阅模式 |
| Topic(主题) | MQTT 消息的分类路径,格式如 devices/{sn}/up/state |
| Upstream(上行) | 设备 → 平台的消息流 |
| Downstream(下行) | 平台 → 设备的消息流 |
| QoS | 消息传输质量等级:QoS0(最多一次)、QoS1(至少一次)、QoS2(恰好一次) |
| ACL | 访问控制列表,限制设备只能访问自己的主题 |
| 术语 | 说明 |
|---|---|
| jobId | 打印任务的唯一标识符 |
| deviceUpgradeId | 设备升级任务的唯一标识符 |
| firmwareType | 固件类型:cloud(云端)、printer(打印机)、bluetooth(蓝牙) |
| seq | 资源序号,从 1 开始递增 |
| 字段 | 单位 | 说明 |
|---|---|---|
| ts | 毫秒 | Unix 时间戳(13位毫秒级),设备上报的当前时间,如 1724040060000 |
| dateTime | 毫秒 | Unix 时间戳(13位毫秒级),服务器时间,如 1724846400000 |
提示: Bootstrap API 仅校验时间戳长度为13位,不校验时间窗口,设备可在引导成功后根据响应中的
serverTime进行时间校准。
⚠️ 错误码定义
Section titled “⚠️ 错误码定义”HTTP 状态码
Section titled “HTTP 状态码”| 状态码 | 说明 | 处理建议 |
|---|---|---|
| 200 | 成功 | - |
| 401 | 签名无效或已过期 | 检查签名算法和时间戳 |
| 404 | 设备未注册 | 联系平台管理员注册设备 |
| 423 | 设备已被禁用 | 设备被停用,无法继续使用 |
| 429 | 请求频率超限 | 实施指数退避策略 |
| 500 | 服务器内部错误 | 重试或联系技术支持 |
业务错误码(errCode)
Section titled “业务错误码(errCode)”约定:凡字段名为
errCode(数组)时,空数组[]表示无错误/正常;当存在错误时,数组内元素为一个或多个错误码,取值参考本章节错误码定义。
| 错误码 | 说明 |
|---|---|
| 0 | 操作成功 |
认证相关 (1000-1099)
Section titled “认证相关 (1000-1099)”| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 1001 | 签名非法 | 检查签名算法,确认 Secret 正确 |
| 1002 | 令牌过期或无效 | 重新调用 Bootstrap API 获取新令牌 |
| 1003 | 设备未授权 | 检查设备是否已注册并启用 |
网络通讯 (2000-2099)
Section titled “网络通讯 (2000-2099)”| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 2001 | 主题访问被拒绝(ACL) | 检查主题路径是否正确 |
| 2002 | 消息格式错误 | 检查 JSON 格式和必填字段 |
| 2003 | 消息体过大 | 单条消息不得超过 256KB |
打印相关 (3000-3099)
Section titled “打印相关 (3000-3099)”| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 3001 | 打印失败(设备硬件故障) | 检查打印机状态,上报详细错误信息 |
| 3002 | 打印超时 | 检查网络和设备性能 |
| 3003 | 打印内容下载失败 | 检查网络连接,支持断点续传 |
| 3004 | 打印内容校验失败 | 重新下载文件,检查 hash 算法 |
| 3005 | 存储空间不足 | 重启机器或者联系工程师排查情况处理 |
固件升级 (4000-4099)
Section titled “固件升级 (4000-4099)”| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 4001 | 升级包校验失败 | 检查 SHA256 哈希值 |
| 4002 | 升级失败/回滚 | 保留旧固件,支持回滚机制 |
| 4003 | 升级包下载失败 | 检查网络,支持断点续传 |
| 4004 | 固件版本不兼容 | 检查设备型号和固件版本匹配 |
| 4005 | 存储空间不足 | 清理临时文件或提示用户 |
| 4006 | 升级超时 | 设备升级超时且达到最大重试次数,平台标记失败 |
设备控制 (5000-5099)
Section titled “设备控制 (5000-5099)”| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 5001 | 不支持的控制指令 | 检查指令是否在支持列表中 |
| 5002 | 控制指令参数错误 | 检查参数格式和取值范围 |
| 5003 | 控制指令执行失败 | 检查设备状态和硬件功能 |
| 5004 | 控制指令超时 | 检查设备响应能力 |
设备状态 (6000-6099)
Section titled “设备状态 (6000-6099)”| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 6001 | 打印机缺纸 | 提示用户添加纸张 |
| 6002 | 打印机卡纸 | 提示用户清除卡纸 |
| 6003 | 打印机过热 | 暂停打印,等待降温 |
| 6004 | 电池电量低 | 提示用户充电 |
| 6005 | 存储空间不足 | 清理缓存文件 |
| 6006 | 盖子未关 | 重新把盖子关上 |
| 6007 | 盖子未关且卡纸 | 重新打开盖子整理下纸张再关好盖子 |
| 6008 | 盖子未关且过热 | 通风散热后再重新关盖打印 |
| 6009 | 卡纸且过热 | 通风散热后打开盖子调整纸张后再重新关盖打印 |
| 6010 | 盖子未关且卡纸且过热 | 通风散热后打开盖子调整纸张后再重新关盖打印 |
| 6011 | 硬件错误 | 联系工程师处理 |
| 6012 | 设备忙 | 等待设备正常后再执行后续打印操作 |
| 6013 | 纸张类型错误 | 检查纸张类型是否与任务要求匹配 |
🔌 接入流程
Section titled “🔌 接入流程”Step 1: Bootstrap API - 获取连接参数
Section titled “Step 1: Bootstrap API - 获取连接参数”设备上电后,首先通过 HTTPS 调用 Bootstrap API 获取 MQTT 连接参数和主题配置。
接口地址
POST https://byteone-core-stg.iprtapp.com/api/v1/bootstrap请求头
请求体结构
{ "sn": "SN-XYZ-001", // 必填:设备序列号 "protocolVersion": "0.1", // 必填:协议版本 "ts": 1724040000000, // 必填:当前时间戳(13位毫秒),误差不超过±5分钟 "sign": "abc123def456..." // 必填:签名,见下方算法说明}请求字段说明
Section titled “请求字段说明”| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
sn | string | ✅ | 设备序列号,全局唯一标识符 |
protocolVersion | string | ✅ | 协议版本号,当前为 0.1 |
ts | int64 | ✅ | 当前时间戳(13位毫秒),仅需确保为13位长度即可 |
sign | string | ✅ | HMAC-SHA256签名,计算方式见下方算法说明 |
签名算法详解
Section titled “签名算法详解”算法: HMAC-SHA256
步骤:
- 拼接明文字符串:
plaintext = sn + ts- 示例:
"SN-XYZ-001" + "1724040000000"→"SN-XYZ-0011724040000000"
- 示例:
- 使用 Secret 作为密钥,对明文进行 HMAC-SHA256 计算,例如明文
SN-XYZ-0011724040000000使用密钥password进行HMAC-SHA256加密后的结果为5776e385eb1e6a652e71dd3cb37b98a7f459d186ec630a3da5274fa97f429743 - 将计算结果转换为十六进制小写字符串
成功响应(HTTP 200)
{ "code": 0, "message": "OK", "data": { "device": { "sn": "SN-TEST-001", "model": "A40w-4G" }, "mqtt": { "host": "mqtt.byteone.com", "port": 8883, // TLS端口,测试环境可能为1883 "clientId": "SN-TEST-001|0.1", // 格式:{sn}|{protocolVersion} "username": "SN-TEST-001", "password": "eyJhbGciOiJIUzI1NiIs...", // JWT短期令牌 "qosUp": 1, // 上行消息QoS等级 "qosDown": 2, // 下行消息QoS等级 "tokenTtlSec": 3600, // 令牌有效期(秒) "keepalive": 60, // MQTT心跳时间(秒),设备应按此间隔发送心跳包 "cleanSession": false, // 是否清洁会话,false表示保持会话以恢复离线消息 "tls": { "enabled": true, // 生产环境必须为true "caCertPem": "-----BEGIN CERTIFICATE-----\n..." // TLS根证书 } }, "topics": { "upState": "devices/SN-TEST-001/up/state", "upPrintAck": "devices/SN-TEST-001/up/print/ack", "upUpgradeAck": "devices/SN-TEST-001/up/upgrade/ack", "upControlAck": "devices/SN-TEST-001/up/control/ack", "upEvent": "devices/SN-TEST-001/up/event", "upPull": "devices/SN-TEST-001/up/pull", "downPrint": "devices/SN-TEST-001/down/print", "downUpgrade": "devices/SN-TEST-001/down/upgrade", "downControl": "devices/SN-TEST-001/down/control", "downPullAck": "devices/SN-TEST-001/down/pull/ack" }, "stateReportInterval": 1800, // 设备状态上报周期(秒),默认1800秒(30分钟) "serverTime": 1759991021 // 服务器当前时间戳,用于时间校准 }}失败响应示例
{ "code": 1001, "message": "签名验证失败", "data": null}响应字段说明
Section titled “响应字段说明”| 字段路径 | 类型 | 说明 |
|---|---|---|
code | int | 业务错误码,0表示成功 |
message | string | 响应消息描述 |
data.device.sn | string | 设备序列号 |
data.device.model | string | 设备型号 |
data.mqtt.host | string | MQTT服务器地址 |
data.mqtt.port | int | MQTT服务器端口,8883(TLS)或1883(非TLS) |
data.mqtt.clientId | string | MQTT客户端ID,格式固定为 {sn}|{protocolVersion} |
data.mqtt.username | string | MQTT连接用户名,通常为设备SN |
data.mqtt.password | string | JWT短期令牌,有效期由 tokenTtlSec 指定 |
data.mqtt.qosUp | int | 上行消息QoS等级(通常为1) |
data.mqtt.qosDown | int | 下行消息QoS等级(通常为2) |
data.mqtt.tokenTtlSec | int | JWT令牌有效期(秒),默认为3600秒(1小时) |
data.mqtt.keepalive | int | MQTT心跳时间(秒),设备应按此间隔发送心跳包保持连接,默认60秒 |
data.mqtt.cleanSession | bool | 是否清洁会话,true表示不保留离线消息,false表示保持会话以便恢复离线消息 |
data.mqtt.tls.enabled | bool | 是否启用TLS,生产环境必须为true |
data.mqtt.tls.caCertPem | string | TLS根证书(PEM格式),用于验证服务器身份,目前使用验证过的公钥,所以当前字段默认为空,由设备端自行通过证书库认证 |
data.topics.upState | string | 设备状态上行主题 |
data.topics.upPrintAck | string | 打印任务回执上行主题 |
data.topics.upUpgradeAck | string | 固件升级回执上行主题 |
data.topics.upControlAck | string | 控制指令回执上行主题 |
data.topics.upEvent | string | 设备事件上行主题,用于设备上报事件通知 |
data.topics.upPull | string | 任务拉取上行主题,设备主动拉取打印或固件升级任务 |
data.topics.downPrint | string | 打印任务下行主题 |
data.topics.downUpgrade | string | 设备固件升级下行主题 |
data.topics.downControl | string | 控制指令下行主题,接收平台控制命令 |
data.topics.downPullAck | string | Pull响应下行主题,用于回复无可用任务的情况 |
data.stateReportInterval | int | 设备状态上报周期(秒),设备应按此间隔定期上报设备状态,默认1800秒(30分钟) |
data.serverTime | int64 | 服务器当前时间戳(秒级,13位),设备可用于时间校准 |
- 参数有效期: Bootstrap 返回的 MQTT 连接参数长期有效,除非平台更新配置
- 令牌续期: JWT 令牌有效期为
tokenTtlSec秒(通常为3600秒),令牌接近过期(剩余5分钟)时,应重新调用 Bootstrap API 获取新令牌 - 时间同步: 如果设备时间与
serverTime差异过大,应使用serverTime进行校准 - TLS证书: 生产环境必须启用 TLS
Step 2: 连接 MQTT Broker
Section titled “Step 2: 连接 MQTT Broker”使用 Bootstrap API 返回的参数连接 MQTT Broker。
TLS 配置(生产环境必选)
Section titled “TLS 配置(生产环境必选)”Step 3: 订阅下行主题
Section titled “Step 3: 订阅下行主题”连接成功后,立即订阅平台下发命令的主题。需订阅以下主题:
devices/{sn}/down/print- 打印任务下发devices/{sn}/down/upgrade- 固件升级任务下发devices/{sn}/down/control- 控制指令下发devices/{sn}/down/pull/ack- Pull 响应(无可用任务时的回复)
Step 4: 上报设备状态
Section titled “Step 4: 上报设备状态”订阅成功后,立即上报一次设备状态。
Step 5: 主动拉取任务
Section titled “Step 5: 主动拉取任务”上报状态后,设备应主动通过 Pull 主题拉取可用的打印任务或固件升级任务:
// 发送到 devices/{sn}/up/pull{ "msgId": "xxx", "jobId": "", // 为空表示请求新任务 "seq": 0, "firmware": [ {"firmwareType": "cloud", "firmwareVersion": "1.0.0", "nn": "NN-001"}, {"firmwareType": "printer", "firmwareVersion": "2.0.0"} ], "ts": 1724044088000}云端根据设备上报的固件版本判断是否需要下发固件升级任务,优先下发固件升级。
注意: 设备端需自行判断本地是否存在阻塞性错误(如缺纸、卡纸),存在错误时不应发送 Pull 请求。云端在收到 Pull 时不再判断设备错误状态。
Step 6: 处理平台下发的任务
Section titled “Step 6: 处理平台下发的任务”监听消息事件,处理打印和升级任务。
📡 消息协议
Section titled “📡 消息协议”主题命名规范
Section titled “主题命名规范”| 方向 | 主题模式 | 说明 |
|---|---|---|
| 上行 | devices/{sn}/up/{message_type} | 设备上报消息到平台 |
| 下行 | devices/{sn}/down/{command_type} | 平台下发命令到设备 |
命名约束:
- 主题路径仅允许字符:
[a-z0-9-_/] - SN 建议使用
A-Z0-9-字符组合 - 主题路径最大长度:256 字符
通用消息格式
Section titled “通用消息格式”所有 MQTT 消息载荷均为 JSON UTF-8 编码格式。
通用字段:
{ "msgId": "32字符唯一ID", // 必填:消息唯一标识符,用于去重 "ts": 1724040060000, // 必填:消息时间戳(13位毫秒) // ... 其他业务字段}msgId 生成规范:
- 长度:32 字符
- 格式:建议使用 UUID v4 去掉连字符(例:
a1b2c3d4e5f6789012345678901234ab) - 唯一性:全局唯一,用于消息去重和链路追踪
- 生成示例:
上行消息:设备状态上报
Section titled “上行消息:设备状态上报”主题: devices/{sn}/up/state
QoS: 1
用途: 上报设备实时状态,包括网络、电量、固件版本等信息
{ "msgId": "a1b2c3d4e5f6789012345678901234ab", "ts": 1724040060000, "rssi": -58, // WiFi 信号强度(dBm) "battery": 90, // 电池电量百分比(0~100) "paper": 1, // 纸张状态:0=无纸,1=有纸 "paperType": "roll", // 纸张类型:fold/roll/gap/hole/tattoo "errCode": [], // 错误码数组:空数组表示正常,可同时上报多个错误码 "temperature": 35.2, // 可选:设备温度(摄氏度) "humidity": 45, // 可选:设备湿度(0~100) "ipAddress": "192.168.1.25", // 可选:设备IP地址 "bluetoothMac": "00:1A:7D:DA:71:13", // 可选:蓝牙MAC地址 "imei": "865123456789012", // 可选:4G模块IMEI号(15位数字) "iccid": "89860123456789012345", // 可选:SIM卡ICCID号(18-22位数字) "signalStrength": -75, // 可选:4G信号强度(dBm) "networkType": "4G", // 可选:网络类型(2G/3G/4G/5G) "printCount": 12345, // 可选:累计打印张数 "avgLatency": 150, // 可选:平均延时(毫秒) "packetLossRate": 2, // 可选:丢包率(0~100,如2表示2%) "charging": false, // 可选:是否正在充电 "sleepTime": 10, // 可选:休眠时间(分钟) "firmware": [ { "firmwareType": "cloud", // 固件类型 "firmwareVersion": "1.2.3", // 固件版本号 "nn": "NN-001" // 模组NN号 }, { "firmwareType": "printer", "firmwareVersion": "3.4.5", "nn": "NN-002" // 打印机NN号 }, { "firmwareType": "bluetooth", "firmwareVersion": "0.9.8" } ]}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
ts | int64 | ✅ | 消息时间戳(13位毫秒) |
rssi | int | ❌ | Wi-Fi RSSI,单位 dBm,通常为负数,数值越接近 0 信号越强 |
battery | float | ❌ | 电量百分比,范围0~1 |
paper | int | ✅ | 纸张状态,0=缺纸,1=正常 |
paperType | string | ❌ | 纸张类型:fold(折叠纸)/roll(卷纸)/gap(标签纸)/hole(孔型纸)/tattoo(纹身纸) |
errCode | int[] | ✅ | 设备错误码数组,[] 表示正常,其他见错误码表,可同时上报多个错误码 |
temperature | float | ❌ | 设备温度(摄氏度) |
humidity | float | ❌ | 设备湿度(0~1) |
ipAddress | string | ❌ | 设备当前IP地址 |
bluetoothMac | string | ❌ | 蓝牙MAC地址 |
imei | string | ❌ | 4G模块IMEI号(15位数字),用于识别4G模块 |
iccid | string | ❌ | SIM卡ICCID号(18-22位数字),用于识别SIM卡 |
signalStrength | int | ❌ | 4G LTE RSRP,单位 dBm,通常为负数;不要上报 RSRP 原始索引值 |
networkType | string | ❌ | 网络类型:2G/3G/4G/5G |
printCount | int | ❌ | 累计打印张数 |
avgLatency | int | ❌ | 平均延时(毫秒),用于监控设备网络质量 |
packetLossRate | int | ❌ | 丢包率(0~100),如2表示2%丢包率 |
charging | bool | ❌ | 是否正在充电,true 表示充电中 |
sleepTime | int | ❌ | 休眠时间(分钟) |
firmware | array | ✅ | 固件版本列表 |
firmware[].firmwareType | string | ✅ | 固件类型:cloud/printer/bluetooth |
firmware[].firmwareVersion | string | ✅ | 固件版本号 |
firmware[].nn | string | ❌ | 设备当前 NN 号,建议在 firmwareType=cloud 的固件项中上报;非空时云端将更新设备记录中的 NN 号 |
信号强度说明
Section titled “信号强度说明”rssi 与 signalStrength 都使用 dBm。Wi-Fi 信号上报 ESP32/ESP-IDF 的 RSSI dBm 值,参考 Espressif ESP-IDF Wi-Fi API 中 RSSI threshold 的 rssi 参数说明:阈值单位为 dBm,常见范围约为 -100 到 10,极强信号可能出现轻微正值。
signalStrength 上报 4G LTE RSRP dBm 值。若模组只提供 LTE RSRP 报告值,应先按 3GPP TS 36.133 v14.5.0 Table 9.1.4-1 的 RSRP measurement report mapping 转成 dBm 后再上报:
| RSRP 报告值 | RSRP dBm 区间 |
|---|---|
value <= -17 | rsrp < -156 dBm |
-16 <= value <= -1 | (value - 140) dBm <= rsrp < (value - 139) dBm |
value = 0 | rsrp < -140 dBm |
1 <= value <= 96 | (value - 141) dBm <= rsrp < (value - 140) dBm |
97 <= value <= 126 | -44 dBm <= rsrp |
value = 127 | 无效值,不上报 signalStrength 或按未知处理 |
4 格信号展示可按以下公式从 dBm 转换。缺失或无效值应显示为未知,不参与公式计算。
wifiBars = min(4, max(0, floor((rssiDbm + 95) / 10)))lteBars = min(4, max(0, floor((rsrpDbm + 125) / 10)))对应阈值为:Wi-Fi < -85 为 0 格,[-85,-76] 为 1 格,[-75,-66] 为 2 格,[-65,-56] 为 3 格,>= -55 为 4 格;4G RSRP < -115 为 0 格,[-115,-106] 为 1 格,[-105,-96] 为 2 格,[-95,-86] 为 3 格,>= -85 为 4 格。
| 场景 | 上报时机 |
|---|---|
| 开机上报 | 设备启动完成,各状态正常,MQTT 连接成功后立即上报 |
| 周期上报 | 可选,建议每 30 分钟上报一次 |
| 变化上报 | 重要状态变更时立即上报(缺纸、电量低、过热等) |
| 频率限制 | 每分钟不超过 100 条(突发异常除外) |
上行消息:打印任务回执
Section titled “上行消息:打印任务回执”主题: devices/{sn}/up/print/ack
QoS: 1
用途: 向平台回复打印任务的执行状态和进度
{ "msgId": "b2c3d4e5f67890a1b2c3d4e5f67890a1", "jobId": "92ef08872ff906876071478274eec57b", // 打印任务ID "ackOriginalMsgId": "a1b2c3d4e5f6789012345678901234ab", // 原始下发消息的msgId "status": "confirm", // 任务状态 "seqs": [1, 2, 3, 4, 5], // 确认的资源序号数组 "errCode": [], // 错误码数组,空数组表示成功 "msg": "ok", // 错误描述信息 "ts": 1724040088000}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
jobId | string | ✅ | 打印任务唯一标识,与下发任务的jobId对应 |
ackOriginalMsgId | string | ✅ | 原始下发消息的msgId,用于消息关联 |
status | string | ✅ | 任务状态:confirm/printing/success/failed |
seqs | int[] | ✅ | 资源序号数组,支持批量确认多个资源,每个元素取值 1~totalFileNumber,详情见seqs字段说明 |
errCode | int[] | ✅ | 错误码数组,[] 表示成功,其他见错误码表,可同时返回多个错误码 |
msg | string | ✅ | 错误描述信息或状态说明 |
ts | int64 | ✅ | 消息时间戳(13位毫秒) |
状态字段详解
Section titled “状态字段详解”| status 值 | 含义 | 是否必传 | 说明 |
|---|---|---|---|
| confirm | 收到任务确认 | ✅ 必传 | 设备收到任务后立即回复,10秒内必须发送,seqs 包含已确认收到的资源序号 |
| printing | 打印中 | ✅ 建议 | 打印进度上报,携带 seqs 字段 |
| success | 打印成功 | ✅ 必传 | 所有资源打印完成,seqs 包含所有成功打印的资源序号 |
| failed | 打印失败 | ✅ 必传 | 打印过程中出现错误,seqs 包含失败的资源序号 |
注意: 拉取新的打印任务或固件升级任务请使用独立的 Pull 主题(
devices/{sn}/up/pull)。
seqs 字段说明
Section titled “seqs 字段说明”- 用途:用于确认每个打印任务单和文件的实际打印情况,也可用与设备掉电后能云端能确定具体打到哪个文件了
- 类型: 整数数组,支持批量确认多个资源
- 取值范围: 每个元素范围 1 ~ totalFileNumber(打印任务文件总数)
- 语义:
confirm状态:已成功接收并确认的资源序号列表,如[1, 2, 3, 4, 5]表示确认收到seq 1-5的资源printing状态:当前正在打印的资源序号,通常只包含当前值,如[3]success状态:当前打印完成的资源序号,通常只只包含当前值,如[3]failed状态:当前打印失败的资源序号,通常只只包含当前值,如[3]
示例:
// 确认收到seq 1-5的资源{"status": "confirm", "seqs": [1, 2, 3, 4, 5], ...}
// 正在打印seq 3的资源{"status": "printing", "seqs": [3], ...}
// 打印完成{"status": "success", "seqs": [3], ...}掉电恢复流程
Section titled “掉电恢复流程”- 设备打印中突然断电
- 设备重启: 设备重启后完成自检、MQTT 连接,上报设备状态
- 主动拉取任务: 设备通过 Pull 主题(
devices/{sn}/up/pull)携带本地缓存的任务ID和seq主动拉取任务,如果本地没有缓存则直接传空由云端判断 - 续打任务下发: 平台根据设备上报的进度,下发续打任务
- 恢复打印: 设备继续打印
上行消息:固件升级回执
Section titled “上行消息:固件升级回执”主题: devices/{sn}/up/upgrade/ack
QoS: 1
用途: 向平台回复固件升级任务的执行状态
{ "msgId": "d4e5f6789012a3b4c5d6e7f89012a3b4", "deviceUpgradeId": "92ef08872ff906876071478274eec57b", // 设备升级任务ID "ackOriginalMsgId": "原始下发消息的msgId", "status": "upgrading", // 升级状态 "errCode": [], // 错误码数组,空数组表示成功 "msg": "ok", // 错误描述 "ts": 1724041088000}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
deviceUpgradeId | string | ✅ | 设备升级任务ID,与下发任务的deviceUpgradeId对应 |
ackOriginalMsgId | string | ✅ | 原始下发消息的msgId,用于消息关联 |
status | string | ✅ | 升级状态:confirm/downloading/upgrading/success/failed |
errCode | int[] | ✅ | 错误码数组,[] 表示成功,其他见错误码表,可同时返回多个错误码 |
msg | string | ✅ | 错误描述信息或状态说明 |
ts | int64 | ✅ | 消息时间戳(13位毫秒) |
| status 值 | 含义 | 是否必传 | 说明 |
|---|---|---|---|
| confirm | 收到任务确认 | ✅ 必传 | 设备收到升级任务后立即回复 |
| downloading | 下载中 | ❌ 可选 | 固件下载进度上报 |
| upgrading | 升级中 | ❌ 可选 | 正在执行升级 |
| success | 升级成功 | ✅ 必传 | 升级完成并重启后发送 |
| failed | 升级失败 | ✅ 必传 | 升级过程中出现错误 |
上行消息:设备控制指令回执
Section titled “上行消息:设备控制指令回执”主题: devices/{sn}/up/control/ack
QoS: 1
用途: 向平台回复设备控制指令的执行状态
{ "msgId": "g5h6i7j890k1l2m3n4o5p6q789r0s1t2", "controlId": "a351eff1b614d0c1f64227d0c4b28c99", // 控制指令ID "ackOriginalMsgId": "原始下发消息的msgId", "status": "confirm", // 执行状态 "errCode": [], // 错误码数组,空数组表示成功 "msg": "ok", // 错误描述 "ts": 1724042088000}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
controlId | string | ✅ | 控制指令ID,与下发指令的controlId对应 |
ackOriginalMsgId | string | ✅ | 原始下发消息的msgId,用于消息关联 |
status | string | ✅ | 执行状态:confirm/success/failed |
errCode | int[] | ✅ | 错误码数组,[] 表示成功,其他见错误码表,可同时返回多个错误码 |
msg | string | ✅ | 错误描述信息或状态说明 |
ts | int64 | ✅ | 消息时间戳(13位毫秒) |
| status 值 | 含义 | 是否必传 | 说明 |
|---|---|---|---|
| confirm | 收到指令确认 | ✅ 必传 | 设备收到控制指令后立即回复 |
| success | 执行成功 | ✅ 必传 | 控制操作完成 |
| failed | 执行失败 | ✅ 必传 | 控制操作失败 |
上行消息:设备事件上报
Section titled “上行消息:设备事件上报”主题: devices/{sn}/up/event
QoS: 1
用途: 设备向平台上报事件通知,用于触发云端特定业务逻辑
{ "msgId": "h7i8j9k012l3m4n5o6p7q8r901s2t3u4", "eventType": "stop_print", // 事件类型 "eventParams": { // 事件参数(可选) "reason": "user_action" // 事件原因或其他参数 }, "ts": 1724043088000}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
eventType | string | ✅ | 事件类型,见下方事件类型表 |
eventParams | object | ❌ | 事件参数,JSON对象,不同事件类型有不同参数 |
ts | int64 | ✅ | 消息时间戳(13位毫秒) |
支持的事件类型
Section titled “支持的事件类型”| eventType 值 | 含义 | eventParams 参数 | 云端处理逻辑 |
|---|---|---|---|
| stop_print | 停止打印事件 | reason: 停止原因(可选) | 云端收到后会清除该设备的打印队列中所有待打印任务 |
停止打印事件说明
Section titled “停止打印事件说明”当设备需要停止打印时(如用户按下停止按钮),设备应发送此事件通知云端。
消息示例:
{ "msgId": "i8j9k012l3m4n5o6p7q8r901s2t3u4v5", "eventType": "stop_print", "eventParams": { "reason": "user_button" }, "ts": 1724043188000}云端响应:
- 收到
stop_print事件后,云端会立即清除该设备打印队列中所有可被打印的任务
上行消息:任务拉取请求(Pull)
Section titled “上行消息:任务拉取请求(Pull)”主题: devices/{sn}/up/pull
QoS: 1
用途: 设备主动拉取新的打印任务或固件升级任务
{ "msgId": "k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6", "jobId": "", // 可选:当前打印任务ID,为空表示请求新任务 "seq": 0, // 本地缓存的最大资源序号 "firmware": [ // 当前固件版本列表 { "firmwareType": "cloud", "firmwareVersion": "1.2.3", "nn": "NN-001" }, { "firmwareType": "printer", "firmwareVersion": "3.4.5", "nn": "NN-002" }, { "firmwareType": "bluetooth", "firmwareVersion": "0.9.8" } ], "ts": 1724044088000}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
jobId | string | ❌ | 当前打印任务ID。为空表示请求新任务,不为空表示请求该任务的后续资源 |
seq | int | ✅ | 本地缓存的最大资源序号。为0表示请求新任务,大于0表示请求seq+1及后续资源 |
firmware | array | ✅ | 当前固件版本列表,用于云端判断是否需要推送固件升级 |
firmware[].firmwareType | string | ✅ | 固件类型:cloud/printer/bluetooth |
firmware[].firmwareVersion | string | ✅ | 固件版本号 |
firmware[].nn | string | ✅ | 模组或者打印机的NN 号 |
ts | int64 | ✅ | 消息时间戳(13位毫秒) |
云端响应逻辑
Section titled “云端响应逻辑”-
优先检查固件升级:根据设备上报的固件版本,判断是否有待执行的固件升级任务
- 如有固件升级任务:通过
devices/{sn}/down/upgrade主题下发固件升级任务,不下发打印任务 - 设备完成升级后再次 Pull 获取打印任务
- 如有固件升级任务:通过
-
打印任务处理(无固件升级时):
- 如
jobId为空或seq >= totalFileNumber:获取队列中最早的有效任务单下发 - 如
jobId不为空且seq < totalFileNumber:下发该任务的 seq+1 及后续资源
- 如
-
任务筛选条件:
- 任务在有效期内
- 任务状态不为”取消”或”完成”
- 失败状态的任务如果在设备设置的有效期内,也允许重新下发
-
无可用任务:通过
devices/{sn}/down/pull/ack主题回复无可用任务
- 设备端职责:设备在发送 Pull 请求前应自行判断本地是否存在阻塞性错误(如缺纸、卡纸等)。存在阻塞性错误时不应发送 Pull 请求
- 云端职责:云端在收到 Pull 请求时不再判断设备错误状态,直接按上述逻辑响应。云端仅在以下场景判断设备错误:
- 用户创建新任务时
- 有队列任务需要由云端主动触发下发时
下行消息:Pull 响应(无可用任务)
Section titled “下行消息:Pull 响应(无可用任务)”主题: devices/{sn}/down/pull/ack
QoS: 1
用途: 当 Pull 请求没有可用的打印任务或固件升级任务时,云端回复此消息
{ "msgId": "m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8", "ackOriginalMsgId": "k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6", "code": 0, // 响应码:0表示成功(但无可用任务) "message": "no_task", // 响应消息 "dateTime": 1724044188000 // 服务器时间}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
ackOriginalMsgId | string | ✅ | 原始 Pull 请求的 msgId |
code | int | ✅ | 响应码:0表示成功 |
message | string | ✅ | 响应消息:no_task 表示无可用任务 |
dateTime | int64 | ✅ | 服务器时间(13位毫秒时间戳) |
- 当有可用的打印任务时,云端通过
devices/{sn}/down/print主题下发,不使用此主题 - 当有可用的固件升级任务时,云端通过
devices/{sn}/down/upgrade主题下发,不使用此主题 - 此主题仅用于回复”无可用任务”的情况
下行消息:打印任务下发
Section titled “下行消息:打印任务下发”主题: devices/{sn}/down/print
QoS: 2(可协商降级到 QoS 1)
用途: 平台向设备下发打印任务
{ "msgId": "e5f6789012a3b4c5d6e7f89012a3b4c5", "jobId": "j-202408-0001", // 打印任务唯一ID "totalFileNumber": 100, // 任务文件总数 "resources": [ { "seq": 1, // 资源序号,从1开始 "url": "https://obj.example.com/prints/j-202408-0001-part1.data", "hash": "sha256:ab12cd34ef56...", // SHA256哈希值 "size": 102400, // 文件大小(字节) "compression": "gzip" // 压缩方式:none-不压缩,gzip-gzip压缩 }, { "seq": 2, "url": "https://obj.example.com/prints/j-202408-0001-part2.data", "hash": "sha256:cd34ef56ab78...", "size": 102400, "compression": "none" } ], "dateTime": 1724846400000 // 服务器时间(13位毫秒时间戳)}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID |
jobId | string | ✅ | 打印任务唯一标识,用于幂等去重 |
totalFileNumber | int | ✅ | 任务文件总数 |
resources | array | ✅ | 打印资源列表,单次下发最多100个 |
resources[].seq | int | ✅ | 资源序号,从1开始递增 |
resources[].url | string | ✅ | 文件下载URL |
resources[].hash | string | ✅ | SHA256哈希值,格式:sha256:十六进制字符串 |
resources[].size | int | ✅ | 文件大小(字节) |
resources[].compression | string | ✅ | 压缩方式:none(不压缩)、gzip(gzip压缩)。设备下载后需根据此字段决定是否解压 |
dateTime | int64 | ✅ | 服务器时间(13位毫秒时间戳) |
- 接收任务: 收到消息后立即发送
status=confirm回执(10秒内) - 幂等检查: 根据
jobId判断是否为重复任务,重复任务直接返回上次结果 - 下载资源: 按 seq 顺序下载文件,支持断点续传
- 校验哈希: 下载完成后验证 SHA256 哈希值
- 解压资源: 检查
compression字段,若为gzip则进行 gzip 解压,若为none则跳过解压 - 执行打印:
- 按顺序打印资源
- 开始打印时发送
status=printing进度回执
- 完成回执: 打印完成后发送
status=success或status=failed
下行消息:固件升级任务
Section titled “下行消息:固件升级任务”主题: devices/{sn}/down/upgrade
QoS: 2(可协商降级到 QoS 1)
用途: 平台向设备下发固件升级任务
{ "msgId": "f6789012a3b4c5d6e7f89012a3b4c5d6", "deviceUpgradeId": "u-202408-0001", // 设备升级任务ID "firmwareType": "cloud", // 固件类型 "firmwareVersion": "1.2.4", // 固件版本号 "url": "https://obj.example.com/fw/p5802-1.2.4.bin", "hash": "sha256:cd34ef56ab78...", // SHA256哈希值 "size": 5242880, // 文件大小(字节) "compression": "gzip", // 压缩方式:none-不压缩,gzip-gzip压缩 "dateTime": 1724846400000 // 服务器时间(13位毫秒时间戳)}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID,32字符 |
deviceUpgradeId | string | ✅ | 设备升级任务唯一标识,用于幂等去重 |
firmwareType | string | ✅ | 固件类型:cloud(云端固件)、printer(打印机固件)、bluetooth(蓝牙固件) |
firmwareVersion | string | ✅ | 固件版本号 |
url | string | ✅ | 固件下载URL |
hash | string | ✅ | SHA256哈希值,格式:sha256:十六进制字符串 |
size | int | ✅ | 文件大小(字节) |
compression | string | ✅ | 压缩方式:none(不压缩)、gzip(gzip压缩)。设备下载后需根据此字段决定是否解压 |
dateTime | int64 | ✅ | 服务器时间(13位毫秒时间戳) |
- 接收任务: 收到消息后立即发送
status=confirm回执 - 下载固件:
- 支持断点续传
- 定期发送
status=downloading进度(可选)
- 校验哈希: 下载完成后验证 SHA256 哈希值,失败则立即报错
- 解压固件: 检查
compression字段,若为gzip则进行 gzip 解压,若为none则跳过解压 - 执行升级:
- 发送
status=upgrading状态 - 应用新固件
- 发送
- 重启设备: 升级完成后重启
- 上报结果:
- 重启后发送
status=success回执 - 上报新的固件版本(通过设备状态消息)
- 重启后发送
下行消息:设备控制指令
Section titled “下行消息:设备控制指令”主题: devices/{sn}/down/control
QoS: 2(可协商降级到 QoS 1)
用途: 平台向设备下发控制指令,用于远程控制设备行为
{ "msgId": "h6i7j890k1l2m3n4o5p6q789r0s1t2u3", "controlId": "a351eff1b614d0c1f64227d0c4b28c99", // 控制指令唯一ID "command": "reboot", // 控制指令类型 "params": {}, // 可选:指令参数 预留字段 "dateTime": 1724846400000 // 服务器时间(13位毫秒时间戳)}支持的控制指令
Section titled “支持的控制指令”| 指令 (command) | 说明 | 参数 (params) | 预期行为 |
|---|---|---|---|
| shutdown | 关机 | 无 | 设备执行安全关机,保存状态后关闭电源 |
| reboot | 重启 | 无 | 设备执行安全重启,保存状态后重启系统 |
| beep | 发声/蜂鸣 | 无 | 发出蜂鸣声 |
| reset_wifi | 重置WiFi | 无 | 清除WiFi配置,恢复到待配网状态 |
| reset_bluetooth | 重置蓝牙 | 无 | 清除蓝牙配对记录,恢复到待配对状态 |
| factory_reset | 恢复出厂设置 | 无 | 清除所有配置,恢复出厂状态。keep_network为true时保留网络配置 |
| clear_cache | 清理缓存 | 无 | 清理临时文件和打印缓存 |
| set_params | 设置设备参数 | - | 设置设备运行参数;设备收到后更新本地配置并回执 |
说明:测试页打印不再通过控制指令下发。平台如需打印测试页,应由云端自行拼接测试页内容并按普通打印任务流程下发。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msgId | string | ✅ | 消息唯一ID |
controlId | string | ✅ | 控制指令唯一标识,用于幂等去重 |
command | string | ✅ | 控制指令类型,见上表 |
params | object | ❌ | 指令参数,不同指令参数不同 |
dateTime | int64 | ✅ | 服务器时间(13位毫秒时间戳) |
- 接收指令: 收到消息后立即发送
status=confirm回执 - 幂等检查: 根据
controlId判断是否为重复指令 - 参数验证: 检查
command是否支持,params是否合法 - 完成回执:
- 成功:发送
status=success - 失败:发送
status=failed,携带错误信息
- 成功:发送
- 重启类操作: 重启后上报设备状态
示例 : 重启设备
{ "msgId": "a1b2c3d4e5f6789012345678901234ab", "controlId": "a351eff1b614d0c1f64227d0c4b28c99", "command": "reboot", "dateTime": 1724846400000}设备应针对不同错误场景返回相应错误码:
| 错误场景 | errCode | msg 示例 |
|---|---|---|
| 不支持的指令 | [5001] | “不支持的控制指令: xxx” |
| 参数错误 | [5002] | “参数错误: duration必须为正整数” |
| 设备忙碌 | [6012] | “设备正在打印,无法执行控制操作” |
| 执行失败 | [5003] | “WiFi重置失败: 网络模块错误” |
安全注意事项
Section titled “安全注意事项”- 危险操作确认: factory_reset 等危险操作建议设备端二次确认(如通过指示灯闪烁或按键确认)
- 幂等性: 基于
controlId实现幂等,避免重复执行
🔒 安全机制
Section titled “🔒 安全机制”TLS 要求
Section titled “TLS 要求”| 环境 | TLS | 端口 | 说明 |
|---|---|---|---|
| 生产环境 | ✅ 必须 | 8883 | 强制使用 TLS 1.2+ |
| 测试环境 | ❌ 可选 | 1883 / 8883 | 建议启用 TLS |
TLS 配置:
- 服务器证书验证: 设备必须验证服务器证书的有效性
- 证书链: 使用 Bootstrap API 返回的
caCertPem验证整个证书链 - 证书过期: 设备应定期检查并更新 CA 证书
阶段 1: HTTP Bootstrap 认证
- 算法: HMAC-SHA256
- 输入:
sn + ts - 密钥: 设备 Secret
- 输出: 十六进制签名字符串
- 有效期: ±5 分钟时间窗口
阶段 2: MQTT 连接认证
- 用户名: 设备 SN
- 密码: JWT 短期令牌(来自 Bootstrap 响应)
- 令牌有效期: 通常 3600 秒/1小时(由
tokenTtlSec指定)
令牌续期策略
Section titled “令牌续期策略”访问控制(ACL)
Section titled “访问控制(ACL)”ACL 规则
Section titled “ACL 规则”设备仅允许访问自己的主题,禁止跨设备访问。
| 操作 | 允许的主题模式 |
|---|---|
| 发布(PUBLISH) | devices/{自己的sn}/up/* |
| 订阅(SUBSCRIBE) | devices/{自己的sn}/down/* |
违规示例:
敏感信息保护
Section titled “敏感信息保护”| 信息类型 | 存储要求 | 说明 |
|---|---|---|
| SN(序列号) | 明文存储 | 设备唯一标识,无需加密 |
| Secret(密钥) | 加密存储 | 使用设备硬件密钥加密,禁止明文存储 |
| JWT Token | 内存存储 | 不持久化,重启后重新获取 |
| CA 证书 | 明文存储 | 公开信息,可明文存储 |
虽然 TLS 已提供传输层加密,但关键业务消息建议额外签名:
🔧 错误处理
Section titled “🔧 错误处理”常见连接错误
Section titled “常见连接错误”| 错误 | 原因 | 解决方案 |
|---|---|---|
| Connection refused | 网络不可达 | 检查网络连接,使用指数退避重试 |
| Authentication failed | 令牌无效或过期 | 重新调用 Bootstrap API 获取新令牌 |
| Certificate verify failed | TLS 证书验证失败 | 检查 CA 证书是否正确 |
| Keep alive timeout | 网络不稳定 | 降低 keepalive 间隔 |
❓ FAQ 常见问题
Section titled “❓ FAQ 常见问题”Q1: 设备时间不准确怎么办?
Section titled “Q1: 设备时间不准确怎么办?”A: 设备应该实现以下时间同步机制:
- NTP 同步: 定期与 NTP 服务器同步时间
- Bootstrap 校准: 使用 Bootstrap API 返回的
serverTime校准本地时间 - 误差检测: 如果时间误差超过 5 分钟,拒绝签名验证并提示用户
Q2: 如何处理网络波动导致的消息丢失?
Section titled “Q2: 如何处理网络波动导致的消息丢失?”A: 采用以下策略:
- 使用 QoS 1 或 QoS 2: 保证消息至少送达一次
- 消息缓存: 离线时缓存消息,连接恢复后重发
- 幂等设计: 基于
jobId和msgId实现幂等去重 - 超时重试: 设置合理的超时时间和重试机制
Q3: 打印任务太大,如何优化下载速度?
Section titled “Q3: 打印任务太大,如何优化下载速度?”A: 建议采用以下优化措施:
- 并发下载: 同时下载多个
resources资源(限制并发数为 3-5) - 断点续传: 使用 HTTP Range 请求支持断点续传
- 预加载: 打印前几个资源时,提前拉取后续资源
- 压缩传输: 资源文件支持 gzip 压缩,设备根据
resources[].compression字段判断是否需要解压。当compression为gzip时,下载完成并校验哈希后需进行 gzip 解压
Q4: 固件升级失败后如何回滚?
Section titled “Q4: 固件升级失败后如何回滚?”A: 设备应该实现以下机制:
- 双分区设计: 保留旧固件分区,升级失败时自动切换
- 升级标记: 记录升级状态,重启后检查是否成功
- 回滚逻辑: 升级失败自动回滚到旧版本
- 上报失败: 通过
up/upgrade/ack上报失败原因
Q5: 如何判断是否需要主动拉取任务?
Section titled “Q5: 如何判断是否需要主动拉取任务?”可以采用以下策略:
- 设备启动后拉取: 设备上电、MQTT连接成功、上报状态后,通过 Pull 主题主动拉取任务
- 拉取同一任务的后续资源: 当
seq < totalFileNumber时,通过 Pull 主题携带jobId和seq拉取后续资源 - 拉取下一单打印任务: 当前任务完成后(发送
status=success),通过 Pull 主题携带空jobId请求下一单任务 - 提前拉取策略: 根据当前打印进度和文件大小,提前拉取下一批资源,避免等待
- 设备端错误判断: 设备在发送 Pull 请求前应自行判断本地是否存在阻塞性错误(如缺纸、卡纸),存在阻塞性错误时不应发送 Pull
重要: 云端在收到 Pull 请求时不再判断设备错误状态,设备端需自行负责错误状态的判断。
Q6: 设备 SN 和 Secret 如何获取?
Section titled “Q6: 设备 SN 和 Secret 如何获取?”A: 设备侧必须具备 SN 和 Secret(用于 Bootstrap 签名与 MQTT 连接认证),通常来源如下:
- 设备注册: 平台侧为设备创建设备记录,生成 SN 与 Secret
- 生产烧录: 在生产环节将 SN 与 Secret 烧录到设备(Secret 必须加密存储)
- App 侧绑定(不同型号流程不同):
- WiFi 版本:App 通过连接设备本体蓝牙,与设备握手后可获取 SN/设备秘钥等信息,再完成配网与绑定
- 4G 版本:App 通过扫描/输入 SN 发起验证码请求;设备在线时打印验证码,App 输入/识别验证码后完成绑定(App 不直接持有 Secret)
注意:以上为“设备入网/绑定”的业务流程说明;协议层仍以设备本地持有 SN/Secret 为前提完成引导与认证。
Q7: 如何调试 MQTT 连接问题?
Section titled “Q7: 如何调试 MQTT 连接问题?”A: 建议使用以下工具和方法:
-
MQTT 客户端工具:
- MQTTX: 图形化 MQTT 客户端
- mosquitto_sub/pub: 命令行工具
-
抓包分析:
-
日志调试:
- 启用 MQTT 库的详细日志
- 记录连接参数、认证信息(脱敏)
-
测试环境:
- 先在测试环境验证(使用 1883 明文端口)
- 确认无误后切换到生产环境(8883 TLS 端口)
Core 内置 MQTT Bridge
Section titled “Core 内置 MQTT Bridge”平台由 Core 内置 MQTT Bridge 实现 MQTT 和 Kafka 之间的双向消息转发:
设备 <--MQTT--> EMQX <--Core MQTT Bridge--> Kafka <--> 平台应用设备到平台 (EMQX → Kafka):
devices/{sn}/up/state→ Kafka Topic:iot-messages(type: device_state)devices/{sn}/up/print/ack→ Kafka Topic:iot-messages(type: print_ack)devices/{sn}/up/upgrade/ack→ Kafka Topic:iot-messages(type: upgrade_ack)devices/{sn}/up/control/ack→ Kafka Topic:iot-messages(type: control_ack)devices/{sn}/up/event→ Kafka Topic:iot-messages(type: device_event)devices/{sn}/up/pull→ Kafka Topic:iot-messages(type: pull_request)
平台到设备 (Kafka → EMQX):
- Kafka Topic:
device-commands(type: print_command) →devices/{sn}/down/print - Kafka Topic:
upgrade-commands(type: upgrade_command) →devices/{sn}/down/upgrade - Kafka Topic:
control-commands(type: control_command) →devices/{sn}/down/control - Kafka Topic:
pull-ack-commands(type: pull_ack) →devices/{sn}/down/pull/ack
| 项目 | 限制 | 说明 |
|---|---|---|
| 单条消息大小 | 256 KB | 打印内容应通过 URL 下载,不直接放在消息体 |
| resources 数组大小 | 100 个元素 | 单次下发最多 100 个打印资源 |
| 状态上报频率 | 10 条/分钟 | 突发异常场景除外 |
| 主题路径长度 | 256 字符 | 包含完整路径 |
| msgId 长度 | 32 字符 | UUID v4 去掉连字符 |
| jobId 长度 | 32 字符 | UUID v4 去掉连字符 |
| Bootstrap 连接参数有效期 | 长期有效 | 仅 JWT Token 需定期续期 |
| JWT Token 有效期 | 1 小时 | 由 tokenTtlSec 指定 |
协议版本历史
Section titled “协议版本历史”| 版本 | 日期 | 变更说明 |
|---|---|---|
| v0.1 | 2025-11-20 | 初始版本,定义基础通信协议 |
文档维护: 爱印科技-应用技术部-方江琛
最后更新: 2026-03-12