ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
264 lines
13 KiB
Markdown
264 lines
13 KiB
Markdown
# VoiceLink:MQTT、在线状态与固件升级
|
||
|
||
平台地址:`https://82.157.201.114`。EchoEar 固件已接入下面的 MQTT 心跳、HTTPS 升级清单及结果上报协议;是否能够远程升级,以设备实际上报的 `ota_supported`、在线状态及真实升级回执为准。
|
||
|
||
部署时 `FIRMWARE_DOWNLOAD_BASE_URL` 必须配置为设备能够访问的平台 HTTPS 根地址,例如 `https://82.157.201.114`。它只用于生成 `/f/{token}` 短下载地址,不能填写 MinIO 内网地址。
|
||
|
||
HTTP 成功统一返回 `{"data": ...}` 外层结构,下面展示其中的 data。设备 HTTP 使用现有 DEVREQ1 请求头;小程序沿用登录 token。无需新增出厂烧录密钥。
|
||
|
||
## 8. MQTT 连接与设备心跳
|
||
|
||
### 设备取得连接参数
|
||
|
||
`POST https://82.157.201.114/api/v1/device/mqtt-config`
|
||
|
||
输入:
|
||
|
||
```json
|
||
{}
|
||
```
|
||
|
||
输出示例:
|
||
|
||
```json
|
||
{
|
||
"broker_url": "mqtt://82.157.201.114:1883",
|
||
"client_id": "KP01-020000260914",
|
||
"username": "KP01-020000260914",
|
||
"password": "<平台返回值,原样使用>",
|
||
"heartbeat_interval_seconds": 30,
|
||
"keepalive_seconds": 60,
|
||
"status_topic": "vl/devices/KP01-020000260914/status",
|
||
"ota_topic": "vl/devices/KP01-020000260914/ota",
|
||
"qos": 1
|
||
}
|
||
```
|
||
|
||
| 字段 | 中文说明与用途 |
|
||
|---|---|
|
||
| broker_url | MQTT 服务器,TCP 1883,明文连接 |
|
||
| client_id / username / password | 连接时原样使用,不是老人的账号密码 |
|
||
| heartbeat_interval_seconds | 心跳间隔,30秒 |
|
||
| keepalive_seconds | MQTT Keep Alive,60秒 |
|
||
| status_topic | 设备发布在线心跳的主题 |
|
||
| ota_topic | 设备订阅升级指令的主题 |
|
||
| qos | 消息等级1,可能重复投递,升级需要按 task_id 去重 |
|
||
|
||
使用 MQTT 3.1.1,Clean Session=true。每次重连后重新订阅 ota_topic,订阅成功后立即上报心跳。消息是完整 UTF-8 JSON,不加蓝牙长度头,Retain=false。当前 MQTT 链路不使用 TLS,连接密码和消息内容在传输中不加密。
|
||
|
||
### 设备每30秒发布心跳
|
||
|
||
发送到返回的 status_topic:
|
||
|
||
```json
|
||
{
|
||
"online": true,
|
||
"firmware_version": "1.0.0",
|
||
"version_code": 0,
|
||
"ota_supported": true
|
||
}
|
||
```
|
||
|
||
| 输入字段 | 中文说明与用途 |
|
||
|---|---|
|
||
| online | true=在线;主动离线时发送false |
|
||
| firmware_version | 当前实际运行固件的显示版本名称 |
|
||
| version_code | 当前实际运行固件的数字顺序号;尚未关联平台固件的出厂版本填0,完成升级后使用指令里的数字 |
|
||
| ota_supported | 最终硬件已实现OTA时填true,否则false,小程序不会允许更新 |
|
||
|
||
设置遗嘱主题为自己的 status_topic,遗嘱内容:
|
||
|
||
```json
|
||
{"online": false}
|
||
```
|
||
|
||
平台以 MQTT 连接和最近心跳判断在线:连接断开即离线,超过90秒未收到心跳也视为离线。仅建立连接、未发心跳不算在线;时间以服务器接收时间为准。
|
||
|
||
设备不用自行计算 MQTT 密码。若需要排查,当前平台生成方法是 `Base64URL无填充(HMAC-SHA256(device_key原始32字节, UTF8("MQTT1\n" + factory_id)))`。与已有设备 HTTP 签名不是同一个计算内容。
|
||
|
||
## 9. 小程序查询设备在线情况
|
||
|
||
`GET https://82.157.201.114/api/v1/devices/{id}/status`
|
||
|
||
输入:路径 id 为平台设备 UUID,不是 factory_id;无请求正文。只能查询当前账号有权查看的设备。
|
||
|
||
该接口与下一节 firmware 接口返回相同结构,小程序只需要请求一次 firmware,便可同时展示在线、版本和升级进度。
|
||
|
||
主要输出:online=true/false、last_seen_at=最近心跳时间(未收到过为null)。查询失败应显示“状态未知”,不能当成已在线。
|
||
|
||
## 10. 当前版本、最新版本与SaaS固件管理
|
||
|
||
`GET https://82.157.201.114/api/v1/devices/{id}/firmware`
|
||
|
||
输入:路径设备 UUID,无请求正文。
|
||
|
||
输出 data 示例(版本、ID和下载链接仅为示例):
|
||
|
||
```json
|
||
{
|
||
"online": true,
|
||
"last_seen_at": "2026-09-14T15:00:00Z",
|
||
"current_version": "1.0.0",
|
||
"current_version_code": 0,
|
||
"current_download_url": "",
|
||
"ota_supported": true,
|
||
"latest": {
|
||
"id": "e6380075-682c-40ca-b327-d2662d04a02c",
|
||
"model": "VL-S3",
|
||
"version": "1.1.0",
|
||
"version_code": 12,
|
||
"release_notes": "改善联网稳定性",
|
||
"size": 3145728,
|
||
"sha256": "abababababababababababababababababababababababababababababababab",
|
||
"status": "READY",
|
||
"created_at": "2026-09-14T14:00:00Z",
|
||
"download_url": "https://82.157.201.114/f/K7z3mF2pQ8xLc4VnB1wRtg"
|
||
},
|
||
"update_available": true,
|
||
"ota_task": null
|
||
}
|
||
```
|
||
|
||
| 输出字段 | 中文说明与用途 |
|
||
|---|---|
|
||
| current_version / current_version_code | 设备已上报的当前版本名称/顺序号,未上报时名称为空、顺序号0 |
|
||
| current_download_url | 当前固件下载地址;平台未收录该版本时为空 |
|
||
| latest | 同租户、同型号中顺序号最大的已发布固件;暂无固件时null |
|
||
| latest.version / latest.version_code | 最新显示名称/数字顺序号 |
|
||
| latest.release_notes | 更新内容 |
|
||
| latest.download_url | 最新固件临时下载地址,有效30分钟,需要时重新查询 |
|
||
| update_available | 最新顺序号大于当前顺序号时true,不能比较版本名称的字符串大小 |
|
||
| ota_task | 最近一次升级任务,没有则null |
|
||
|
||
小程序每10秒刷新,离开页面停止。最新顺序号等于当前时显示“当前是最新版本”;暂无固件时显示“该型号暂无已发布固件”。在线、支持OTA、存在更新且没有进行中任务时,才允许点击更新。两个下载地址可以在小程序中复制。
|
||
|
||
### SaaS上传页面
|
||
|
||
只填写:**设备型号、版本名称、更新内容、固件文件**。顺序号由平台自动生成,数字越大越新,不要求连续;每个型号单独判断最新版本。最大文件32MB。同型号已经发布的版本名称不能重复,已发布文件不能覆盖;上传失败可以用相同版本名称重试。
|
||
|
||
管理接口需运营账号登录,并携带 `X-Tenant-Id`:
|
||
|
||
| 接口 | 输入 | 输出/用途 |
|
||
|---|---|---|
|
||
| GET /api/v1/ops/device-firmware/models | 无正文 | items:库存已有型号,供下拉选择 |
|
||
| GET /api/v1/ops/device-firmware | 无正文 | items:最近200条固件记录,包含版本名称、数字顺序号、状态和更新内容 |
|
||
| POST /api/v1/ops/device-firmware | model、version、release_notes、size、sha256 | 固件记录、自动生成的version_code、upload_url |
|
||
| PUT 返回的upload_url | 固件文件原始字节,Content-Type: application/octet-stream | 2xx表示文件上传成功 |
|
||
| POST /api/v1/ops/device-firmware/{id}/finalize | 无正文 | 校验文件大小和SHA256,保存发布文件,状态变为READY;之前不会提供给设备更新 |
|
||
| GET /api/v1/ops/device-firmware/{id}/download | 无正文 | 固件信息和临时download_url |
|
||
|
||
前端自动计算 size(字节数)、sha256(文件内容的SHA256,64位小写十六进制),用户无需填写。
|
||
|
||
EchoEar 上传的是 ESP-IDF 构建生成的**应用固件** `build/VolcRTCDemo.bin`。不要上传整片 Flash 备份、合并烧录包、bootloader 或分区表。当前设备两个应用槽分别为 4 MiB,应用文件必须小于等于槽大小;平台通用上传上限 32 MB 不代表 EchoEar 可以安装 32 MB 文件。版本名称应与本次构建的 `main/firmware_version.h` 中 `DEVICE_FIRMWARE_VERSION` 保持一致。
|
||
|
||
发布后,在绑定设备的家属端进入设备详情 →「固件与远程控制」→「更新固件」→ 确认。发布不会自动升级所有同型号设备;每次更新都对当前选择的单台设备发起。保持设备通电联网,结束通话后操作,等到状态显示「升级完成」且当前版本变为目标版本再确认完成。新 Wi-Fi 或手机热点需要先完成重新配网,离线设备无法接收升级指令。
|
||
|
||
创建固件输入示例:
|
||
|
||
```json
|
||
{"model":"VL-S3","version":"1.1.0","release_notes":"改善联网稳定性","size":3145728,"sha256":"abababababababababababababababababababababababababababababababab"}
|
||
```
|
||
|
||
## 11. 用户点击更新,平台通过MQTT下发
|
||
|
||
### 小程序请求
|
||
|
||
`POST https://82.157.201.114/api/v1/devices/{id}/ota`
|
||
|
||
输入:
|
||
|
||
```json
|
||
{"version_code":12}
|
||
```
|
||
|
||
version_code 使用最新查询结果,小程序不传下载地址。同一 `X-Request-ID` 重试返回同一任务,不重复下发;不同请求也不能同时创建两个进行中升级。
|
||
|
||
成功为HTTP 200,data示例:
|
||
|
||
```json
|
||
{"task_id":"f58d3018-38a8-4fc4-b490-4d3154fa8464","status":"pending","version":"1.1.0","version_code":12,"message":"","created_at":"2026-09-14T15:00:00Z"}
|
||
```
|
||
|
||
常见拒绝:DEVICE_OFFLINE(离线)、DEVICE_BUSY(通话/指令/升级进行中)、OTA_UNSUPPORTED(设备未实现OTA)、VERSION_CHANGED(最新版本变化或已经无需更新)。此时刷新设备页面。
|
||
|
||
### 设备收到升级指令
|
||
|
||
接收主题:mqtt-config 返回的 ota_topic。
|
||
|
||
```json
|
||
{
|
||
"task_id": "f58d3018-38a8-4fc4-b490-4d3154fa8464",
|
||
"model": "VL-S3",
|
||
"version": "1.1.0",
|
||
"version_code": 12,
|
||
"download_url": "https://82.157.201.114/f/K7z3mF2pQ8xLc4VnB1wRtg",
|
||
"sha256": "abababababababababababababababababababababababababababababababab",
|
||
"size": 3145728,
|
||
"expires_at_ms": 1789398030000
|
||
}
|
||
```
|
||
|
||
| 输入字段 | 中文说明与用途 |
|
||
|---|---|
|
||
| task_id | 本次升级任务,持久化保存,用于去重和重启后的结果上报 |
|
||
| model | 必须与设备型号一致 |
|
||
| version / version_code | 升级后显示的版本名称/数字顺序号,顺序号必须大于当前版本 |
|
||
| download_url | HTTPS短地址下载固件,不通过MQTT传文件;当前完整地址不超过64字符 |
|
||
| sha256 / size | 下载后的文件必须校验,计算方式同上一节 |
|
||
| expires_at_ms | 指令最晚开始接收时间,Unix毫秒,发出后30秒;过期指令不能开始升级 |
|
||
|
||
MQTT 使用明文连接,升级消息仅用于唤醒。设备不得直接采用 MQTT 消息中的下载地址、摘要或目标版本安装固件。收到 `task_id` 后,先使用现有 DEVREQ1 身份签名与 HTTPS 证书校验获取服务端清单:
|
||
|
||
`POST https://82.157.201.114/api/v1/device/ota/manifest`
|
||
|
||
```json
|
||
{"task_id":"f58d3018-38a8-4fc4-b490-4d3154fa8464"}
|
||
```
|
||
|
||
返回 `data` 中含 `task_id`、`model`、`version`、`version_code`、`download_url`、`sha256`、`size`、`expires_at_ms`,字段格式同上面的升级消息。清单只可由该任务所属设备获取,服务端重新签发短下载票据;下载时使用这次 HTTPS 返回的地址。其他设备、已结束任务、已过期任务、未发布或型号不符的固件均不能获取清单。新请求清单会使同一任务之前的下载票据失效。
|
||
|
||
设备步骤:接收唤醒并取得 HTTPS 清单 → 检查型号、顺序号、有效期和当前是否空闲 → 持久化 task_id 和目标版本 → HTTP 上报 accepted → 上报成功后下载并校验 → 写入另一个应用槽并重启 → 新槽启动及健康检查成功后上报 success。下载/校验/安装失败上报 failed;正在通话时拒绝并上报 failed 及原因。同一 task_id 重复到达只回复已有进度,不再执行安装。
|
||
|
||
`/f/{token}` 是平台签发的短期私有下载票据,不会再跳转到较长的对象存储预签名地址。平台下载接口支持 `HEAD` 和 HTTP `Range`;当前 EchoEar 固件下载中断后保留旧槽并报告失败,由用户重新发起升级,不承诺自动断点续传。任务成功、失败、超时或票据过期后立即失效。设备不得记录或上报完整票据,安装前仍必须校验 HTTPS 清单中的大小和 SHA-256。
|
||
|
||
## 12. 设备报告升级结果
|
||
|
||
`POST https://82.157.201.114/api/v1/device/ota/report`
|
||
|
||
设备已接收:
|
||
|
||
```json
|
||
{"task_id":"f58d3018-38a8-4fc4-b490-4d3154fa8464","status":"accepted"}
|
||
```
|
||
|
||
重启并确认运行目标固件后:
|
||
|
||
```json
|
||
{"task_id":"f58d3018-38a8-4fc4-b490-4d3154fa8464","status":"success","firmware_version":"1.1.0","version_code":12}
|
||
```
|
||
|
||
失败:
|
||
|
||
```json
|
||
{"task_id":"f58d3018-38a8-4fc4-b490-4d3154fa8464","status":"failed","message":"固件下载失败,仍运行原版本"}
|
||
```
|
||
|
||
成功输出 data:
|
||
|
||
```json
|
||
{"status":"ok"}
|
||
```
|
||
|
||
| 输入字段 | 中文说明与用途 |
|
||
|---|---|
|
||
| task_id | 必须是平台发给本设备的升级任务ID |
|
||
| status | accepted=接收并准备升级;success=实际运行新版本;failed=升级失败 |
|
||
| firmware_version / version_code | success时必填,必须同时与目标名称和数字顺序号一致 |
|
||
| message | 失败原因,最多500字节,可省略 |
|
||
|
||
上报允许重试;完成任务后继续每30秒心跳,填写实际运行的版本和顺序号。不能只因为下载完就上报success。网络不通时保留结果,重连后补报。
|
||
|
||
平台任务状态:pending=已发送、accepted=设备升级中、success=已完成、failed=失败、timeout=结果超时。30秒未确认或10分钟未完成转为timeout,超时不代表升级成功;设备可以补报success/failed。过期任务不能再接收为accepted。
|
||
|
||
防火墙:设备 MQTT 需要公网 TCP 1883,HTTP 接口与固件下载仍走 HTTPS 443。不需要给小程序直接连接 MQTT。
|