# 780epm **Repository Path**: killeddriver/780epm ## Basic Information - **Project Name**: 780epm - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-17 - **Last Updated**: 2026-08-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 可以,下面是**按“请求 + 应答”成对组织的小节**重排后的完整 README,一次性覆盖即可(内容保持你原协议含义,只是结构和标题风格统一,方便查阅和维护)。 ```markdown # STM32 ↔ AIR780EPM 通讯协议说明(统一版 · 20260428) 本协议定义 **STM32 单片机** 与 **AIR780EPM 模组** 之间基于 UART 的 **JSON 报文格式**。 核心设计思路: - 顶层用 `msg_type` 区分三大类:`cmd` / `data` / `status` - 每一类再用对应的“子类型字段”细分: - `msg_type = "cmd"` → 使用 `cmd_type` - `msg_type = "data"` → 使用 `data_type` - `msg_type = "status"` → 使用 `status_type` - 所有业务内容统一放到 `item` 字段中,避免顶层字段混乱。 --- ## 1. 报文总规范 ### 1.1 顶层结构 统一 JSON 结构如下(字段可选性见表): ```json { "msg_type": "cmd | data | status", "src": "stm32 | air780 | mqtt | ...", "topic": "可选,MQTT 相关时使用", "qos": 0, "cmd_type": "仅 msg_type = cmd 时使用", "data_type": "仅 msg_type = data 时使用", "status_type": "仅 msg_type = status 时使用", "item": { } } ``` ### 1.2 字段说明 | 字段名 | 类型 | 必选 | 适用场景 | 说明 | | ------------- | ------ | ---- | ---------------------------- | ----------------------------------------- | | `msg_type` | string | 是 | 所有 | 消息大类:`"cmd"` / `"data"` / `"status"` | | `src` | string | 是 | 所有 | 消息来源:`stm32` / `air780` / `mqtt` / … | | `topic` | string | 否 | MQTT 相关 | MQTT 主题名 | | `qos` | number | 否 | MQTT 相关 | MQTT QoS 等级,0/1,默认 0 | | `cmd_type` | string | 否 | `msg_type = "cmd"` 时必须 | 命令子类型(命令名) | | `data_type` | string | 否 | `msg_type = "data"` 时必须 | 数据子类型(数据类别) | | `status_type` | string | 否 | `msg_type = "status"` 时必须 | 状态子类型(状态类别) | | `item` | object | 是 | 所有 | 业务负载容器,所有具体字段放到这里 | ### 1.3 字段约束规则 - 当 `msg_type = "cmd"`: - 必须存在 `cmd_type` - 当 `msg_type = "data"`: - 必须存在 `data_type` - 当 `msg_type = "status"`: - 必须存在 `status_type` 伪代码示意: ```c if msg_type == "cmd": assert(cmd_type != NULL); assert(data_type == NULL && status_type == NULL); elif msg_type == "data": assert(data_type != NULL); assert(cmd_type == NULL && status_type == NULL); elif msg_type == "status": assert(status_type != NULL); assert(cmd_type == NULL && data_type == NULL); else: // 协议错误 ``` 约定: - 顶层 `item` 必须是一个 JSON object(Lua table / C struct),**禁止为数组或基础类型**。 - 未知字段建议忽略,保留向后兼容性。 --- ## 2. 基础命令(PING / FEED / REBOOT) ### 2.1 PING:链路探测 #### 2.1.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "PING", "item": { } } ``` #### 2.1.2 应答:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "link", "item": { "cmd": "PING", "result": "ok", // ok / fail "rtt_ms": 10 // 可选:往返时间 } } ``` --- ### 2.2 FEED:喂狗 #### 2.2.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "FEED", "item": { "note": "feed_watchdog" // 可选 } } ``` #### 2.2.2 应答 - 一般不强制回包,如需确认,可使用 `status_type = "link"` 自定义。 --- ### 2.3 REBOOT:重启 #### 2.3.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "REBOOT", "item": { "target": "module", // module / mcu / all "reason": "manual" // 可选:重启原因 } } ``` #### 2.3.2 应答(可选) ```json { "msg_type": "status", "src": "air780", "status_type": "link", "item": { "cmd": "REBOOT", "result": "ok" } } ``` --- ## 3. 模组自身 FOTA(CUSTOMER_SRV_FOTA) ### 3.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "CUSTOMER_SRV_FOTA", "item": { "url": "https://example.com/firmware/Air780EPM.bin", "version": "001.999.222", "md5": "0123456789abcdef0123456789abcdef", // 可选 "size": 1234567 // 可选 } } ``` ### 3.2 过程与结果上报:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "module_fota", "item": { "cmd": "CUSTOMER_SRV_FOTA", "stage": "downloading", // downloading / verifying / upgrading / done / failed "progress": 42, // 0-100 "error": 0, // 0 无错误,非 0:错误码 "msg": "optional status text" } } ``` --- ### 3.3 获取 AIR780EPM 4G 设备信息:STM32 → AIR780 → STM32 STM32 下发获取 AIR780EPM 4G 设备信息命令: ```json { "msg_type": "cmd", "dst": "air780", "cmd_type": "GET_AIR780EPM4G_DEV_INFO", "item": { "cmd": "GET_AIR780EPM4G_DEV_INFO" } } ``` AIR780 返回当前 4G 模组设备信息、信号状态、脚本版本和 core 版本: ```json { "msg_type": "status", "src": "air780", "status_type": "air780epm4g_dev_info", "item": { "cmd": "GET_AIR780EPM4G_DEV_INFO", "result": "ok", "imei": "861234567890123", "imsi": "460001234567890", "status": "REGISTERED", "iccid": "89860012345678901234", "csq": 25, "rssi": -65, "rsrq": -10, "rsrp": -95, "snr": 20, "simid": 0, "script_version": "1.0.0", "core_version": "LuatOS-SoC_VXXXX_Air780EPM" } } ``` 字段说明: | 字段 | 说明 | | ---------------- | ----------------------------------------------------- | | `cmd` | 命令码,固定为 `GET_AIR780EPM4G_DEV_INFO` | | `result` | 执行结果,`ok` 表示成功 | | `imei` | 模组 IMEI | | `imsi` | SIM 卡 IMSI | | `status` | 移动网络状态,对应 `mobile.status()` | | `iccid` | SIM 卡 ICCID | | `csq` | 信号质量,对应 `mobile.csq()` | | `rssi` | 接收信号强度,对应 `mobile.rssi()` | | `rsrq` | 参考信号接收质量,对应 `mobile.rsrq()` | | `rsrp` | 参考信号接收功率,对应 `mobile.rsrp()` | | `snr` | 信噪比,对应 `mobile.snr()` | | `simid` | 当前 SIM 卡 ID,对应 `mobile.simid()` | | `script_version` | AIR780EPM 当前脚本版本号,对应 Lua 中的 `VERSION` | | `core_version` | AIR780EPM 当前 core 固件版本号,对应 `rtos.version()` | --- ## 4. MCU OTA(下载 MCU 固件并分片下发给 STM32) 本节为你当前重点功能: - STM32 下发 `MCU_OTA` 指令,包含 `action` 字段控制「启动下载 / 请求分片」。 - 模组从 HTTP 下载 `.bin` 和 `.md5`,先做 MD5 校验,通过后才提供分片数据。 - 分片数据通过 **Base64 编码**,放在 `data_type = "ota_chunk"` 报文中返回。 ### 4.1 MCU OTA 信息查询(MCU_OTA_INFO) #### 4.1.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "MCU_OTA_INFO", "item": { } } ``` #### 4.1.2 应答:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "mcu_ota_info", "item": { "ready": true, // true:已下载并准备好;false:未准备好/下载失败 "now_download_size": 2048, // 可选 当前已下载大小(字节) "size": 2048, // 可选 固件总大小(字节) "version": "001.002.003", // MCU 固件版本或目标版本 "crc32": "0x12345678" // 可选 全量 CRC32 } } --- ### 4.2 MCU OTA 启动(MCU_OTA · action = "start") #### 4.2.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "MCU_OTA", "item": { "action": "start", // 固定 "start" "url": "https://example.com/mcu_ota.bin", // MCU 固件 .bin 下载地址 "version": "001.002.003", // 目标 MCU 固件版本 "md5": "0123456789abcdef0123456789abcdef", // 可选:整包 MD5(可与 .md5 文件一致) "size": 123456, // 可选:整包大小 "file": "mcu_ota.bin", // 可选:文件名,未填则从 url 中解析 "extra": { // 可选:扩展字段 "note": "optional description" } } } ``` #### 4.2.2 下载 + MD5 校验结果(start 应答):AIR780 → STM32 当模组完成以下步骤后返回该结果: 1. 通过 HTTP 下载 `.bin` 文件; 2. 基于同名 `.md5` 下载 MD5 文件,例如: 如果 `url = http://host/path/user_crc.bin` 则 MD5 URL 约定为 `http://host/path/user_crc.md5`; 3. 从 MD5 文件读取字符串 `remote_md5`; 4. 计算本地 `.bin` 文件的 MD5 `local_md5`; 5. 比较 `remote_md5` 与 `local_md5`,一致则视为下载成功。 **成功:** ```json { "msg_type": "status", "src": "air780", "status_type": "mcu_ota", "item": { "cmd": "MCU_OTA_START", "result": "ok", "size": 123456, "version": "001.002.003", "md5": "0123456789abcdef0123456789abcdef" } } ``` **失败(示例):** ```json { "msg_type": "status", "src": "air780", "status_type": "mcu_ota", "item": { "cmd": "MCU_OTA_START", "result": "fail", "reason": "md5_mismatch" // 其它可能值:url_empty / bin_http_404 / md5_http_404 / md5_read_error / md5_calc_error ... } } ``` --- ### 4.3 MCU OTA 分片请求(MCU_OTA · action = "chunk") #### 4.3.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "MCU_OTA", "item": { "action": "chunk", // 表示请求分片数据 "begin": 0, // 起始字节(含) "len": 1024, // 请求的原始二进制长度 "retry": 0 // 可选,重试计数 } } ``` - `begin`:本次请求从整包固件的第几个字节开始(0-based)。 - `len`:本次希望返回的原始二进制长度,模组可按实际文件末尾进行缩减。 --- ### 4.4 MCU OTA 分片数据(data_type = "ota_chunk",Base64) > 分片内容通过 **Base64 编码后再放入 JSON 中传输**,STM32 侧需要先 Base64 解码再使用。 #### 4.4.1 应答:AIR780 → STM32(成功) ```json { "msg_type": "data", "src": "air780", "data_type": "ota_chunk", "item": { "begin": 0, "user_end": 1024, "len": 1368, "crc16": 4660, "data": "BASE64_ENCODED_BIN_DATA" } } ``` 字段说明: | 字段名 | 类型 | 说明 | | ---------- | ------ | ------------------------------------------------------------------------- | | `begin` | number | 分片在整包固件中的起始字节偏移(含) | | `user_end` | number | 分片在整包固件中的结束字节偏移(不含),原始分片长度 = `user_end - begin` | | `len` | number | `data` 字段的 Base64 字符串长度(ASCII 长度) | | `crc16` | number | 对 Base64 字符串 `data` 计算的 CRC16(`MODBUS` 多项式) | | `data` | string | 对原始固件分片做 Base64 编码后的字符串 | AIR780 侧处理流程(已在 Lua 中实现): 1. 从已下载的 `.bin` 文件中按 `begin` / `len` 读取原始分片; 2. 计算 `raw_len = user_end - begin`; 3. `data = Base64Encode(raw_bin)`; 4. `crc16 = CRC16_MODBUS(data)`; 5. `len = #data`(字符串长度); 6. 封装为上面的 JSON 并通过 UART 发送。 STM32 侧解码流程建议: 1. 解析 JSON,确认 `msg_type = "data"` 且 `data_type = "ota_chunk"`; 2. 检查 `len(item.data) == item.len`; 3. 计算 `crc16_local = CRC16_MODBUS(item.data)`,与 `item.crc16` 比较,不一致则请求重传; 4. 对 `item.data` 做 Base64 解码,得到原始二进制 `raw_bin`: - 其长度应等于 `user_end - begin`; 5. 将 `raw_bin` 按 `begin` 偏移写入 MCU Flash 对应 OTA 区; 6. 所有分片接收完成后,对整个固件做整包校验(如整包 MD5 / CRC32)。 #### 4.4.2 失败应答(可选) 当读取失败、超出文件范围等情况时,可用 `status` 报错: ```json { "msg_type": "status", "src": "air780", "status_type": "mcu_ota", "item": { "cmd": "MCU_OTA_CHUNK", "result": "fail", "reason": "read_empty", "begin": 0, "len": 1024 } } ``` --- ## 5. 设备三元组协议(SET_DEVICE_TRIPLE) ### 5.1 STM32 → AIR780:设置 / 更新设备三元组 命令名:`SET_DEVICE_TRIPLE` 含义:设置 / 更新设备的「产品身份信息」,用于 MQTT 等连接。 #### 5.1.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "SET_DEVICE_TRIPLE", "item": { "product_key": "your_product_key", // 必选 "device_name": "your_device_name", // 必选 "device_secret": "your_device_secret", // 必选 "broker": "mqtt.example.com", // 可选:MQTT 服务器地址 "tls_port": 8883, // 可选:MQTT TLS 端口 "port": 1883, // 可选:MQTT 非 TLS 端口 "mqtt_prefix": "user", // 可选,默认 "user" "mqtt_sub_suffix": "get", // 可选,默认 "get" "mqtt_pub_suffix": "update" // 可选,默认 "update" } } ``` ### 5.2 应答:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "device_triple", "item": { "result": "ok", // ok / fail "updated": true, // true:有写 KV;false:KV 已是最新 "reason": "", // 可选:失败原因 "error": 0 // 可选:错误码,0 表示成功 } } ``` 典型返回示例: - 写入成功且发生更新: ```json { "msg_type": "status", "src": "air780", "status_type": "device_triple", "item": { "result": "ok", "updated": true, "reason": "", "error": 0 } } ``` - KV 中已是最新(无写入): ```json { "msg_type": "status", "src": "air780", "status_type": "device_triple", "item": { "result": "ok", "updated": false, "reason": "", "error": 0 } } ``` - 写 KV 失败示例: ```json { "msg_type": "status", "src": "air780", "status_type": "device_triple", "item": { "result": "fail", "updated": false, "reason": "kv_write_fail", "error": 1 } } ``` --- ## 6. 获取模组 IMEI(GET_DEVICE_IMEI) ### 6.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "GET_DEVICE_IMEI", "item": { "cmd": "GET_DEVICE_IMEI" } } ``` 字段说明: - `msg_type`:固定 `"cmd"`; - `src`:`"stm32"`; - `cmd_type`:`"GET_DEVICE_IMEI"`; - `item.cmd`:命令简写,固定 `"GET_DEVICE_IMEI"`。 ### 6.2 应答:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "device_imei", "item": { "cmd": "GET_DEVICE_IMEI", "result": "ok", "imei": "867612345678901" } } ``` 字段说明: - `status_type`:`"device_imei"`; - `item.result`:`"ok"` / `"fail"`; - `item.imei`:模组当前 IMEI 号(字符串)。 --- ## 7. 获取模组 时间(GET_DEVICE_TIME) ### 7.1 请求:STM32 → AIR780 ```json { "msg_type": "cmd", "src": "stm32", "cmd_type": "GET_DEVICE_TIME", "item": { "cmd": "GET_DEVICE_TIME" } } ``` 字段说明: - `msg_type`:固定 `"cmd"`; - `src`:`"stm32"`; - `cmd_type`:`"GET_DEVICE_TIME"`; - `item.cmd`:命令简写,固定 `"GET_DEVICE_TIME"`。 ### 7.2 应答:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "device_time", "item": { "result": "ok", "time": "1234567890" } } ``` 字段说明: - `status_type`:`"device_time"`; - `item.result`:`"ok"` / `"fail"`; - `item.time`:模组当前时间(字符串)。 --- ## 8. MQTT 数据透传 ### 8.1 上行:STM32 → AIR780 → MQTT(mqtt_up) #### 8.1.1 请求:STM32 → AIR780 ```json { "msg_type": "data", "src": "stm32", "data_type": "mqtt_up", "topic": "your/up/topic", "qos": 1, "item": { "payload": "要发到 MQTT 的内容", "format": "plain" // plain / json / bin } } ``` ### 8.2 下行:MQTT → AIR780 → STM32(mqtt_down) #### 8.2.1 上报:AIR780 → STM32 ```json { "msg_type": "data", "src": "mqtt", "data_type": "mqtt_down", "topic": "your/down/topic", "item": { "payload": "从 MQTT 服务器收到的 payload", "format": "plain" // plain / json / bin } } ``` --- ## 9. MQTT 连接状态(status_type = "mqtt") ### 9.1 上报:AIR780 → STM32 ```json { "msg_type": "status", "src": "air780", "status_type": "mqtt", "topic": "mqtt/status", "item": { "status": "online", // online / offline "reason": "net_lost" // 可选:net_lost / server_close / auth_fail ... } } ``` --- ## 10. MCU 上传日志协议格式 ### 10.1 STM32 发送一条日志 ```json { "msg_type": "cmd", "cmd_type": "MCU_LOG", "item": { "action": "append", "log": "这里是一行日志内容" } } ``` ### 10.2 AIR780 返回日志接收结果 - `msg_type`: 固定 `"status"` - `src`: 固定 `"air780"` - `status_type`: 固定 `"mcu_log"` - `item.cmd`: 固定 `"LOG_APPEND"` - `result`: `"ok"` / `"fail"` - `reason`: `"create_fail"`/ `"write_fail"` ```json { "msg_type": "status", "src": "air780", "status_type": "mcu_log", "item": { "cmd": "LOG_APPEND", "result": "fail", "reason": "write_fail" } } ``` ### 10.3 STM32 触发上传当日日志文件 ```json { "msg_type": "cmd", "cmd_type": "MCU_LOG", "item": { "action": "upload", "url": "http://your.server.com/upload/log" } } ``` ### 10.4 AIR780 返回日志上传结果 - `msg_type`: 固定 `"status"` - `src`: `"air780"` - `status_type`: `"mcu_log"` - `item.cmd`: `"LOG_UPLOAD"` - `item.result`: `"ok"` / `"fail"` - `item.reason`: `"url_empty"`/ `"file_empty"` ```json { "msg_type": "status", "src": "air780", "status_type": "mcu_log", "item": { "cmd": "LOG_UPLOAD", "result": "fail", "reason": "url_empty" } } ``` --- ## 11. 方向与典型流程小结 ### 11.1 STM32 → AIR780 典型报文 - `msg_type = "cmd"`: - `PING` - `FEED` - `REBOOT` - `CUSTOMER_SRV_FOTA` - `MCU_OTA_INFO` - `MCU_OTA`(`action = start / chunk`) - `SET_DEVICE_TRIPLE` - `GET_DEVICE_IMEI` - `msg_type = "data"`: - `data_type = "mqtt_up"`:上行 MQTT 透传 --- ### 11.2 AIR780 → STM32 典型报文 - `msg_type = "data"`: - `data_type = "mqtt_down"`:MQTT 下行透传 - `data_type = "ota_chunk"`:MCU OTA 固件分片(Base64) - `msg_type = "status"`: - `status_type = "link"`:PING/REBOOT 等结果 - `status_type = "mqtt"`:MQTT online/offline - `status_type = "module_fota"`:模组 FOTA 进度与结果 - `status_type = "device_triple"`:三元组设置结果 - `status_type = "mcu_ota"`:MCU OTA 启动/分片错误 - `status_type = "mcu_ota_info"`:MCU OTA 信息应答 --- ## 12. 实现建议 1. **解析顺序** - 先解析 JSON; - 校验 `msg_type`、`src`; - 根据 `msg_type` 读取 `cmd_type` / `data_type` / `status_type`; - 分发到对应处理函数。 2. **错误处理** - 协议错误:建议记录日志,可用 `status_type = "error"` 上报; - 业务错误:按各自业务的 `status` 报文返回(例如 `mcu_ota` 的 `reason` 字段)。 3. **版本演进** - 未知字段忽略,保持前向/后向兼容; - 推荐在应用层(如 MQTT payload、三元组配置)增加版本标识。 ---