Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
@@ -0,0 +1,184 @@
# 4G 模块 AT 指令与配置功能说明(EC200A)
本目录实现 **Quectel EC200A** 与 MCU 的 AT 交互:**蜂窝注册 → PDP → TCP/TLS 业务**,以及 **FTP(S) 远程升级****GetDiagnostics 日志上传**。下文按 **AT 流程**、**透传 / 非透传**(含 **TCP 与 FTP 分 PDP**)、**FTP 升级与日志两套状态机**、**plat_comm / OCPP 衔接**、**配置宏与排障** 说明。
---
## 目录与文件速览
| 文件 | 作用 |
|------|------|
| `ec200a_4g_cfg.h` | 透传开关、TCP/FTP **分 PDP**、PlatComm FTP 与平台间隔、**QFREAD 分片**、**QFTPGET(COM) 分片收包**(与 QFREAD 同思路的轮询等待)、**QFWRITE 分片轮询**、**UFS 单次读写块 `EC200A_FTP_UFS_RW_CHUNK_BYTES`(默认 4096)**、FTP 期间是否暂缓 **QISEND**、日志块大小、日志收尾到 **QISEND** 前延时等 |
| `ec200a_4g.h` / `ec200a_4g.c` | 初始化状态机、UART 收发、`QIOPEN`/`QSSLOPEN`、非透传 **`QISEND`/`QIRD`**(明文 TCP)与 **`QSSLSEND`/`QSSLRECV`**`wss`/`QSSLOPEN`)、软重连等 |
| `ec200a_4g_ftp.h` / `ec200a_4g_ftp.c` | **`u8_ec200a_4g_ftp_update`**(固件升级)、**`u8_ec200a_4g_ftp_log_upload`**(诊断日志)、**`v_ftp_ctrl_init`** |
上层业务(如 OCPP WebSocket)经 **`bs_connect_impl.c`** 的 **`v_bs_send` / `u16_bs_recv`** 接入 4GFTP 调度由 **`bs_public_impl.c`** 的 **`v_ftp_update_action`** 在 PlatComm 中周期调用。OCPP 侧置位 **`u8_updata_flag`**、**`u8_log_file_upload`** 等与 **`BS_ocpp_ctrl.c`** 协同(见第四节)。
---
## 一、AT 指令流程(蜂窝初始化与建链)
### 1.1 前置:`v_4g_data_init`
应用解析配置里的服务器 URL(域名、端口、APN、`ws://`/`wss://`)填入 **`S_4G_CONFIG_PARA`**,再调用 **`v_4g_data_init`**,内部 **`s8_cat_reboot(0)`** 完成模组上电/复位类动作,随后周期性调用 **`s8_ec200a_4g_init`** 推进状态机。
### 1.2 主初始化路径(`s8_ec200a_4g_init`
状态枚举 **`E_EC200A_4G_INIT_STEP`**,典型顺序:命令模式 → SIM → CCID → CFUN → CGREG → **业务 PDP `QICSGP`/`QIACT``EC200A_TCP_PDP_CTX_ID`** → 可选 TLS 配置 → **`QIOPEN`/`QSSLOPEN`** → 完成。
非透传在打开 Socket 前会 **`s8_ec200a_qisde_apply()`**`AT+QISDE`),再 **`s8_ec200a_qiopen_accumulate`** 等。
### 1.3 建链与 access_mode
- **`EC200A_TCP_QIOPEN_ACCESS_MODE`**:透传为 **2**(等 `CONNECT`),缓冲模式为 **0**(轮询 `+QIOPEN`/`+QSSLOPEN` 错误码)。
- 业务 Socket 使用 **`EC200A_TCP_PDP_CTX_ID`**(默认 **1**),与 FTP 所用 PDP **必须不同**(见 `ec200a_4g_cfg.h` 编译期校验)。
### 1.4 超时与恢复
状态机末尾根据 **`u8_out_cnt`** 累加;过久会打日志、回退 **`STEP_SWCHTO_CMD_MODE`** 并可能 **`s8_cat_reboot`**。非透传下 TCP 断连可先做 **QICLOSE+QIOPEN 软重连**,连续失败后再全量 **`v_bs_4G_data_init`**(见 `ec200a_4g_cfg.h`**`EC200A_TCP_SOFT_REOPEN_FAIL_MAX`**)。
---
## 二、透传与非透传(缓冲模式)
### 2.1 配置关系(`ec200a_4g_cfg.h` 摘要)
| 宏 | 作用 |
|----|------|
| **`EC200A_TCP_USE_TRANSPARENT_MODE`** | **1** 透传;**0** 缓冲(`QISEND`/`QIRD` |
| **`EC200A_TCP_PDP_CTX_ID` / `EC200A_FTP_PDP_CTX_ID`** | 业务 TCP 与 FTP **分 PDP**(默认 1 / 2),**禁止相等** |
| **`EC200A_FTP_TO_PLATFORM_GAP_MS`** | 每轮 PlatComm 内 **`v_ftp_update_action` 一步之后**、平台收发循环前的间隔 (ms),减轻 FTP AT 与 **QISEND** 粘连 |
| **`EC200A_FTP_QFREAD_*`** | 非透传下 **`AT+QFREAD`**(日志读 UFS)及 **`AT+QFTPGET``COM:`** 固件分片:应答在单周期内 **短 `mSleep` + 收包累加** 直至 `\r\nOK`**QFTPGET** 侧等待轮次约为 **`EC200A_FTP_QFREAD_TOTAL_WAITS` 的 12 倍**(FTP 拉网侧数据较慢,见 `s16_qftpget_com_at_slice` |
| **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** | 模组 UFS 单次 **`AT+QFREAD` / `AT+QFWRITE`** 数据负载,同时也是 **固件 `AT+QFTPGET="…","COM:",off,len` 的单片长度**(默认 **4096 = 1024×4**);须为 Flash **`PACKET_SIZE`(1024)** 的整数倍(编译期校验)。固件下载每片经 **CONNECT 负载** 解析后,按 **`upgrade_write_packet`** 与 **`QFREAD` 读 UFS 写 Flash** 相同的 **flash_base + 1024 子包** 逻辑写入 |
| **`EC200A_FTP_QFWRITE_*`** | 非透传下 **`AT+QFWRITE`**CONNECT→发负载→`\r\nOK` 跨多轮 PlatComm **短等轮询**`QFWRITE_WAIT_MS` / `QFWRITE_WAITS_PER_PLAT_ROUND` / `QFWRITE_TOTAL_WAITS`),避免单周期长时间阻塞 UART |
| **`EC200A_FTP_LOG_CHUNK_SIZE` / `EC200A_FTP_LOG_QFWRITE_MAX_BYTES`** | 默认与 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** 对齐;日志从 FATFS 读块与单次 QFWRITE 负载一致,**一步一块**、包间回到 AT 态 |
| **`EC200A_BLOCK_QISEND_DURING_4G_FTP`** | **1**(默认):`u8_updata_flag` 且 4G 模组 FTP 时 **`v_bs_send`/`ws_send` 暂缓 QISEND**QFTPPUT 大文件期间避免与 AT 抢 UART;**0** 尝试穿插(仍可能 `no prompt >`)。**勿**仅用 `u8_log_file_upload` 判暂缓(recv 早于 CallResult 置位会挡应答)。 |
| **`EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS`** | 日志上传收尾(**`QFTPCLOSE`**、`v_ftp_ctrl_init`、排空 RX)后,首帧 OCPP **QISEND** 前短延时,减轻 **`break off=0`** |
| **`EC200A_FTP_LOG_DONE_REQUEST_BS_RENEW`** | 日志上传 **`STEP_FTP_INIT_OK`**:是否 **`v_set_bs_renew`**(默认 **0**,不重连 OCPP);置 **1** 时 FTP 后软重开 TCP 并 **重登** |
| **`EC200A_TCP_APP_RW_MAX`** / **`EC200A_QIRD_AT_READ_MAX`** / **`EC200A_QISEND_AT_LEN_MAX`** | 应用单次读写与 Quectel AT 上限(1500 / 1460)钳位 |
### 2.2 平台统一收发(`bs_connect_impl.c`
**`u8_netType == 0`4G** 时:`v_bs_send` → 透传 **`v_4g_send_data`** / 非透传 **`v_ec200a_tcp_app_send`**`u16_bs_recv` 同理。
非透传 **`s8_ec200a_qisend_once`**:先发 **`AT+QISEND=id,len`**,等 **`>`**,再发二进制负载,再等 **`SEND OK`**;发送前 **`ec200a_modem_uart_rx_drain`** 减轻粘连。详见源码注释与历史版本本文档附录(若需可从版本管理拷贝为 `4G模块AT指令配置功能说明_appendix.md`)。
### 2.3 TCP 与 FTP 的关系(重点)
| 维度 | 说明 |
|------|------|
| **PDP** | 业务 TCP 与 FTP 使用 **不同 context**FTP 侧 **`QIDEACT`/`QIACT`** 只作用 **`EC200A_FTP_PDP_CTX_ID`****不拆**业务 TCP 所在 PDP。 |
| **UART** | 物理 **一根** 串口;**`QISEND`/`QIRD`/`QFTP*`/`QFREAD`/`QFWRITE`** 均为 **分时串行**。 |
| **“并存”** | 模组协议栈内 TCP 与 FTP 会话可同时存在;MCU **交替发 AT**,单时刻 UART 上只有一种事务。 |
| **PlatComm 与联网穿插** | **`plat_comm_task.c`**4G FTP 活跃时 **相邻两周期交替**——一周期只做 **联网**`v_plat_comm_network_round`:双路 `recv` + 各平台 `send_ctrl`),下一周期只做 **FTP 一步**`v_plat_comm_ftp_round``recv`**`v_ftp_update_action`** → **`EC200A_FTP_TO_PLATFORM_GAP_MS`**)。配合 **QFTPGET(COM) / QFREAD / QFWRITE 分包 + 短等**FTP 传输期间 **OCPP 仍可周期性收发包**(非透传路径)。 |
---
## 三、FTP:固件升级与日志上传(两套路径)
**`u8_ec200a_4g_ftp_update`** 与 **`u8_ec200a_4g_ftp_log_upload`** 共用全局 **`e_ec200a_ftp_init_step`** 与同一套 **`STEP_FTP_*`** 枚举,但 **跳转序列不同**;进入任一路径前通常 **`v_ftp_ctrl_init()`** 将步进复位到 **`STEP_FTP_SWCHTO_CMD_MODE`**(或由业务在 **`GetDiagnostics_res`** 等里显式调用)。
**`v_ftp_update_action``bs_public_impl.c`** 分支逻辑:
1.**`s_bs_task_ctrl.ocpp_data.u8_log_file_upload == 1`**(且编译开启 OCPP):走 **`u8_ec200a_4g_ftp_log_upload()`**(4G);网线且未实现日志 FTP 时会清标志并上报失败。
2. **否则**:走 **`u8_ec200a_4g_ftp_update()`**(4G 固件)或 LwIP 升级状态机。
因此 **`u8_log_file_upload` 必须与 `u8_updata_flag` 配合正确**:若误清 **`u8_log_file_upload`**,会在 **`u8_updata_flag` 仍为 1** 时误入 **固件升级** 分支,出现日志里的 **`[FTP-UPG]`** 与 **`RX pkt(0)/(3)`** 等异常(**`BS_ocpp_ctrl.c`** 中已去掉在 **`u8_updata_flag==0` 时无条件清 `u8_log_file_upload`** 的逻辑)。
### 3.1 固件远程升级(`u8_ec200a_4g_ftp_update`
- **入口**PlatComm 周期调用 **`v_ftp_update_action`** → **`u8_ec200a_4g_ftp_update()`**(当 **`u8_log_file_upload==0`** 且 **`t_ftp_update_info.u8_netType==0`**)。
- **日志标签****`[FTP-UPG] step=…`**(与日志上传的 **`ftp test …`** 区分)。
- **配置****`T_FTP_UPDATE_INFO t_ftp_update_info`**(URL、主机、端口、路径、**固件文件名**、账号等)。
**远端对象路径**:由 **`s32_ftp_fill_remote_get_object()`** 将 **`u8_path` + `u8_file_name`** 拼成 **`s_ftp_remote_get_obj`**(如 `/home/ftpuser/ftpfile/firmware/CCU601E.bin`),供 **`QFTPSIZE`** 与 **`QFTPGET`** 使用,**避免仅 CWD 到绝对路径失败时无法取文件**。
**`QFTPCWD`**:路径过长则跳过;先 **`AT+QFTPCWD="<path>"`**,失败再试 **去掉前导 `/`**;仍失败则 **打日志并返回成功步进(1**,后续依赖 **全路径 `QFTPSIZE` / `QFTPGET`**
**与源码一致的步骤顺序(摘要)**
| 顺序 | 枚举步 | 行为要点 |
|------|--------|----------|
| 1 | `STEP_FTP_SWCHTO_CMD_MODE` | **`s8_work_mode_cmd`**:退出透传、**`AT`** |
| 23 | `SET_CGREG` / `QUERY_CGREG` | PS 注册 |
| 45 | `SET_QIDEACT` / `SET_QIACT` | 仅 **FTP PDP**:去激活 → **`QICSGP`+`QIACT`** |
| 610 | `FTP_CFG_*` | **`QFTPCFG`**contextid、account、filetype、transmode、rsptimeout |
| 11 | `QFTPOPEN` | 登录 FTP 服务器 |
| 12 | `QFTPCWD` | 切换远程目录(失败时仍可继续,见上) |
| 13 | `GET_QFTPSIZE` | **`QFTPSIZE`** 对 **`s_ftp_remote_get_obj`** 取远程大小 → **`u32_file_size`**;成功则按 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** 计算 **`u16_packSize` / `u16_packNum`****`u16_packIndex=0`** |
| 14 | `QFDEL` | 清模组 **UFS** 旧文件(与日志/杂项共用空间,仍建议执行) |
| 15 | `GET_QFTPGET` | 循环 **`AT+QFTPGET="<remote>","COM:",offset,len`****非透传** 用 **`s16_qftpget_com_at_slice`**(与 **`s16_qfread_at_slice`** 同思路等 **OK**);**透传** 用 **`s16_4g_Send_Recv_Ftp_Get`**。首片前 **`v_upgrade_header_data(0)`****`parse_4g_ftp_response`** 取 CONNECT 与 **OK** 间二进制,按与 **`s8_ec200a_qfread`** 相同的 **`flash_base` + 1024 子包** 调 **`upgrade_write_packet`**。**返回 0** 表示尚有分片;**1** 表示全部写完 |
| 16 | `STEP_FTP_SAVE` / `INIT_OK` | **`v_upgrade_header_data(1)`** + **`v_sys_reboot()`** |
**已不再作为升级主路径****`QFLST``QFOPEN``QFSEEK``QFREAD`(从 UFS 整包下载后再读)**;源码中相关 **`case`** 仍保留,供其它场景或对照,**正常 OCPP 固件升级不会走入**。
**超时**:全局 **`u8_out_cnt`** 超过约 **3 分钟****`u8_updata_flag`**、打 **`FTP TO, exit upg`****`QUERY_CGREG`** 另有 **120 次** 强制跳转。
### 3.2 GetDiagnostics 日志上传(`u8_ec200a_4g_ftp_log_upload`
- **入口****`u8_log_file_upload==1`** 时由 **`v_ftp_update_action`** 调用。
- **日志标签****`ftp test <步号>`**。
- **典型流程**:命令模式 → **UFS `QFDEL`** → 本地日志信息(**`s8_get_log_info`**:按 **`EC200A_FTP_LOG_CHUNK_SIZE`** 等收紧 **`u16_packSize`**)→ **`QFOPEN`** 在 UFS 上覆盖打开日志文件 → **`STEP_FTP_FTP_WRITE_LOG_UFS`****`QFSEEK` + `QFWRITE`** 从 FATFS **按块(默认 4096** 写入模组 UFS**`QFWRITE` 在单周期内仅短等轮询**,未收齐 **CONNECT/OK****`s8_ec200a_write_log_data` 返回进行中**,下一 FTP 半周期续传(**不采用整段 `QFUPL` 透传写文件`**,避免长时间占 UART)→ **`QFLST`** →(与升级类似的)**CGREG / FTP PDP `QIDEACT`/`QIACT`** → **`QFTPCFG` 链** → **`QFTPOPEN`/`QFTPCWD`** → **`QFTPPUT`** 上传到远端 → **`AT+QFTPCLOSE`****`s8_ec200a_qftpclose`**)→ **`STEP_FTP_INIT_OK`** 等待若干周期 → 清 **`u8_updata_flag`**、**`u8_log_file_upload`**、**`v_ftp_ctrl_init`**,并在非透传下 **排空 RX + `EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS` 延时 + 再 drain**,减轻紧接着的 **Uploaded** / **StatusNotification** 出现 **`break off=0`**。
**`EC200A_FTP_LOG_CHUNK_SIZE` / `EC200A_FTP_LOG_QFWRITE_MAX_BYTES`**:默认与 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** 一致;控制从 FATFS 单次读取与单次 **QFWRITE** 负载。注意 **读缓冲 + 过滤缓冲** 的 RAM 占用(过滤后长度 ≤ 当块读长)。
### 3.3 与「误走升级分支」的鉴别
| 现象 | 含义 |
|------|------|
| 日志 **`[FTP-UPG]`** + **`QFTPGET(COM)` / `step``GET_QFTPGET`** | 正在跑 **`u8_ec200a_4g_ftp_update`** |
| 日志 **`ftp test`** | 正在跑 **`u8_ec200a_4g_ftp_log_upload`** |
| GetDiagnostics 后出现 **`[FTP-UPG]`** 且 **`RX pkt(0)/(3)`** 卡进度 | 多为 **`u8_log_file_upload` 被清 0** 仍 **`u8_updata_flag==1`**,已按上节在 OCPP 侧修复 |
---
## 四、与 plat_comm、OCPP、互斥策略的衔接
### 4.1 `plat_comm_task.c`
- **`u8_ftp_transfer_active`**:任一路 **`u8_updata_flag!=0`**;若开启 OCPP,在 **4G****`u8_log_file_upload==1`** 时,即使 **`u8_updata_flag` 仍为 0**GetDiagnostics **CallResult 尚未置位 `updata_flag`** 的窗口),也视为 **FTP 活跃**,以便调度 **`v_ftp_update_action`** 跑日志状态机。
- **`ftp4g`****`u8_ftp_transfer_active && t_ftp_update_info.u8_netType==0`**。为真时:使用 **静态相位 `s_plat_comm_net_ftp_phase`** —— **奇偶半周期交替**:一半周期只跑 **`v_plat_comm_network_round`**(联网收发),另一半周期只跑 **`v_plat_comm_ftp_round`**(开头双路 **`v_bs_recv_data`** → **`v_ftp_update_action` 一步** → **`EC200A_FTP_TO_PLATFORM_GAP_MS`**)。这样 **FTP 与 OCPP 不在同一 PlatComm 周期内叠执行**,与 **`QFTPGET(COM)`/`QFREAD`/`QFWRITE` 分包 + 短等** 叠加后,**FTP 传输过程中仍可稳定进行联网数据收发**。
- **网线 FTP****`u8_ftp_transfer_active && netType!=0`** 时独占整周期 **`continue`**(与 4G 交替策略不同)。
### 4.2 `u8_bs_ftp_ocpp_strict_mux``plat_comm_task.h` / `v_plat_data_init`
- **默认 0**:不在 FTP 期间强行拦截 OCPP **`v_bs_send`**;大文件上传请依赖 **`EC200A_BLOCK_QISEND_DURING_4G_FTP`(默认已 1)** 暂缓 QISEND,否则易 **`no prompt >`**。若仍要业务帧与 FTP 硬穿插可 **`#define EC200A_BLOCK_QISEND_DURING_4G_FTP 0`** 并拉大 **`EC200A_FTP_TO_PLATFORM_GAP_MS`**,或运行时置 **`u8_bs_ftp_ocpp_strict_mux=1`**。
- **为 1****`bs_connect_impl.c`** 在 **模组 FTP 传输请求有效** 且目标为 OCPP 通道时 **return** 掉发送;**`bs_public_impl.c`** 在 OCPP **4G TX busy** 时暂缓 **`v_ftp_update_action`**。与 **GetDiagnostics****「先 CallResult / Uploading,再置 `u8_updata_flag`」** 顺序配合使用(见 **`BS_ocpp_ctrl.c`**)。
### 4.3 OCPP 要点(文件在 `ocpp/`,此处仅列衔接)
- **GetDiagnostics****`GetDiagnostics_mag`** 解析 URL 并置 **`u8_log_file_upload`****`GetDiagnostics_res`** 准备 **`fileName`**、**`v_ftp_ctrl_init`****不在组帧前**置 **`u8_updata_flag`**,避免与 strict mux 组合导致 **CallResult 发不出**。**`send_mag`** 在 **CallResult `ws_send` 成功** 后置 **`u8_diag_notify_pending=Uploading`****`ocpp_send_mag_ctrl`** 在 **Uploading****`send_mag` 之后** 再置 **`u8_updata_flag=1`**。与 **4G 分包 FTP、PlatComm 半周期交替** 的衔接说明见 **`ocpp/OCPP功能模块说明.md`** **第 9.1 节**
- **`v_ocpp_private_logic`**:不再在 **`u8_updata_flag==0`** 时无条件 **`u8_log_file_upload=0`**,避免误切 **固件升级** 分支。
### 4.4 透传模式下的 FTP
透传(**`EC200A_TCP_USE_TRANSPARENT_MODE==1`**)下,**日志写 UFS 的 `QFOPEN`/`QFSEEK`/`QFWRITE` 路径不参与编译**(相关步进会失败或超时退出);**固件升级** 仍使用 **`QFTPGET``COM:`** 分片 + **`s16_4g_Send_Recv_Ftp_Get`**,与缓冲模式相比 UART 占用特征不同,需单独测试。
---
## 五、配置与排障建议
| 问题倾向 | 建议 |
|-----------|------|
| **`4G QISEND: no prompt >` / `break off=0`** | 保持 **`EC200A_BLOCK_QISEND_DURING_4G_FTP=1`**(默认);增大 **`EC200A_FTP_TO_PLATFORM_GAP_MS`**;确认 **日志已走 `QFOPEN+QFSEEK+QFWRITE` 分包**(勿再依赖长时 **`QFUPL`** 透传);固件已走 **`QFTPGET(COM)`** 分片;可调 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`**(须为 1024 倍数)、**`EC200A_FTP_QFREAD_*`**(同时影响 QFTPGET 片内等待上限);或 **`u8_bs_ftp_ocpp_strict_mux=1`** |
| **`QSSLOPEN` 成功但 `AT+QISEND` 立刻 ERROR** | **`wss` 须用 `AT+QSSLSEND` / `AT+QSSLRECV`**,不能对 SSL socket 发 **`QISEND`/`QIRD`**(固件 `ec200a_4g.c` 已按 `use_tls` 分支) |
| **`QFTPCWD` 失败但需升级** | 当前实现会在 CWD 失败后仍继续;若仍 **QFTPSIZE/QFTPGET** 失败,核对 **FTP 上实际路径****`s_ftp_remote_get_obj`** 拼接是否一致(含前导 `/` |
| **日志上传结束后首包 OCPP 失败** | 确认 **`QFTPCLOSE`**;调大 **`EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS`**;若确认 TCP 已死且可接受重登,再设 **`EC200A_FTP_LOG_DONE_REQUEST_BS_RENEW=1`** |
| **GetDiagnostics 走成 `[FTP-UPG]`** | 确认 **`u8_log_file_upload`** 未被异常清除;**`v_ftp_update_action`** 是否进入 **`u8_log_file_upload` 分支** |
| **双 PDP** | 修改 context 时同时覆盖 **TCP/FTP** 两宏且保持 **不等** |
| **QIRD ERROR** | 检查 **`want`** 是否超 **1500**(已由宏钳位) |
---
## 六、自检清单(FTP 升级相关)
- [ ] **`EC200A_TCP_PDP_CTX_ID``EC200A_FTP_PDP_CTX_ID`**
- [ ] 固件升级日志为 **`[FTP-UPG]`****`QFTPSIZE`** 使用 **远端全路径****`QFTPGET(COM)`** 分片日志 **`pkt i/N`****全部片完成后** 直接进入 **`STEP_FTP_SAVE`**(不经 **UFS QFLST/QFREAD** 主路径)
- [ ] 日志上传为 **`ftp test`**,写 UFS 阶段为 **`QFOPEN` + 循环 `QFSEEK`/`QFWRITE`(块长默认 4096)**,上传结束有 **`QFTPCLOSE`** 与收尾 **drain + 延时**
- [ ] PlatComm**4G FTP** 时周期头 **recv + FTP + gap**,且 **每平台 send 后 recv**
- [ ] OCPP**CallResult → Uploading → `u8_updata_flag`** 顺序;**不误清 `u8_log_file_upload`**
---
*路径:`CCU601E_D/app/plat_comm/4g_module/4G模块AT指令配置功能说明.md`*
*若需保留旧版「2.5 QISEND/QIRD 字节级长说明」,可从版本管理历史中拷贝为 `4G模块AT指令配置功能说明_appendix.md`。*