Files
CCU621M/app/plat_comm/4g_module/ec200a_4g_ftp.c
T

2412 lines
87 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "publicdata/publicdata.h"
#if EC200A_4G_EN
#include <string.h>
#include <stdio.h>
#include "plat_comm/4g_module/ec200a_4g.h"
#include "plat_comm/4g_module/ec200a_4g_ftp.h"
#include "mylog/mylog.h"
#include "usart.h"
#include "app_init/app_init.h"
#include "plat_comm/impl/bs_public_impl.h"
#if(MY_SHELL_EN) //Shell 使能
#include "letter_shell/shell_port.h"
#endif
#include "plat_comm/plat_comm_task.h"
#include "plat_comm/impl/bs_connect_impl.h"
#include "bsp_include.h"
#include "app_fatfs/app_fatfs.h"
#include "app_fatfs/fatfs_init.h"
/* CCU621_M stubs: upgrade impl not available */
static int upgrade_erase_program_area(void) { return 0; }
static int upgrade_success_update_header(upgrade_header_t *h) { (void)h; return 0; }
static int upgrade_write_packet(uint32_t i, const uint8_t *d, uint32_t l) { (void)i; (void)d; (void)l; return (int)l; }
static U8_T fatfs_set_write_disable_flag(U8_T m, U8_T e) { (void)m; (void)e; return 0U; }
/* CCU621_M fs stubs */
static FS_Status fs_file_exists(const char *p, bool *e) { (void)p; if(e)*e=false; return FS_STATUS_OK; }
static FS_Status fs_get_file_size(const char *p, uint32_t *s) { (void)p; if(s)*s=0; return FS_STATUS_OK; }
static FS_Status fs_read_file_content(const char *p, char *b, uint32_t bs, FS_ReadRange *r) { (void)p; (void)b; (void)bs; (void)r; return FS_STATUS_OK; }
#if BS_OCPP_EN
#include "plat_comm/ocpp/BS_ocpp_ctrl.h"
/** OCPPSTEP_FTP_SAVE 先上送 Installing,再写头、FRAM 置位并复位;Installed 在 BootNotification 后上送 */
static U8_T s_u8_fw_save_ocpp_arm;
#endif
/** 本文件 FTP/升级/日志 AT 调试:不走 Plat_Comm 的 Plat_Comm_LOG,统一 MYLOG_MSG(0xFF) 直出 */
#define EC200A_FTP_LOG(fmt, ...) MYLOG_MSG(0xFF, fmt, ##__VA_ARGS__)
/* QFREAD 应答(含 CONNECT 与负载)须 ≥ 单包读长 + AT/URC 余量;与 EC200A_FTP_UFS_RW_CHUNK_BYTES 对齐 */
#define FTP_REAC_DATA_BUF_LEN ((U16_T)((EC200A_FTP_UFS_RW_CHUNK_BYTES) + 512U))
#if ((EC200A_FTP_UFS_RW_CHUNK_BYTES) % (PACKET_SIZE)) != 0
#error EC200A_FTP_UFS_RW_CHUNK_BYTES must be a multiple of PACKET_SIZE (FTP upgrade QFREAD -> flash packets).
#endif
//引出 给ftp升级配置使用
extern S8_T s8_FindAck(U8_T *src, U8_T *des);
extern S16_T s16_FindAck_Ex(U8_T *src, U16_T srcLen, U8_T *des, U16_T desLen);
extern S8_T s8_4g_Send_AT_Cmd(U8_T *sendBuf, U8_T sendLen, U8_T *ack, U8_T resendCnt, U16_T delayTimeMs);
extern S8_T s8_4g_Send_Recv_Cmd(U8_T *sendBuf, U8_T sendLen,U8_T *rcvBuf,U16_T recvLen, U8_T *ack, U8_T resendCnt, U16_T delayTimeMs);
extern S8_T s8_cat_reboot(U8_T u8_flag);
extern S8_T s8_work_mode_cmd(); // 切换到指令模式
extern S8_T s8_ec200a_set_cgreg();
extern S8_T s8_ec200a_cgreg();
typedef enum
{
STEP_FTP_SWCHTO_CMD_MODE = 0, //切换到指令模式
STEP_FTP_SET_CGREG,
STEP_FTP_QUERY_CGREG,
STEP_FTP_SET_QIDEACT, //去激活PDP场景
STEP_FTP_SET_QIACT, //激活PDP场景
STEP_FTP_FTP_CFG_contextid, //配置 FTP(S) PDP 上下文 ID
STEP_FTP_FTP_CFG_account, //配置 FTP(S) 设置用户名和密码。
STEP_FTP_FTP_CFG_filetype, //配置 FTP(S) 设置文件类型为ASCII。
STEP_FTP_FTP_CFG_transmode, //配置 FTP(S) 设置为被动传输方式。
STEP_FTP_FTP_CFG_rsptimeout, //配置 FTP(S) 设置最大响应时间(默认为90秒)。
STEP_FTP_FTP_CFG_QFTPOPEN, //登录FTP服务器。
STEP_FTP_FTP_CFG_QFTPCWD, //切换目录
STEP_FTP_FTP_CFG_QFTPLIST, //获取当前目录的内容,并通过
STEP_FTP_FTP_GET_QFTPSIZE, //获取 文件大小
STEP_FTP_FTP_QFDEL, //获取 删除存储区保存的文件内容
STEP_FTP_FTP_GET_QFTPGET, //获取 文件内容 qflst
STEP_FTP_FTP_GET_QFLST, //获取 UFS中文件内容 qflst
STEP_FTP_FTP_GET_QFOPEN, //打开文件
STEP_FTP_FTP_GET_QFSEEK, //设置读取文件指针头地址
STEP_FTP_FTP_GET_QFREAD, //读取文件
STEP_FTP_FTP_GET_LOG_INFO, //获取日志信息
STEP_FTP_FTP_UPLOAD_LOG_UFS, /* 模组 UFS:QFOPEN 覆盖写日志文件,建立句柄 */
STEP_FTP_FTP_WRITE_LOG_UFS, //写入日志文件到 UFS
STEP_FTP_FTP_UPLOAD_LOG_FTP, //上传日志文件到 FTP
/** 日志上传成功后须关闭 FTP 会话,否则 +QFTP* URC 残留易干扰同 UART 上后续 QISENDbreak off=0 */
STEP_FTP_FTP_QFTPCLOSE_AFTER_LOG,
STEP_FTP_SAVE, //保存当前设置并重启
STEP_FTP_INIT_OK, //设置完成
STEP_FTP_INIT_DEBUG, // 调试
}E_EC200A_4G_FTP_INIT_STEP;
E_EC200A_4G_FTP_INIT_STEP e_ec200a_ftp_init_step = STEP_FTP_SWCHTO_CMD_MODE;
#if !EC200A_TCP_USE_TRANSPARENT_MODE
/** 日志经 QFWRITE 写入 UFS 时已发送字节累计(QFSEEK 绝对偏移) */
static uint32_t s_log_ufs_qwrite_total;
/**
* AT+QFREAD:单次调用内发 AT→轮询至应答中出现 OK(与 QFWRITE 同思路,不跨 PlatComm 累加)。
* @param rsp_out 成功时为 pvPortMalloc 缓冲,调用方须 vPortFree;失败时为 NULL。
*/
static S16_T s16_qfread_at_slice(const U8_T *sendBuf, U16_T sendLen, U8_T **rsp_out)
{
U8_T *acc;
U16_T rcv_len = 0U;
U16_T total_waits = 0U;
S16_T ack;
U8_T ackOK[] = "OK";
if (rsp_out == NULL || sendBuf == NULL || sendLen == 0U || sendLen > 72U) {
return -1;
}
*rsp_out = NULL;
acc = (U8_T *)pvPortMalloc((size_t)FTP_REAC_DATA_BUF_LEN);
if (acc == NULL) {
EC200A_FTP_LOG("[FTP] QFREAD malloc fail\r\n");
return -1;
}
memset(acc, 0, (size_t)FTP_REAC_DATA_BUF_LEN);
v_4g_send_data((U8_T)0xFF, (U8_T *)(void *)sendBuf, sendLen);
while (total_waits < (U16_T)EC200A_FTP_QFREAD_TOTAL_WAITS) {
mSleep(EC200A_FTP_UART_SLICE_WAIT_MS);
if (rcv_len + 1U < (U16_T)FTP_REAC_DATA_BUF_LEN) {
rcv_len += u16_4g_recv_data(
acc + rcv_len,
(U16_T)((U16_T)FTP_REAC_DATA_BUF_LEN - 1U - rcv_len));
} else {
EC200A_FTP_LOG("[FTP] QFREAD rsp overflow\r\n");
vPortFree(acc);
return -1;
}
total_waits++;
if (rcv_len > 0U) {
if (strstr((char *)acc, "+CME ERROR") != NULL
|| strstr((char *)acc, "\r\nERROR\r\n") != NULL) {
EC200A_FTP_LOG("[FTP] QFREAD modem ERROR\r\n");
vPortFree(acc);
return -1;
}
ack = s16_FindAck_Ex(acc, rcv_len, ackOK, (U16_T)(sizeof(ackOK) - 1U));
if (ack != (S16_T)-1) {
*rsp_out = acc;
return (S16_T)rcv_len;
}
}
}
EC200A_FTP_LOG("[FTP] QFREAD wait total exceeded (single session)\r\n");
vPortFree(acc);
return -1;
}
/** 发 QFTPGET 前排空模组 UART 残留,减轻与上一条 QIRD/URC 粘包导致帧头错位 */
static void v_qftpget_drain_modem_rx(void)
{
U8_T junk[128];
U16_T got;
uint8_t r;
for (r = 0U; r < 48U; r++) {
got = u16_4g_recv_data(junk, (U16_T)sizeof(junk));
if (got == 0U) {
break;
}
}
}
/** QFTPGET→COM:必须以行格式识别 CONNECT,避免固件二进制内出现子串 "CONNECT" 时负载起点错位 */
static int s32_qftpget_find_payload_start(const U8_T *raw, uint32_t raw_len, uint32_t *out_data_start)
{
static const uint8_t p_crlf[] = "\r\nCONNECT\r\n";
const uint32_t n_crlf = (uint32_t)(sizeof(p_crlf) - 1U);
static const uint8_t p_lf[] = "\nCONNECT\n";
const uint32_t n_lf = (uint32_t)(sizeof(p_lf) - 1U);
static const uint8_t p_tail[] = "CONNECT\r\n";
const uint32_t n_tail = (uint32_t)(sizeof(p_tail) - 1U);
static const char atpfx[] = "AT+QFTPGET";
const uint32_t n_at = (uint32_t)(sizeof(atpfx) - 1U);
uint32_t i;
uint32_t atmin = 0U;
uint8_t found_at = 0U;
if (raw == NULL || out_data_start == NULL) {
return -1;
}
for (i = 0U; i + n_crlf <= raw_len; i++) {
if (memcmp(raw + i, p_crlf, (size_t)n_crlf) == 0) {
*out_data_start = i + n_crlf;
return 0;
}
}
for (i = 0U; i + n_lf <= raw_len; i++) {
if (memcmp(raw + i, p_lf, (size_t)n_lf) == 0) {
*out_data_start = i + n_lf;
return 0;
}
}
{
static const uint8_t p_lfcrlf[] = "\nCONNECT\r\n";
const uint32_t n_lfcrlf = (uint32_t)(sizeof(p_lfcrlf) - 1U);
for (i = 0U; i + n_lfcrlf <= raw_len; i++) {
if (memcmp(raw + i, p_lfcrlf, (size_t)n_lfcrlf) == 0) {
*out_data_start = i + n_lfcrlf;
return 0;
}
}
}
for (i = 0U; i + n_at <= raw_len; i++) {
if (memcmp(raw + i, atpfx, (size_t)n_at) == 0) {
atmin = i;
found_at = 1U;
break;
}
}
if (found_at != 0U) {
for (i = atmin; i + n_tail <= raw_len; i++) {
if (memcmp(raw + i, p_tail, (size_t)n_tail) == 0) {
*out_data_start = i + n_tail;
return 0;
}
}
}
return -1;
}
/** 仅在 [0, scan_len) 内查 AT 失败串;CONNECT 之后为固件二进制,勿全缓冲 strstr(易误判) */
static int s32_qftpget_prefix_has_modem_fail(const U8_T *buf, uint32_t scan_len)
{
static const char k_cme[] = "+CME ERROR";
static const char k_err[] = "\r\nERROR\r\n";
const uint32_t L1 = (uint32_t)(sizeof(k_cme) - 1U);
const uint32_t L2 = (uint32_t)(sizeof(k_err) - 1U);
uint32_t n;
if (buf == NULL || scan_len == 0U) {
return 0;
}
if (scan_len >= L1) {
for (n = 0U; n + L1 <= scan_len; n++) {
if (memcmp(buf + n, k_cme, (size_t)L1) == 0) {
return 1;
}
}
}
if (scan_len >= L2) {
for (n = 0U; n + L2 <= scan_len; n++) {
if (memcmp(buf + n, k_err, (size_t)L2) == 0) {
return 1;
}
}
}
return 0;
}
/**
* AT+QFTPGET="remote","COM:",off,len:非透传下与 QFREAD 同思路,拼 UART 直至 **CONNECT 后 expect_payload 字节 + \\r\\nOK**。
* 不得仅因缓冲区内出现「OK」(如 QIRD 的 \\r\\nOK)提前返回,否则会截断固件二进制。
* @param payload_off_out 成功时输出 CONNECT 负载首字节在 acc 内下标
*/
static S16_T s16_qftpget_com_at_slice(const U8_T *sendBuf, U16_T sendLen, U8_T **rsp_out,
U16_T expect_payload, uint32_t *payload_off_out)
{
U8_T *acc;
U16_T rcv_len = 0U;
U16_T total_waits = 0U;
const U16_T max_waits = (U16_T)((EC200A_FTP_QFREAD_TOTAL_WAITS) * 12U);
if (rsp_out == NULL || payload_off_out == NULL || sendBuf == NULL || sendLen == 0U || sendLen > 220U
|| expect_payload == 0U) {
return -1;
}
*rsp_out = NULL;
*payload_off_out = 0U;
acc = (U8_T *)pvPortMalloc((size_t)FTP_REAC_DATA_BUF_LEN);
if (acc == NULL) {
EC200A_FTP_LOG("[FTP] QFTPGET(COM) malloc fail\r\n");
return -1;
}
memset(acc, 0, (size_t)FTP_REAC_DATA_BUF_LEN);
v_qftpget_drain_modem_rx();
v_4g_send_data((U8_T)0xFF, (U8_T *)(void *)sendBuf, sendLen);
while (total_waits < max_waits) {
mSleep(EC200A_FTP_UART_SLICE_WAIT_MS);
if (rcv_len + 1U < (U16_T)FTP_REAC_DATA_BUF_LEN) {
rcv_len += u16_4g_recv_data(
acc + rcv_len,
(U16_T)((U16_T)FTP_REAC_DATA_BUF_LEN - 1U - rcv_len));
} else {
EC200A_FTP_LOG("[FTP] QFTPGET(COM) rsp overflow\r\n");
vPortFree(acc);
return -1;
}
total_waits++;
if (rcv_len > 0U) {
uint32_t ds = 0U;
int pay_ok;
uint32_t scan_top;
pay_ok = s32_qftpget_find_payload_start(acc, (uint32_t)rcv_len, &ds);
if (pay_ok == 0) {
scan_top = ds;
} else {
scan_top = (uint32_t)rcv_len;
if (scan_top > 256U) {
scan_top = 256U;
}
}
if (s32_qftpget_prefix_has_modem_fail(acc, scan_top)) {
EC200A_FTP_LOG("[FTP] QFTPGET(COM) modem ERROR (prefix scan top=%u)\r\n",
(unsigned)scan_top);
vPortFree(acc);
return -1;
}
if (pay_ok == 0) {
uint32_t need = ds + (uint32_t)expect_payload + 4U;
if (rcv_len >= need) {
if (memcmp(acc + ds + expect_payload, "\r\nOK", 4U) == 0) {
*rsp_out = acc;
*payload_off_out = ds;
return (S16_T)rcv_len;
}
}
}
}
}
EC200A_FTP_LOG("[FTP] QFTPGET(COM) wait total exceeded (expect_payload=%u)\r\n", (unsigned)expect_payload);
vPortFree(acc);
return -1;
}
static void s8_ec200a_qfclose_log_fd(void)
{
U8_T b[48];
int fd = (int)t_ftp_update_info.u32_file_fd;
if (fd <= 0) {
return;
}
(void)snprintf((char *)b, sizeof(b), "AT+QFCLOSE=%d\r\n", fd);
(void)s8_4g_Send_AT_Cmd(b, (U16_T)strlen((char *)b), NULL, 5U, 200U);
t_ftp_update_info.u32_file_fd = 0U;
}
/** 日志上传:QFOPEN 覆盖写 UFS 裸文件名(与 QFTPPUT 侧 UFS: 名一致) */
static S8_T s8_ec200a_qfopen_log_ufs(void)
{
U8_T u8_sendBuf[96] = {0};
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
U8_T u8_ack[] = "+QFOPEN:";
char *last_slash = strrchr((char *)t_ftp_update_info.u8_file_name, '/');
S8_T s8_ret;
int fdi = 0;
if (last_slash == NULL) {
return -1;
}
(void)snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QFOPEN=\"%s\",1\r\n", last_slash + 1);
s8_ret = s8_4g_Send_Recv_Cmd(u8_sendBuf, (U16_T)strlen((char *)u8_sendBuf), rcvBuf, BS_RECV_BUF_LEN, u8_ack, 5, 200);
if (s8_ret < 0) {
return -1;
}
if (sscanf((char *)&rcvBuf[s8_ret], "+QFOPEN: %d", &fdi) != 1 || fdi <= 0) {
return -1;
}
t_ftp_update_info.u32_file_fd = (U32_T)fdi;
s_log_ufs_qwrite_total = 0U;
EC200A_FTP_LOG("[FTP-LOG] QFOPEN ok fd=%d file=%s\r\n", fdi, last_slash + 1);
return 0;
}
static S8_T s8_ec200a_qfseek_log_abs(uint32_t abs_off)
{
U8_T u8_sendBuf[64] = {0};
int fd = (int)t_ftp_update_info.u32_file_fd;
if (fd <= 0) {
return -1;
}
(void)snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QFSEEK=%d,%lu,0\r\n", fd, (unsigned long)abs_off);
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)strlen((char *)u8_sendBuf), NULL, 5, 150);
}
/** 单包 QFWRITE:本次调用内发 AT→轮询 CONNECT→发负载→等 OK;UART 应答仅在函数内局部 `acc` 拼接,不跨 PlatComm。
* 负载用 `pvPortMalloc(wlen)`,超过 `EC200A_FTP_QFWRITE_TOTAL_WAITS` 仍未完成则返回 -1。 */
static S8_T s8_ec200a_qfwrite_log_bin(const U8_T *data, U16_T wlen)
{
U8_T cmd[72] = {0};
U8_T acc[512];
U16_T acc_len = 0;
U8_T phase = 0;
U16_T total_waits = 0;
U32_T tout = (U32_T)EC200A_FTP_QFUPL_AT_TIMEOUT_SEC;
int fd = (int)t_ftp_update_info.u32_file_fd;
const char *needle_ok = "\r\nOK";
U8_T *pay = NULL;
if (data == NULL || wlen == 0U) {
return (S8_T)-1;
}
if (wlen > (U16_T)EC200A_FTP_UFS_RW_CHUNK_BYTES) {
return (S8_T)-1;
}
if (fd <= 0) {
return (S8_T)-1;
}
if (tout < 5U) {
tout = 5U;
}
if (tout > 65535U) {
tout = 65535U;
}
pay = (U8_T *)pvPortMalloc((size_t)wlen);
if (pay == NULL) {
EC200A_FTP_LOG("[FTP-LOG] QFWRITE malloc pay fail\r\n");
return (S8_T)-1;
}
memcpy(pay, data, (size_t)wlen);
memset(acc, 0, sizeof(acc));
(void)snprintf((char *)cmd, sizeof(cmd), "AT+QFWRITE=%d,%u,%lu\r\n", fd, (unsigned)wlen, (unsigned long)tout);
v_4g_send_data((U8_T)0xFF, cmd, (U16_T)strlen((char *)cmd));
while (total_waits < (U16_T)EC200A_FTP_QFWRITE_TOTAL_WAITS) {
mSleep(EC200A_FTP_UART_SLICE_WAIT_MS);
if (acc_len + 32U < sizeof(acc)) {
acc_len += u16_4g_recv_data(acc + acc_len,
(U16_T)(sizeof(acc) - 1U - acc_len));
} else {
if (acc_len > 160U) {
memmove(acc, acc + 160U, (size_t)(acc_len - 160U));
acc_len = (U16_T)(acc_len - 160U);
} else {
acc_len = 0U;
}
acc_len += u16_4g_recv_data(acc + acc_len,
(U16_T)(sizeof(acc) - 1U - acc_len));
}
if (acc_len >= sizeof(acc)) {
acc_len = (U16_T)(sizeof(acc) - 1U);
}
acc[acc_len] = '\0';
total_waits++;
if (strstr((char *)acc, "+CME ERROR") != NULL || strstr((char *)acc, "\r\nERROR\r\n") != NULL) {
EC200A_FTP_LOG("[FTP-LOG] QFWRITE modem ERROR\r\n");
vPortFree(pay);
return (S8_T)-1;
}
if (phase == 0U) {
if (strstr((char *)acc, "CONNECT") != NULL) {
v_4g_send_data((U8_T)0xFF, pay, wlen);
acc_len = 0;
acc[0] = '\0';
phase = 1U;
}
} else {
if (strstr((char *)acc, needle_ok) != NULL) {
vPortFree(pay);
return 0;
}
}
}
EC200A_FTP_LOG("[FTP-LOG] QFWRITE wait total exceeded (single session)\r\n");
vPortFree(pay);
return (S8_T)-1;
}
#endif /* !EC200A_TCP_USE_TRANSPARENT_MODE */
#if 1 //ftp初始化配置
void v_ftp_ctrl_init()
{
#if !EC200A_TCP_USE_TRANSPARENT_MODE
s_log_ufs_qwrite_total = 0U;
#endif
#if BS_OCPP_EN
s_u8_fw_save_ocpp_arm = 0U;
v_ocpp_clr_fw_installing_fsn_sent();
#endif
e_ec200a_ftp_init_step = STEP_FTP_SWCHTO_CMD_MODE;
}
#endif
#if 1 //数据操作
/**
* @brief 解析4G模块FTP下载响应数据(简化版,只需验证\r\nOK)
* @param raw_data: 原始接收数据
* @param raw_len: 原始数据长度
* @param file_len: 输出参数,文件数据长度
* @retval 成功返回文件数据起始地址,失败返回NULL
*/
const uint8_t* parse_4g_ftp_response(const uint8_t *raw_data, uint32_t raw_len, uint32_t *file_len)
{
// 改为查找 "CONNECT" 关键字
const uint8_t connect_keyword[] = "CONNECT";
const uint8_t ok_footer[] = "\r\nOK";
const uint8_t connect_keyword_len = sizeof(connect_keyword) - 1; // 7字节
const uint8_t ok_footer_len = sizeof(ok_footer) - 1; // 4字节
int32_t connect_pos = -1;
int32_t ok_pos = -1;
const uint8_t *file_start = NULL;
if (raw_data == NULL || file_len == NULL) {
return NULL;
}
*file_len = 0;
// 1. 查找CONNECT关键字
for (uint32_t i = 0; i < raw_len - connect_keyword_len; i++) {
if (memcmp(raw_data + i, connect_keyword, connect_keyword_len) == 0) {
connect_pos = i;
break;
}
}
if (connect_pos < 0) {
//EC200A_FTP_LOG("Error: CONNECT keyword not found\n");
return NULL;
}
// 2. 从CONNECT位置开始,找到下一个\r\n,文件数据从这里之后开始
uint32_t data_start = connect_pos;
while (data_start < raw_len) {
if (raw_data[data_start] == '\r' && raw_data[data_start + 1] == '\n') {
data_start += 2; // 跳过\r\n
break;
}
data_start++;
}
if (data_start >= raw_len) {
//EC200A_FTP_LOG("Error: Cannot find data start after CONNECT\n");
return NULL;
}
// 3. 查找OK尾部(从后往前找,找到最后一个\r\nOK)
ok_pos = -1;
for (int32_t i = raw_len - ok_footer_len; i >= 0; i--) {
if (memcmp(raw_data + i, ok_footer, ok_footer_len) == 0) {
ok_pos = i;
break;
}
}
if (ok_pos < 0) {
//EC200A_FTP_LOG("Error: \\r\\nOK footer not found\n");
return NULL;
}
// 4. 计算文件数据长度和起始地址
if (data_start >= (uint32_t)ok_pos) {
//EC200A_FTP_LOG("Error: Invalid data positions (start=%u, ok=%d)\n", data_start, ok_pos);
return NULL;
}
*file_len = ok_pos - data_start;
file_start = raw_data + data_start;
if (*file_len == 0) {
//EC200A_FTP_LOG("Error: No file data found\n");
return NULL;
}
//EC200A_FTP_LOG("Success: Found file data at offset %u, length %u bytes\n", data_start, *file_len);
return file_start;
}
/**
* ************************************************************************
* @brief - FTP 获取文件数据时 自定义发送解析内容
*
* @param[in] sendBuf - 发送数据指针
* @param[in] sendLen - 发送数据长度
* @param[in] rcvBuf - 接收的数据内容 外部传入,接收赋值后传出 方便外部直接使用
* @param[in] recvLen - 最大可支持数据内容,防止数组越界
* @param[in] resendCnt - 重发次数计数
* @param[in] delayTimeMs - 单次接收等待延时时间
*
* @return S16_T - 期望应答有效数据 -1 未接收到 》=0 成功接收到期望应答数据 并返回接收数据长度
*
* @author jiankalka
* @date 2025-11-24
* ************************************************************************
*/
S16_T s16_4g_Send_Recv_Ftp_Get(U8_T *sendBuf, U8_T sendLen,U8_T *rcvBuf,U16_T recvLen, U8_T resendCnt, U16_T delayTimeMs)
{
U8_T count = 0;
U16_T rcvLen = 0;
S16_T ackIndex = -1;
U8_T ackOK[] = "OK";
// com_write(COM_PORT_BS, sendBuf, sendLen, 0);
v_4g_send_data((U8_T)0xFF, sendBuf, sendLen);
memset(rcvBuf, 0, recvLen);
do
{
mSleep(delayTimeMs);
// rcvLen = com_read(COM_PORT_BS, rcvBuf, 100, 0);
if(rcvLen < recvLen) //已接收数据满了 不再接收新数据
rcvLen += u16_4g_recv_data(&rcvBuf[rcvLen], recvLen-rcvLen);
if (rcvLen > 0)
{
//EC200A_FTP_LOG("recvLen = %d\r\n",rcvLen);
ackIndex = s16_FindAck_Ex(rcvBuf,rcvLen, ackOK ,sizeof(ackOK)-1);
if ((ackIndex != -1)) {
return rcvLen;
}
}
count++;
} while (count < resendCnt); // 如果没收到期望应答,重新发送
//EC200A_FTP_LOG("sendBuf = %s is recv err [%d]\r\n",sendBuf,ackIndex);
return -1;
}
#endif
#if 1 //逻辑控制
/**
* ************************************************************************
* @brief - 升级头部信息控制
*
* @param[in] u8_flag - 0升级前 1升级后
*
*
* @author jiankalka
* @date 2025-12-01
* ************************************************************************
*/
static void v_upgrade_header_data(U8_T u8_flag)
{
upgrade_header_t header_data;
Comm_Time time;
GetCurrentTime(&time);
memset(&header_data , 0, sizeof(upgrade_header_t));
header_data.file_size = t_ftp_update_info.u32_file_size; // 升级文件总大小
header_data.packet_count = (t_ftp_update_info.u32_file_size + (uint32_t)PACKET_SIZE - 1U) /
(uint32_t)PACKET_SIZE; // 总包数(按 Flash 1KB 包计,与 QFREAD 块大小无关)
header_data.crc32 = 0; // 文件CRC32校验和
header_data.timestamp = xDate2Seconds(&time); // 时间戳
//uint8_t version[16]; // 版本号
//uint8_t reserved[32]; // 保留字段
if(u8_flag == 0)
{
/* 升级开始前:先清空升级区域所有扇区,写入时就不用再擦除 */
if (upgrade_erase_program_area() != 0)
{
EC200A_FTP_LOG("upgrade_erase_program_area failed\r\n");
}
header_data.upgrade_flag = UPGRADE_FLAG_IDLE; // 升级标志 无数据状态
}
else
{
header_data.upgrade_flag = UPGRADE_FLAG_NEED_UP; // 升级标志 存在需要升级数据
}
EC200A_FTP_LOG("upgrade_header flag(%d) fileSize[%d]",header_data.upgrade_flag,t_ftp_update_info.u32_file_size);
upgrade_success_update_header(&header_data);
}
/**
* ************************************************************************
* @brief - 发送一次AT并等待指定ACK(不重发)
*
* @param[in] sendBuf - 发送数据指针
* @param[in] sendLen - 发送数据长度
* @param[in/out] rcvBuf - 接收缓存
* @param[in] recvLen - 接收缓存长度
* @param[in] ack - 期望ACK字符串
* @param[in] ackLen - ACK长度
* @param[in] pollDelayMs - 轮询间隔
* @param[in] pollCount - 轮询次数
*
* @return S16_T - ack在rcvBuf中的索引,失败返回-1
* ************************************************************************
*/
static S16_T s16_4g_Send_Once_WaitAck(U8_T *sendBuf, U16_T sendLen,
U8_T *rcvBuf, U16_T recvLen,
const U8_T *ack, U16_T ackLen,
U16_T pollDelayMs, U16_T pollCount)
{
U16_T rcvLen = 0;
S16_T ackIndex = -1;
if (sendBuf == NULL || rcvBuf == NULL || ack == NULL || recvLen == 0 || ackLen == 0)
return -1;
v_4g_send_data((U8_T)0xFF, sendBuf, sendLen);
memset(rcvBuf, 0, recvLen);
for (U16_T i = 0; i < pollCount; i++)
{
mSleep(pollDelayMs);
if (rcvLen < recvLen)
rcvLen += u16_4g_recv_data(&rcvBuf[rcvLen], recvLen - rcvLen);
if (rcvLen > 0)
{
ackIndex = s16_FindAck_Ex(rcvBuf, rcvLen, (U8_T *)ack, ackLen);
if (ackIndex != -1)
return ackIndex;
}
}
return -1;
}
/**
* ************************************************************************
* @brief - 发送一次AT并等待ACK1或ACK2(不重发)
*
* @return >0 命中ack1; <0 命中ack2; 0/其它 失败
* ************************************************************************
*/
static S16_T s16_4g_Send_Once_WaitAck2(U8_T *sendBuf, U16_T sendLen,
U8_T *rcvBuf, U16_T recvLen,
const U8_T *ack1, U16_T ack1Len,
const U8_T *ack2, U16_T ack2Len,
U16_T pollDelayMs, U16_T pollCount)
{
U16_T rcvLen = 0;
S16_T idx1 = -1;
S16_T idx2 = -1;
if (sendBuf == NULL || rcvBuf == NULL || recvLen == 0)
return 0;
if (ack1 == NULL || ack1Len == 0 || ack2 == NULL || ack2Len == 0)
return 0;
v_4g_send_data((U8_T)0xFF, sendBuf, (U16_T)sendLen);
memset(rcvBuf, 0, recvLen);
for (U16_T i = 0; i < pollCount; i++)
{
mSleep(pollDelayMs);
if (rcvLen < recvLen)
rcvLen += u16_4g_recv_data(&rcvBuf[rcvLen], recvLen - rcvLen);
if (rcvLen > 0)
{
idx1 = s16_FindAck_Ex(rcvBuf, rcvLen, (U8_T *)ack1, ack1Len);
if (idx1 != -1)
return idx1; // >0: ack1
idx2 = s16_FindAck_Ex(rcvBuf, rcvLen, (U8_T *)ack2, ack2Len);
if (idx2 != -1)
return (S16_T)(-idx2); // <0: ack2
}
}
return 0;
}
#endif
#if 1 //AT指令
/**
* ************************************************************************
* @brief - 配置 FTP(S) 使用的 PDP 上下文(恒为 EC200A_FTP_PDP_CTX_ID,与业务 TCP 分场景,见 ec200a_4g_cfg.h
* ************************************************************************
*/
static S8_T s8_ec200a_qftpcfg_contextid(void)
{
U8_T u8_sendBuf[40];
unsigned ctx = (unsigned)EC200A_FTP_PDP_CTX_ID;
int n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QFTPCFG=\"contextid\",%u\r\n", ctx);
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
return -1;
}
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 3, 50);
}
/**
* ************************************************************************
* @brief - 设置用户名和密码。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpcfg_account()
{
U8_T u8_sendBuf[64] = {0};
//AT+QFTPCFG="account","test","test" //设置用户名和密码。
snprintf((char *)u8_sendBuf,64,"AT+QFTPCFG=\"account\",\"%s\",\"%s\"\r\n",
t_ftp_update_info.u8_user , t_ftp_update_info.u8_pwd);
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/**
* ************************************************************************
* @brief - 设置文件类型为二进制 0 二进制 1 ASCII
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpcfg_filetype()
{
U8_T u8_sendBuf[] = "AT+QFTPCFG=\"filetype\",0\r\n";
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/**
* ************************************************************************
* @brief - 设置为被动传输方式。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpcfg_transmode()
{
U8_T u8_sendBuf[] = "AT+QFTPCFG=\"transmode\",1\r\n";
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 20, 1000);
}
/**
* ************************************************************************
* @brief - 设置最大响应时间(默认为90秒)。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpcfg_rsptimeout()
{
U8_T u8_sendBuf[] = "AT+QFTPCFG=\"rsptimeout\",90\r\n";
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/**
* ************************************************************************
* @brief - 登录FTP服务器。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpopen()
{
U8_T u8_sendBuf[64] = {0};
//AT+QFTPOPEN="192.0.2.0",8309 登录FTP服务器
snprintf((char *)u8_sendBuf,64,"AT+QFTPOPEN=\"%s\",%d\r\n",
t_ftp_update_info.u8_update_server_addr , t_ftp_update_info.u16_update_server_port);
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/**
* ************************************************************************
* @brief - 设置目录
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpcwd()
{
const char *p = (const char *)t_ftp_update_info.u8_path;
U8_T u8_sendBuf[160] = {0};
S8_T r;
size_t pl;
int n;
if (p == NULL || p[0] == '\0') {
EC200A_FTP_LOG("[FTP-UPG] QFTPCWD skip (empty path)\r\n");
return 1;
}
pl = strlen(p);
while (pl > 0U && p[pl - 1U] == '/') {
pl--;
}
if (pl == 0U) {
return 1;
}
n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QFTPCWD=\"%.*s\"\r\n", (int)pl, p);
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
EC200A_FTP_LOG("[FTP-UPG] QFTPCWD cmd too long, skip (use full remote path)\r\n");
return 1;
}
r = s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 5, 100);
if (r >= 0) {
return r;
}
if (p[0] == '/') {
n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QFTPCWD=\"%.*s\"\r\n", (int)(pl - 1U), p + 1);
if (n > 0 && n < (int)sizeof(u8_sendBuf)) {
r = s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 5, 100);
if (r >= 0) {
return r;
}
}
}
EC200A_FTP_LOG("[FTP-UPG] QFTPCWD fail, continue (QFTPSIZE/QFTPGET use full remote path) ret=%d\r\n", (int)r);
return 1;
}
/**
* ************************************************************************
* @brief - 获取当前目录的内容,并通过 COM 口输出数据。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftplist()
{
U8_T u8_sendBuf[] = "AT+QFTPLIST=\".\"\r\n";
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/** 远端 path + file,供 QFTPSIZE / QFTPGET(COM)(减少对 QFTPCWD 的依赖) */
static U8_T s_ftp_remote_get_obj[200];
static int s32_ftp_fill_remote_get_object(void)
{
const char *p = (const char *)t_ftp_update_info.u8_path;
const char *f = (const char *)t_ftp_update_info.u8_file_name;
size_t pl;
int n;
if (f[0] == '\0') {
return -1;
}
pl = (p != NULL) ? strlen(p) : 0U;
while (pl > 0U && p[pl - 1U] == '/') {
pl--;
}
if (pl == 0U) {
n = snprintf((char *)s_ftp_remote_get_obj, sizeof(s_ftp_remote_get_obj), "%s", f);
} else {
n = snprintf((char *)s_ftp_remote_get_obj, sizeof(s_ftp_remote_get_obj), "%.*s/%s", (int)pl, p, f);
}
if (n <= 0 || (size_t)n >= sizeof(s_ftp_remote_get_obj)) {
EC200A_FTP_LOG("[FTP-UPG] remote object path too long\r\n");
return -1;
}
return 0;
}
/**
* ************************************************************************
* @brief - 获取文件大小 并计算包数量
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpsize()
{
U8_T u8_sendBuf[256] = {0};
int n, k;
enum { QFTPSIZE_ACCUM_LEN = 640 };
U8_T rcvBuf[QFTPSIZE_ACCUM_LEN];
int cmd_len;
U16_T rcv_len = 0U;
U16_T total_waits = 0U;
/* +QFTPSIZE 常为异步 URC,可与业务 TCP 的 QIRD 应答交错;勿用 s8_4g_Send_Recv_Cmd 仅 2×50ms 等首包 */
const U16_T max_waits = (U16_T)((EC200A_FTP_QFREAD_TOTAL_WAITS) * 10U);
if (s32_ftp_fill_remote_get_object() != 0) {
return -1;
}
cmd_len = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
"AT+QFTPSIZE=\"%s\"\r\n", (char *)s_ftp_remote_get_obj);
if (cmd_len <= 0 || cmd_len >= (int)sizeof(u8_sendBuf))
{
EC200A_FTP_LOG("[FTP-UPG] QFTPSIZE cmd too long (obj=%s,len=%d)\r\n",
(char *)s_ftp_remote_get_obj, cmd_len);
return -1;
}
memset(rcvBuf, 0, sizeof(rcvBuf));
v_4g_send_data((U8_T)0xFF, u8_sendBuf, (U16_T)cmd_len);
while (total_waits < max_waits) {
mSleep(EC200A_FTP_UART_SLICE_WAIT_MS);
if (rcv_len + 1U < (U16_T)sizeof(rcvBuf)) {
rcv_len += u16_4g_recv_data(
rcvBuf + rcv_len,
(U16_T)((U16_T)sizeof(rcvBuf) - 1U - rcv_len));
} else {
EC200A_FTP_LOG("[FTP-UPG] QFTPSIZE accum overflow\r\n");
return -1;
}
total_waits++;
if (rcv_len == 0U) {
continue;
}
if (strstr((char *)rcvBuf, "+CME ERROR") != NULL
|| strstr((char *)rcvBuf, "\r\nERROR\r\n") != NULL) {
EC200A_FTP_LOG("[FTP-UPG] QFTPSIZE modem ERROR in rsp\r\n");
return -1;
}
{
char *p = strstr((char *)rcvBuf, "+QFTPSIZE:");
if (p != NULL && sscanf(p, "+QFTPSIZE: %d,%d", &n, &k) == 2) {
if (k <= 0) {
EC200A_FTP_LOG("[FTP-UPG] QFTPSIZE invalid size=%d\r\n", k);
return -1;
}
t_ftp_update_info.u32_file_size = (uint32_t)k;
EC200A_FTP_LOG("[FTP-UPG] QFTPSIZE obj=%s size=%lu (waits=%u)\r\n",
(char *)s_ftp_remote_get_obj, (unsigned long)t_ftp_update_info.u32_file_size,
(unsigned)total_waits);
return 0;
}
}
}
EC200A_FTP_LOG("[FTP-UPG] QFTPSIZE timeout no +QFTPSIZE (obj=%s, rcv_len=%u)\r\n",
(char *)s_ftp_remote_get_obj, (unsigned)rcv_len);
return -1;
}
/**
* ************************************************************************
* @brief - //删除 UFS 中所有文件(不删除目录)。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qfdel()
{
U8_T u8_sendBuf[] = "AT+QFDEL=\"*\"\r\n"; ////删除 UFS 中所有文件(不删除目录)。
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/** 调试:打印本包负载前 10 / 后 10 字节(与 PC 上 bin 对拍);len≤10 只打一行 */
static void v_qftpget_log_pkt_data_edges(const U8_T *p, uint32_t len, uint32_t pkt_idx, int byte_off)
{
char hbuf[56];
char tbuf[56];
uint32_t hlen;
uint32_t tstart;
uint32_t ti;
int pos;
if (p == NULL || len == 0U) {
return;
}
hlen = (len < 10U) ? len : 10U;
pos = 0;
for (ti = 0U; ti < hlen && pos < (int)sizeof(hbuf) - 4; ti++) {
pos += snprintf(hbuf + (size_t)pos, sizeof(hbuf) - (size_t)pos, "%02X ", (unsigned)p[ti]);
}
hbuf[sizeof(hbuf) - 1U] = '\0';
if (len <= 10U) {
EC200A_FTP_LOG("[FTP-UPG] pkt%u off=%d len=%lu hex: %s\r\n",
(unsigned)pkt_idx, byte_off, (unsigned long)len, hbuf);
return;
}
tstart = len - 10U;
pos = 0;
for (ti = tstart; ti < len && pos < (int)sizeof(tbuf) - 4; ti++) {
pos += snprintf(tbuf + (size_t)pos, sizeof(tbuf) - (size_t)pos, "%02X ", (unsigned)p[ti]);
}
tbuf[sizeof(tbuf) - 1U] = '\0';
EC200A_FTP_LOG("[FTP-UPG] pkt%u off=%d len=%lu head10: %s tail10: %s\r\n",
(unsigned)pkt_idx, byte_off, (unsigned long)len, hbuf, tbuf);
}
/**
* ************************************************************************
* @brief - 周期获取文件内容 并存储
*
*
* @return S8_T - -1 获取失败 0获取中 1获取完成
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpget()
{
U8_T u8_sendBuf[224] = {0};
S16_T s16_ret;
U32_T file_len = 0U;
const U8_T *file_data = NULL;
uint32_t done;
uint32_t f_sz;
uint32_t rem;
U16_T read_size;
int packAddr;
int sn;
#if EC200A_TCP_USE_TRANSPARENT_MODE
U8_T rcvBuf[FTP_REAC_DATA_BUF_LEN] = {0};
#else
U8_T *qfget_rsp = NULL;
#endif
/* 分片经 COM 拉取并写 Flash(与 QFREAD 分包思路一致),不经 UFS 整文件 */
if (s32_ftp_fill_remote_get_object() != 0) {
return -1;
}
f_sz = t_ftp_update_info.u32_file_size;
done = (uint32_t)t_ftp_update_info.u16_packIndex * (uint32_t)t_ftp_update_info.u16_packSize;
rem = (f_sz > done) ? (f_sz - done) : 0U;
read_size = t_ftp_update_info.u16_packSize;
if (rem == 0U) {
return 1;
}
if (rem < (uint32_t)read_size) {
read_size = (U16_T)rem;
}
packAddr = (int)((uint32_t)t_ftp_update_info.u16_packIndex * (uint32_t)t_ftp_update_info.u16_packSize);
if (t_ftp_update_info.u16_packIndex == 0) {
v_upgrade_header_data(0);
}
sn = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
"AT+QFTPGET=\"%s\",\"COM:\",%d,%u\r\n",
(char *)s_ftp_remote_get_obj, packAddr, (unsigned)read_size);
if (sn <= 0 || sn >= (int)sizeof(u8_sendBuf)) {
EC200A_FTP_LOG("[FTP-UPG] QFTPGET(COM) cmd too long\r\n");
return -1;
}
#if EC200A_TCP_USE_TRANSPARENT_MODE
s16_ret = s16_4g_Send_Recv_Ftp_Get(u8_sendBuf, (U16_T)sn, rcvBuf, (U16_T)FTP_REAC_DATA_BUF_LEN, 12, 200);
#else
{
uint32_t com_pay_off = 0U;
s16_ret = s16_qftpget_com_at_slice(u8_sendBuf, (U16_T)sn, &qfget_rsp, read_size, &com_pay_off);
if (s16_ret >= 0 && qfget_rsp != NULL) {
uint32_t need = com_pay_off + (uint32_t)read_size + 4U;
if ((uint32_t)s16_ret < need) {
EC200A_FTP_LOG("[FTP-UPG] QFTPGET(COM) frame len err off=%lu need=%lu got=%d\r\n",
(unsigned long)com_pay_off, (unsigned long)need, (int)s16_ret);
vPortFree(qfget_rsp);
qfget_rsp = NULL;
s16_ret = -1;
} else {
file_data = qfget_rsp + com_pay_off;
file_len = (U32_T)read_size;
}
}
}
#endif
EC200A_FTP_LOG("[FTP-UPG] QFTPGET(COM) pkt %u/%u off=%d len=%u ret=%d\r\n",
(unsigned)t_ftp_update_info.u16_packIndex, (unsigned)t_ftp_update_info.u16_packNum,
packAddr, (unsigned)read_size, (int)s16_ret);
if (s16_ret < 0) {
#if !EC200A_TCP_USE_TRANSPARENT_MODE
if (qfget_rsp != NULL) {
vPortFree(qfget_rsp);
}
#endif
return -1;
}
#if EC200A_TCP_USE_TRANSPARENT_MODE
file_data = parse_4g_ftp_response(rcvBuf, (uint32_t)s16_ret, &file_len);
#else
/* 非透传:已由 s16_qftpget_com_at_slice 定界,不再 parse(避免尾缀混入 QIRD 时误用「最后一个 \\r\\nOK」) */
#endif
if (file_data == NULL) {
#if !EC200A_TCP_USE_TRANSPARENT_MODE
if (qfget_rsp != NULL) {
vPortFree(qfget_rsp);
}
#endif
EC200A_FTP_LOG("[FTP-UPG] QFTPGET(COM) parse fail\r\n");
#if BS_OCPP_EN
v_ocpp_set_fw_notify_pending((U8_T)OCPP_FW_NOTIFY_PENDING_DOWNLOAD_FAILED);
#endif
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0U;
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0U;
v_ftp_ctrl_init();
return -1;
}
v_qftpget_log_pkt_data_edges(file_data, (uint32_t)file_len,
(uint32_t)t_ftp_update_info.u16_packIndex, packAddr);
{
uint32_t flash_base = (uint32_t)t_ftp_update_info.u16_packIndex *
((uint32_t)t_ftp_update_info.u16_packSize / (uint32_t)PACKET_SIZE);
uint32_t o = 0U;
while (o < file_len) {
uint32_t sub = file_len - o;
if (sub > (uint32_t)PACKET_SIZE) {
sub = (uint32_t)PACKET_SIZE;
}
{
int flash_write = upgrade_write_packet(
flash_base + (uint32_t)(o / (uint32_t)PACKET_SIZE),
file_data + o, sub);
if (flash_write != 0) {
EC200A_FTP_LOG("[FTP-UPG] flash wr fail sub_off=%lu ret=%d\r\n",
(unsigned long)o, flash_write);
#if BS_OCPP_EN
v_ocpp_set_fw_notify_pending((U8_T)OCPP_FW_NOTIFY_PENDING_INSTALLATION_FAILED);
#endif
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0U;
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0U;
v_ftp_ctrl_init();
#if !EC200A_TCP_USE_TRANSPARENT_MODE
if (qfget_rsp != NULL) {
vPortFree(qfget_rsp);
}
#endif
return -1;
}
}
o += sub;
}
}
#if !EC200A_TCP_USE_TRANSPARENT_MODE
if (qfget_rsp != NULL) {
vPortFree(qfget_rsp);
}
#endif
t_ftp_update_info.u16_packIndex++;
if (t_ftp_update_info.u16_packIndex >= t_ftp_update_info.u16_packNum) {
return 1;
}
return 0;
}
/**
* ************************************************************************
* @brief - 列出 UFS 中所有文件。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-12-04
* ************************************************************************
*/
static S8_T s8_ec200a_qflst()
{
U8_T u8_sendBuf[] = "AT+QFLST=\"*\"\r\n"; //列出 UFS 中所有文件。
S8_T s8_ret;
int size;
char fileName[NUM_64] = {0};
U8_T u8_ack[] = "+QFLST:";
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
s8_ret = s8_4g_Send_Recv_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1,rcvBuf,BS_RECV_BUF_LEN, u8_ack, 1, 1000*6);
if(s8_ret >= 0)
{
/*
+QFLST: "UFS:CCU601E.bin",584072
OK
*/
if (sscanf((char *)&rcvBuf[s8_ret], "+QFLST: \"UFS:%[^\"]\",%d", fileName, &size) == 2) {
if(/*size == t_ftp_update_info.u32_file_size &&*/
strstr((char *)t_ftp_update_info.u8_file_name,fileName))
{
EC200A_FTP_LOG("FTP file ok(%s)[%d], start\r\n",fileName,size);
t_ftp_update_info.u16_packIndex = 0; //清空当前索引号
t_ftp_update_info.u32_file_size = (uint32_t)size; // UFS 文件大小
{
uint32_t chunk = (uint32_t)EC200A_FTP_UFS_RW_CHUNK_BYTES;
uint32_t fsz = (uint32_t)size;
t_ftp_update_info.u16_packSize = (U16_T)chunk;
t_ftp_update_info.u16_packNum = (U16_T)((fsz + chunk - 1U) / chunk);
}
EC200A_FTP_LOG("%s file size= (%d) ,packSize= (%u) ,packNum= (%d) \r\n",
t_ftp_update_info.u8_file_name, size,
(unsigned)EC200A_FTP_UFS_RW_CHUNK_BYTES, t_ftp_update_info.u16_packNum);
if(t_ftp_update_info.u16_packNum == 0) //异常处理
s8_ret = -1;
}
else
{
EC200A_FTP_LOG("文件获取失败(%s)[%d] ,(%s)[%d] \r\n",fileName,size,
t_ftp_update_info.u8_file_name,t_ftp_update_info.u32_file_size);
s8_ret = -1;
}
}
else
s8_ret = -1;
}
return s8_ret;
}
/**
* ************************************************************************
* @brief - 打开文件获取文件指针
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-12-04
* ************************************************************************
*/
static S8_T s8_ec200a_qfopen()
{
U8_T u8_sendBuf[64] = {0};
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
S8_T s8_ret;
U8_T u8_ack[] = "+QFOPEN:";
//AT+QFOPEN="test.txt",2 //打开文件获取文件指针。 2:若文件存在,则作为只读文件直接打开;若文件不存在,则响应错误
snprintf((char *)u8_sendBuf,64,"AT+QFOPEN=\"%s\",2\r\n",
t_ftp_update_info.u8_file_name);
s8_ret = s8_4g_Send_Recv_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1,rcvBuf,BS_RECV_BUF_LEN, u8_ack, 5, 200);
if(s8_ret >= 0)
{
if (sscanf((char *)&rcvBuf[s8_ret], "+QFOPEN: %d", &t_ftp_update_info.u32_file_fd) == 1) {
EC200A_FTP_LOG("FTP open ok[%d], start\r\n",t_ftp_update_info.u32_file_fd);
v_upgrade_header_data(0); //升级前更新头部信息
}
else
s8_ret = -1;
}
return s8_ret;
}
/**
* ************************************************************************
* @brief -设置文件指针为文件的初始位置。
*
*
* @return S8_T -
*
* @author jiankalka
* @date 2025-12-04
* ************************************************************************
*/
static S8_T s8_ec200a_qfseek()
{
U8_T u8_sendBuf[64] = {0};
//AT+QFSEEK=1,0,0 //设置文件指针为文件的初始位置。
snprintf((char *)u8_sendBuf,64,"AT+QFSEEK=%d,0,0\r\n",
t_ftp_update_info.u32_file_fd);
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 100);
}
/**
* ************************************************************************
* @brief - 读取文件数据
*
*
* @return S8_T - 0 尚有分包待读 1 已全部读完 -1 失败
*
* @author jiankalka
* @date 2025-12-04
* ************************************************************************
*/
static S8_T s8_ec200a_qfread()
{
U8_T u8_sendBuf[64] = {0};
S16_T s16_ret;
U32_T file_len; // 文件数据长度
const U8_T *file_data = NULL; // 文件数据起始地址
int sn;
#if EC200A_TCP_USE_TRANSPARENT_MODE
U8_T rcvBuf[FTP_REAC_DATA_BUF_LEN] = {0};
#else
U8_T *qfread_rsp = NULL;
#endif
int packAddr = t_ftp_update_info.u16_packIndex*t_ftp_update_info.u16_packSize; //获取包地址
uint32_t done = (uint32_t)t_ftp_update_info.u16_packIndex * (uint32_t)t_ftp_update_info.u16_packSize;
uint32_t f_sz = t_ftp_update_info.u32_file_size;
uint32_t rem = (f_sz > done) ? (f_sz - done) : 0U;
U16_T read_size = t_ftp_update_info.u16_packSize;
if (rem == 0U) {
return 1;
}
if (rem < (uint32_t)read_size) {
read_size = (U16_T)rem;
}
snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QFREAD=%d,%d\r\n",
t_ftp_update_info.u32_file_fd ,read_size);
sn = (int)strlen((char *)u8_sendBuf);
if (sn <= 0 || sn >= (int)sizeof(u8_sendBuf)) {
return -1;
}
#if EC200A_TCP_USE_TRANSPARENT_MODE
s16_ret = s16_4g_Send_Recv_Ftp_Get(u8_sendBuf, (U16_T)sn, rcvBuf, FTP_REAC_DATA_BUF_LEN, 6, 200);
#else
s16_ret = s16_qfread_at_slice(u8_sendBuf, (U16_T)sn, &qfread_rsp);
#endif
EC200A_FTP_LOG("RX pkt(%d)/(%d), prog %02d%% [%d](%d)\r\n",t_ftp_update_info.u16_packIndex,t_ftp_update_info.u16_packNum,
(packAddr*100/t_ftp_update_info.u32_file_size),packAddr,t_ftp_update_info.u32_file_size);
if(s16_ret >= 0)
{
#if EC200A_TCP_USE_TRANSPARENT_MODE
file_data = parse_4g_ftp_response(rcvBuf, (uint32_t)s16_ret, &file_len);
#else
file_data = parse_4g_ftp_response(qfread_rsp, (uint32_t)s16_ret, &file_len);
#endif
if (file_data != NULL) {
EC200A_FTP_LOG("File data ok, len=%uB\r\n", file_len);
{
uint32_t flash_base = (uint32_t)t_ftp_update_info.u16_packIndex *
((uint32_t)t_ftp_update_info.u16_packSize / (uint32_t)PACKET_SIZE);
uint32_t o = 0U;
int flash_write = 0;
while (o < file_len) {
uint32_t sub = file_len - o;
if (sub > (uint32_t)PACKET_SIZE) {
sub = (uint32_t)PACKET_SIZE;
}
flash_write = upgrade_write_packet(
flash_base + (uint32_t)(o / (uint32_t)PACKET_SIZE),
file_data + o, sub);
if (flash_write != 0) {
EC200A_FTP_LOG("flash wr fail sub_off=%lu ret=%d\r\n", (unsigned long)o, flash_write);
#if !EC200A_TCP_USE_TRANSPARENT_MODE
if (qfread_rsp != NULL) {
vPortFree(qfread_rsp);
qfread_rsp = NULL;
}
#endif
return -1;
}
o += sub;
}
EC200A_FTP_LOG("flash wr:%d (-1F 0OK) data[%02x %02x %02x %02x %02x %02x %02x %02x]\r\n", flash_write ,
file_data[0],file_data[1],file_data[2],file_data[3],
file_data[4],file_data[5],file_data[6],file_data[7]);
}
t_ftp_update_info.u16_packIndex ++;
} else {
EC200A_FTP_LOG("FTP rsp parse fail\r\n");
}
//退出条件判断
if(t_ftp_update_info.u16_packIndex >= t_ftp_update_info.u16_packNum) // 接收包足够 则退出
{
s16_ret = 1;
}
else
s16_ret = 0;
}
#if !EC200A_TCP_USE_TRANSPARENT_MODE
if (qfread_rsp != NULL) {
vPortFree(qfread_rsp);
}
#endif
return (S8_T)s16_ret;
}
static S8_T s8_get_log_info()
{
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 1);
/* v_app_fatfs_log_real_file_name -- CCU601E_D, disabled */
EC200A_FTP_LOG("LOG file = %s\r\n",
t_ftp_update_info.u8_file_name);
/* 检查文件是否存在 */
bool file_exists = false;
FS_Status fs_status = fs_file_exists((char *)t_ftp_update_info.u8_file_name, &file_exists);
if (fs_status != FS_STATUS_OK) {
EC200A_FTP_LOG("[FileCtrl] Error checking file existence: %s, status=%d\r\n",
t_ftp_update_info.u8_file_name, fs_status);
/* 恢复文件写入禁用标志(清除TCP调试功能标志位) */
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
if (!file_exists) {
EC200A_FTP_LOG("[FileCtrl] File not found: %s\r\n", t_ftp_update_info.u8_file_name);
/* 恢复文件写入禁用标志(清除TCP调试功能标志位) */
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
/* 获取文件大小 */
uint32_t file_size = 0;
fs_status = fs_get_file_size((char *)t_ftp_update_info.u8_file_name, &file_size);
if (fs_status != FS_STATUS_OK) {
EC200A_FTP_LOG("[FileCtrl] Failed to get file size: %s, status=%d\r\n",
t_ftp_update_info.u8_file_name, fs_status);
/* 恢复文件写入禁用标志(清除TCP调试功能标志位) */
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
t_ftp_update_info.u32_file_size = file_size;
if (EC200A_FTP_UFS_RW_CHUNK_BYTES > 65535U) {
EC200A_FTP_LOG("[FileCtrl] EC200A_FTP_UFS_RW_CHUNK_BYTES too large for u16_packSize\r\n");
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
t_ftp_update_info.u16_packSize = (U16_T)EC200A_FTP_UFS_RW_CHUNK_BYTES;
/* 向上取整包数(与 u16_packSize 一致,便于 QFSEEK/QFWRITE 分包) */
t_ftp_update_info.u16_packNum = (U16_T)((file_size + (uint32_t)t_ftp_update_info.u16_packSize - 1U) /
(uint32_t)t_ftp_update_info.u16_packSize);
return 1;
}
#if !EC200A_TCP_USE_TRANSPARENT_MODE
/** 当前 FAT 块 QFWRITE 完成后是否为日志文件末尾 */
static uint8_t s_log_tx_chunk_is_last;
static S8_T s8_ec200a_write_log_data(void)
{
uint32_t chunk_size = t_ftp_update_info.u16_packSize;
uint32_t offset = t_ftp_update_info.u16_packIndex * chunk_size;
uint32_t file_size = t_ftp_update_info.u32_file_size;
uint32_t remaining = file_size - offset;
uint32_t read_size = (remaining < chunk_size) ? remaining : chunk_size;
uint8_t is_last_chunk = (offset + read_size >= file_size) ? 1 : 0;
uint8_t *read_buffer = NULL;
char *text_data = NULL;
uint32_t filtered_size = 0;
uint32_t dest_index = 0;
uint32_t i;
S8_T wr;
read_buffer = (uint8_t *)pvPortMalloc(read_size);
if (read_buffer == NULL) {
EC200A_FTP_LOG("[FileCtrl] Failed to allocate read buffer (%u bytes)\r\n", read_size);
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
{
FS_Status fs_status;
FS_ReadRange range = {
.start_pos = offset,
.end_pos = offset + read_size,
.actual_read = 0
};
fs_status = fs_read_file_content((char *)t_ftp_update_info.u8_file_name, (char *)read_buffer, read_size, &range);
if (fs_status != FS_STATUS_OK) {
EC200A_FTP_LOG("[FileCtrl] Failed to read file content: %s, status=%d (offset=%u, size=%u)\r\n",
(char *)t_ftp_update_info.u8_file_name, fs_status, offset, read_size);
vPortFree(read_buffer);
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
if (range.actual_read == 0) {
EC200A_FTP_LOG("[FileCtrl] Read failed: expected %u, got 0\r\n", read_size);
vPortFree(read_buffer);
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
read_size = range.actual_read;
is_last_chunk = (offset + read_size >= file_size - 20U) ? 1 : 0;
}
for (i = 0; i < read_size; i++) {
if (read_buffer[i] != '\0') {
filtered_size++;
}
}
text_data = (char *)pvPortMalloc(filtered_size + 1U);
if (text_data == NULL) {
EC200A_FTP_LOG("[FileCtrl] Failed to allocate text buffer (%u bytes)\r\n", filtered_size + 1U);
vPortFree(read_buffer);
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
dest_index = 0;
for (i = 0; i < read_size; i++) {
if (read_buffer[i] != '\0') {
text_data[dest_index++] = read_buffer[i];
}
}
text_data[filtered_size] = '\0';
EC200A_FTP_LOG(" Progress: %.1f%% (%u/%u bytes)\r\n",
(float)(offset + read_size) / (float)file_size * 100.0f,
offset + read_size, file_size);
vPortFree(read_buffer);
read_buffer = NULL;
if (filtered_size == 0U) {
vPortFree(text_data);
if (is_last_chunk != 0U) {
s8_ec200a_qfclose_log_fd();
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
EC200A_FTP_LOG("[FileCtrl] File write disabled flag (TCP debug) disabled after final chunk download operation\r\n");
return 1;
}
EC200A_FTP_LOG("[FileCtrl] File write disabled flag (TCP debug) remains enabled for next chunk download\r\n");
t_ftp_update_info.u16_packIndex++;
return 0;
}
if (s8_ec200a_qfseek_log_abs(s_log_ufs_qwrite_total) < 0) {
EC200A_FTP_LOG("[FTP-LOG] QFSEEK fail off=%lu\r\n", (unsigned long)s_log_ufs_qwrite_total);
s8_ec200a_qfclose_log_fd();
vPortFree(text_data);
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
s_log_tx_chunk_is_last = is_last_chunk;
wr = s8_ec200a_qfwrite_log_bin((const U8_T *)text_data, (U16_T)filtered_size);
vPortFree(text_data);
if (wr < 0) {
EC200A_FTP_LOG("[FTP-LOG] QFWRITE fail len=%u off=%lu\r\n",
(unsigned)filtered_size, (unsigned long)s_log_ufs_qwrite_total);
s8_ec200a_qfclose_log_fd();
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
return -1;
}
s_log_ufs_qwrite_total += filtered_size;
if (is_last_chunk != 0U) {
s8_ec200a_qfclose_log_fd();
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
EC200A_FTP_LOG("[FileCtrl] File write disabled flag (TCP debug) disabled after final chunk download operation\r\n");
return 1;
}
EC200A_FTP_LOG("[FileCtrl] File write disabled flag (TCP debug) remains enabled for next chunk download\r\n");
t_ftp_update_info.u16_packIndex++;
return 0;
}
#else
static S8_T s8_ec200a_write_log_data(void)
{
return -1;
}
#endif /* !EC200A_TCP_USE_TRANSPARENT_MODE */
/**
* ************************************************************************
* @brief - 上传文件至FTP服务器
*
*
* @return S8_T - -1 获取失败 0获取中 1获取完成
*
* @author jiankalka
* @date 2025-11-29
* ************************************************************************
*/
static S8_T s8_ec200a_qftpclose(void)
{
const U8_T u8_sendBuf[] = "AT+QFTPCLOSE\r\n";
return s8_4g_Send_AT_Cmd((U8_T *)(void *)u8_sendBuf, (U16_T)(sizeof(u8_sendBuf) - 1U), NULL, 5U, 400U);
}
static S8_T s8_ec200a_qftpput()
{
U8_T u8_sendBuf[128] = {0};
char *last_slash = strrchr((char *)t_ftp_update_info.u8_file_name, '/'); //带路径的 提取出文件名称
#if 1 //使用UFS接收文件
//AT+QFTPGET="test_my1.txt","UFS:test.txt" //下载文件并将其保存至 UFS,命名为 test.txt。
snprintf((char *)u8_sendBuf,sizeof(u8_sendBuf) - 1,"AT+QFTPPUT=\"%s\",\"UFS:%s\",0\r\n",
last_slash+1 ,last_slash+1);
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 10, 500);
#endif
}
#endif
U8_T u8_ec200a_4g_ftp_update()
{
S8_T s8_at_res;
static U8_T u8_out_cnt = 0;
static E_EC200A_4G_FTP_INIT_STEP s_last_step = (E_EC200A_4G_FTP_INIT_STEP)0xFF;
// static U16_T u16_cnt = 0;
// if(u16_cnt++ < 10)
// return 0;
// u16_cnt = 0;
if (s_last_step != e_ec200a_ftp_init_step) {
EC200A_FTP_LOG("[FTP-UPG] step=%d\r\n", (int)e_ec200a_ftp_init_step);
s_last_step = e_ec200a_ftp_init_step;
}
switch (e_ec200a_ftp_init_step)
{
case STEP_FTP_SWCHTO_CMD_MODE:
{
s8_at_res = s8_work_mode_cmd();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_SET_CGREG; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_SET_CGREG: //设置PS域 网络注册状态
{
s8_at_res = s8_ec200a_set_cgreg(); //
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_QUERY_CGREG; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_QUERY_CGREG: //查询PS域 网络注册状态
{
s8_at_res = s8_ec200a_cgreg(); //查询PS域 网络注册状态
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_SET_QIDEACT; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_SET_QIDEACT: //去激活 FTP 专用 PDP(不碰业务 TCP 场景)
{
s8_at_res = s8_ec200a_qideact_ctx((U8_T)EC200A_FTP_PDP_CTX_ID);
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_SET_QIACT; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_SET_QIACT: //配置并激活 FTP 专用 PDP
{
s8_at_res = s8_ec200a_qicsgp_ctx((U8_T)EC200A_FTP_PDP_CTX_ID);
if (s8_at_res >= 0) {
s8_at_res = s8_ec200a_qiact_ctx((U8_T)EC200A_FTP_PDP_CTX_ID);
}
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_contextid; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_contextid: //配置 FTP(S) PDP 上下文 ID
{
s8_at_res = s8_ec200a_qftpcfg_contextid();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_account; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_account: //配置 FTP(S) 设置用户名和密码。
{
s8_at_res = s8_ec200a_qftpcfg_account();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d (user=%s)\r\n", (int)e_ec200a_ftp_init_step, s8_at_res, t_ftp_update_info.u8_user);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_filetype; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_filetype: //配置 FTP(S) 设置文件类型为ASCII。
{
s8_at_res = s8_ec200a_qftpcfg_filetype();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_transmode; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_transmode: //配置 FTP(S) 设置为被动传输方式。
{
s8_at_res = s8_ec200a_qftpcfg_transmode();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_rsptimeout; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_rsptimeout: //配置 FTP(S) 设置最大响应时间(默认为90秒
{
s8_at_res = s8_ec200a_qftpcfg_rsptimeout();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_QFTPOPEN; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_QFTPOPEN: //登录FTP服务器。
{
s8_at_res = s8_ec200a_qftpopen();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d (host=%s:%d)\r\n", (int)e_ec200a_ftp_init_step, s8_at_res,
t_ftp_update_info.u8_update_server_addr, t_ftp_update_info.u16_update_server_port);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_QFTPCWD; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_QFTPCWD: //切换目录
{
s8_at_res = s8_ec200a_qftpcwd();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d (path=%s)\r\n", (int)e_ec200a_ftp_init_step, s8_at_res, t_ftp_update_info.u8_path);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFTPSIZE; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_QFTPLIST: //获取当前目录的内容 -- 不使用
{
s8_at_res = s8_ec200a_qftplist();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_SAVE; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFTPSIZE: //获取 文件名称
{
s8_at_res = s8_ec200a_qftpsize();
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d (file=%s size=%lu)\r\n", (int)e_ec200a_ftp_init_step, s8_at_res,
t_ftp_update_info.u8_file_name, (unsigned long)t_ftp_update_info.u32_file_size);
if(s8_at_res >= 0){
{
uint32_t chunk = (uint32_t)EC200A_FTP_UFS_RW_CHUNK_BYTES;
uint32_t fsz = t_ftp_update_info.u32_file_size;
t_ftp_update_info.u16_packSize = (U16_T)chunk;
t_ftp_update_info.u16_packNum = (U16_T)((fsz + chunk - 1U) / chunk);
t_ftp_update_info.u16_packIndex = 0;
if (t_ftp_update_info.u16_packNum == 0U) {
EC200A_FTP_LOG("[FTP-UPG] packNum=0, abort\r\n");
break;
}
EC200A_FTP_LOG("[FTP-UPG] COM chunk packSize=%u packNum=%u\r\n",
(unsigned)t_ftp_update_info.u16_packSize,
(unsigned)t_ftp_update_info.u16_packNum);
}
e_ec200a_ftp_init_step = STEP_FTP_FTP_QFDEL; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_QFDEL: //获取 删除存储区保存的文件内容
{
s8_at_res = s8_ec200a_qfdel();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFTPGET; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFTPGET: //获取 文件内容
{
s8_at_res = s8_ec200a_qftpget(); //返回值 等于1时 成功
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
// 约定:-1 失败;0 正在处理/需要重试;1 完成成功
/* COM 分片已直接写入 Flash,跳过 QFLST/QFOPEN/QFSEEK/QFREAD(UFS) */
if(s8_at_res == 1){
#if BS_OCPP_EN
v_ocpp_set_fw_notify_pending((U8_T)OCPP_FW_NOTIFY_PENDING_DOWNLOADED);
#endif
e_ec200a_ftp_init_step = STEP_FTP_SAVE; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFLST: //获取 UFS中文件内容 qflst
{
s8_at_res = s8_ec200a_qflst(); //返回值 等于1时 成功
EC200A_FTP_LOG("[FTP-UPG] step=%d ret=%d\r\n", (int)e_ec200a_ftp_init_step, s8_at_res);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFOPEN; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFOPEN: //打开文件
{
s8_at_res = s8_ec200a_qfopen(); //返回值 等于1时 成功
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFSEEK; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFSEEK: //设置读取文件指针头地址
{
s8_at_res = s8_ec200a_qfseek(); //返回值 等于1时 成功
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFREAD; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFREAD: //读取文件
{
s8_at_res = s8_ec200a_qfread(); //返回值 等于1时 成功
if (s8_at_res == 1 || s8_at_res == 0)
{
u8_out_cnt = 0; //超时计数清空
if(s8_at_res == 1){
e_ec200a_ftp_init_step = STEP_FTP_SAVE; //状态跳转
}
}
break;
}
case STEP_FTP_SAVE: //保存当前设置并重启
case STEP_FTP_INIT_OK: //设置完成
{
#if BS_OCPP_EN
if (s_u8_fw_save_ocpp_arm == 0U) {
/* 仅置 pending;实际上送在 Plat_Comm 联网半周期的 ocpp_send_mag_ctrl */
v_ocpp_set_fw_notify_pending((U8_T)OCPP_FW_NOTIFY_PENDING_INSTALLING);
s_u8_fw_save_ocpp_arm = 1U;
break;
}
if (u8_ocpp_fw_installing_fsn_sent() == 0U) {
/* 须等 FSN(Installing) ws_send 成功;勿在未上送时写头/复位 */
break;
}
#endif
v_upgrade_header_data(1); //升级后更新头部信息
#if BS_OCPP_EN
v_ocpp_fw_eeprom_set_installed_after_boot();
s_u8_fw_save_ocpp_arm = 0U;
v_ocpp_clr_fw_installing_fsn_sent();
v_sys_reboot(); /* Installing 已上送后再复位;Installed 在 BootNotification 后上送 */
#else
v_sys_reboot(); //系统重启 默认此时未充电
#endif
break;
}
case STEP_FTP_INIT_DEBUG: // 调试
{
break;
}
default:
break;
}
u8_out_cnt++ ;
if(e_ec200a_ftp_init_step == STEP_FTP_QUERY_CGREG) //查询PS域 网络注册状态
{
if(u8_out_cnt > 120){
e_ec200a_ftp_init_step = STEP_FTP_SET_QIDEACT; //强制跳转
u8_out_cnt = 0;
}
}
else if(u8_out_cnt > 60*3) //停留在其中一个状态 大于3分钟
{
#if BS_OCPP_EN
if (s_bs_task_ctrl.ocpp_data.u8_log_file_upload) {
v_ocpp_set_diag_upload_notify_pending(OCPP_DIAG_NOTIFY_PENDING_UPLOAD_FAILED);
} else {
v_ocpp_set_fw_notify_pending((U8_T)OCPP_FW_NOTIFY_PENDING_DOWNLOAD_FAILED);
}
#endif
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0; //停止
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0; //停止
u8_out_cnt = 0;
EC200A_FTP_LOG("FTP TO, exit upg\r\n");
}
return 0;
}
U8_T u8_ec200a_4g_ftp_log_upload()
{
S8_T s8_at_res;
static U8_T u8_out_cnt = 0;
// static U16_T u16_cnt = 0;
// if(u16_cnt++ < 10)
// return 0;
// u16_cnt = 0;
EC200A_FTP_LOG("ftp test %d\r\n",e_ec200a_ftp_init_step);
switch (e_ec200a_ftp_init_step)
{
case STEP_FTP_SWCHTO_CMD_MODE:
{
s8_at_res = s8_work_mode_cmd();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_QFDEL; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_QFDEL: //获取 删除存储区保存的文件内容
{
s8_at_res = s8_ec200a_qfdel();//
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_LOG_INFO; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_LOG_INFO:
{
s8_at_res = s8_get_log_info();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_UPLOAD_LOG_UFS; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_UPLOAD_LOG_UFS:
{
#if !EC200A_TCP_USE_TRANSPARENT_MODE
s8_at_res = s8_ec200a_qfopen_log_ufs();
#else
s8_at_res = -1;
#endif
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_WRITE_LOG_UFS; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_WRITE_LOG_UFS: //写入日志文件到 UFS
{
#if !EC200A_TCP_USE_TRANSPARENT_MODE
s8_at_res = s8_ec200a_write_log_data(); //返回值 等于1时 成功
#else
s8_at_res = -1;
#endif
// 约定:-1 失败;0 进行中;1 完成
if(s8_at_res == 0){
// 有进度则刷新超时计数,避免大文件上传过程中误判超时
u8_out_cnt = 0;
}else if(s8_at_res == 1){
e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFLST; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_GET_QFLST: //获取 UFS中文件内容 qflst
{
s8_at_res = s8_ec200a_qflst(); //返回值 等于1时 成功
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_QUERY_CGREG; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_QUERY_CGREG: //查询PS域 网络注册状态
{
s8_at_res = s8_ec200a_cgreg(); //查询PS域 网络注册状态
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_SET_QIDEACT; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_SET_QIDEACT: //去激活 FTP 专用 PDP
{
s8_at_res = s8_ec200a_qideact_ctx((U8_T)EC200A_FTP_PDP_CTX_ID);
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_SET_QIACT; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_SET_QIACT: //配置并激活 FTP 专用 PDP
{
s8_at_res = s8_ec200a_qicsgp_ctx((U8_T)EC200A_FTP_PDP_CTX_ID);
if (s8_at_res >= 0) {
s8_at_res = s8_ec200a_qiact_ctx((U8_T)EC200A_FTP_PDP_CTX_ID);
}
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_contextid; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_contextid: //配置 FTP(S) PDP 上下文 ID
{
s8_at_res = s8_ec200a_qftpcfg_contextid();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_account; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_account: //配置 FTP(S) 设置用户名和密码。
{
s8_at_res = s8_ec200a_qftpcfg_account();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_filetype; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_filetype: //配置 FTP(S) 设置文件类型为ASCII。
{
s8_at_res = s8_ec200a_qftpcfg_filetype();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_transmode; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_transmode: //配置 FTP(S) 设置为被动传输方式。
{
s8_at_res = s8_ec200a_qftpcfg_transmode();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_rsptimeout; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_rsptimeout: //配置 FTP(S) 设置最大响应时间(默认为90秒
{
s8_at_res = s8_ec200a_qftpcfg_rsptimeout();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_QFTPOPEN; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_QFTPOPEN: //登录FTP服务器。
{
s8_at_res = s8_ec200a_qftpopen();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_CFG_QFTPCWD; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_CFG_QFTPCWD: //切换目录
{
s8_at_res = s8_ec200a_qftpcwd();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_UPLOAD_LOG_FTP; //状态跳转
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_UPLOAD_LOG_FTP: //上传日志文件到 FT
{
s8_at_res = s8_ec200a_qftpput();
if(s8_at_res >= 0){
e_ec200a_ftp_init_step = STEP_FTP_FTP_QFTPCLOSE_AFTER_LOG;
u8_out_cnt = 0; //超时计数清空
}
break;
}
case STEP_FTP_FTP_QFTPCLOSE_AFTER_LOG:
{
static U8_T u8_qclose_fail_streak;
s8_at_res = s8_ec200a_qftpclose();
if (s8_at_res >= 0) {
u8_qclose_fail_streak = 0;
e_ec200a_ftp_init_step = STEP_FTP_INIT_OK;
u8_out_cnt = 0;
} else if (++u8_qclose_fail_streak >= 4U) {
u8_qclose_fail_streak = 0;
EC200A_FTP_LOG("FTP QFTPCLOSE fail ret=%d, continue\r\n", (int)s8_at_res);
e_ec200a_ftp_init_step = STEP_FTP_INIT_OK;
u8_out_cnt = 0;
}
break;
}
case STEP_FTP_INIT_OK:
case STEP_FTP_INIT_DEBUG: // 调试
{
if(u8_out_cnt < 20){ //等待 ftp 服务器 处理文件
break;
}
#if BS_OCPP_EN
v_ocpp_set_diag_upload_notify_pending(OCPP_DIAG_NOTIFY_PENDING_UPLOADED);
#endif
EC200A_FTP_LOG("FTP log upload done, keep OCPP (no relink)\r\n");
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0;
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0;
#if BS_OCPP_EN
s_bs_task_ctrl.ocpp_data.u8_log_file_upload = 0;
s_bs_task_ctrl.ocpp_data.ulong_timer = 0;
#endif
t_ftp_update_info.u8_init_flag = 0;
v_ftp_ctrl_init();
u8_out_cnt = 0;
break;
}
default:
break;
}
u8_out_cnt++ ;
if(e_ec200a_ftp_init_step == STEP_FTP_QUERY_CGREG) //查询PS域 网络注册状态
{
if(u8_out_cnt > 120){
e_ec200a_ftp_init_step = STEP_FTP_SET_QIDEACT; //强制跳转
u8_out_cnt = 0;
}
}
else if(u8_out_cnt > 60*3) //停留在其中一个状态 大于3分钟
{
#if BS_OCPP_EN
if (s_bs_task_ctrl.ocpp_data.u8_log_file_upload) {
v_ocpp_set_diag_upload_notify_pending(OCPP_DIAG_NOTIFY_PENDING_UPLOAD_FAILED);
}
#endif
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0; //停止
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0; //停止
u8_out_cnt = 0;
EC200A_FTP_LOG("FTP TO, exit upg\r\n");
}
return 0;
}
/*
LTE Standard(A)系列模块支持 FTP(S)协议,因此 FTP(S)服务器上的文件和目录可以通过 FTP(S)相
关 AT 命令进行操作。FTP(S)相关 AT 命令使用流程如下:
第一步:配置和激活 PDP 上下文。
1. 使用 AT+QICSGP 配置 PDP 上下文的<APN>、<username>、<password>和其他参数,详细
信息可参考文档 [1]。可通过 AT+CGQMIN、AT+CGEQMIN、AT+CGQREQ 和 AT+CGEQREQ
更新 QoS 设置,详细信息可参考文档 [2];
2. 通过 AT+QIACT 激活 PDP 上下文,详细信息可参考文档 [1];
3. 通过 AT+QFTPCFG="contextid",<contextID>配置 FTP(S) PDP 上下文 ID。需提前激活 PDP 上
下文。
第二步:配置用户账号和 FTP(S)服务器。
1. 通过 AT+QFTPCFG="account",<username>,<password>配置账号信息;
2. 通过 AT+QFTPCFG="filetype",<file_type>配置文件类型。在 FTP(S)服务器和客户端间传输的
文件类型可以是二进制数据或 ASCII 数据;
3. 通过 AT+QFTPCFG="transmode",<transmode>配置传输模式,即在 FTP(S)服务器端口或客户
端 端 口 监 听 数 据 连 接 。 请 注 意 , 因 为 FTPS 当 前 不 支 持 主 动 模 式 , 必 须 设 置
AT+QFTPCFG="transmode",1 后才能进行 FTPS 操作;
4. 通过 AT+QFTPCFG="rsptimeout",<timeout>配置命令的最大响应时间;
5. 若模块作为 FTPS 客户端,需执行如下命令:
1 执行 AT+QFTPCFG="ssltype",1
2 执行 AT+QFTPCFG="SSL_ctxID",<SSL_ctxID>设置 SSL 上下文 ID
3 AT+QSSLCFG 配置所指定的<SSL_ctxID>,详细信息可参考文档 [3]。
第三步:登录 FTP(S)服务器。
通 过 AT+QFTPOPEN=<hostname>,<port> 登录 FTP(S) 服务器 ,命令执行后, 若 响 应 URC
+QFTPOPEN: 0,0,表示操作成功。注意 FTPS 和 FTP 端口不同。FTPS 服务器的端口取决于 FTPS
服务器提供商,通常为 990。
第四步:文件操作。
1. 通过 AT+QFTPCWD 设置当前目录;
2. 上传文件至 FTP(S)服务器;
1) 通过 AT+QFUPL 可以将文件上传至 UFS 或 SD 卡,再通过 AT+QFTPPUT 将文件上传至
FTP(S)服务器。文件上传成功后,可通过 AT+QFDEL 将其删除。详细信息可参考文档 [4];
2 在 COM 口通过 AT+QFTPPUT 将文件上传至 FTP(S)服务器。执行+++可结束文件上传。
3. 通过 AT+QFTPGET 从 FTP(S)服务器下载文件。文件将在 COM 口输出或被保存至 UFS 或 SD
卡。若 COM 口输出文件,模块将进入数据模式;
4. 通过 AT+QFTPSIZE 获取 FTP(S)服务器文件大小;
5. 通过 AT+QFTPLEN 获取 FTP(S)服务器和客户端之间传输的数据长度;
6. 通过 AT+QFTPDEL 删除 FTP(S)服务器文件;
7. 通过 AT+QFTPRENAME 重命名 FTP(S)服务器文件。
第五步:FTP(S)服务器目录操作。
1. 通过 AT+QFTPCWD 设置当前目录;
2. 通过 AT+QFTPMKDIR 创建文件夹;
3. 通过 AT+QFTPLIST 列出目录内容;
4. 通过 AT+QFTPNLST 列出目录文件名;
5. 通过 AT+QFTPRENAME 重命名文件或文件夹;
6. 通过 AT+QFTPRMDIR 删除文件夹;
7. 通过 AT+QFTPMLSD 列出标准文件和目录信息;
8. 通过 AT+QFTPMDTM 获取 FTP(S)服务器文件修改时间。
第六步:关闭和 FTP(S)服务器的连接。
通过 AT+QFTPCLOSE 关闭和 FTP(S)服务器的连接,命令执行后,若响应 URC +QFTPCLOSE: 0,0
表明操作成功,即可重复上述第三步至第六步。
第七步:去激活 PDP 上下文。
通过 AT+QIDEACT=<contextID>去激活 PDP 上下文,详细信息可参考文档 [1]。
*/
//
#if(MY_SHELL_EN) //Shell 使能
/**
* ************************************************************************
* @brief - 遥控测试函数
*
*
*
* @author jiankalka
* @date 2025-08-04
* ************************************************************************
*/
// 命令 type 0 is fan1; 1 is fan2; 2 is acyk1; 3 is acyk2;
extern int parse_ftp_url(const char* url, T_FTP_UPDATE_INFO* info);
void ftpFunc()
{
//ftp://cpadmin@39.104.96.26/ftpfile/firmareV3.61.rbl
//ftp://cpadmin@39.104.96.26/AcTest.bin \addtogroup
//ftp://cpadmin@39.104.96.26/CCU601E.bin
const char *ftpUrl = "ftp://cpadmin:ieszt1234@39.104.96.26/CCU601E.bin";
v_shell_print("ftpTest %s\r\n",ftpUrl);
if(parse_ftp_url(ftpUrl, &t_ftp_update_info) == 0)
{
v_shell_print("ftpTest is ok\r\n");
v_shell_print("用户名=%s\r\n",t_ftp_update_info.u8_user);
v_shell_print("密码=%s\r\n",t_ftp_update_info.u8_pwd);
v_shell_print("域名=%s\r\n",t_ftp_update_info.u8_update_server_addr);
v_shell_print("文件路径=%s\r\n",t_ftp_update_info.u8_path);
v_shell_print("文件名=%s\r\n",t_ftp_update_info.u8_file_name);
t_ftp_update_info.u8_netType = 0;
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 1; //开始升级
//e_ec200a_ftp_init_step = STEP_FTP_FTP_GET_QFLST;
}
else
v_shell_print("ftpTest is err\r\n");
}
// 注册
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), ftpTest,ftpFunc, myshlle-- 4 g cat ftp test);
// U8_T u8_flash_test[PACKET_SIZE];
// void flashFunc(int id)
// {
// uint32_t len = PACKET_SIZE;
// upgrade_header_t header;
// memset(u8_flash_test,0,PACKET_SIZE);
// upgrade_read_packet(id,u8_flash_test,&len);
// printf_hex("flash",u8_flash_test,len);
// if (upgrade_read_header(&header) == 0)
// {
// EC200A_FTP_LOG("flag =[%d] , fileSize =[%d]\r\n",header.upgrade_flag,header.file_size);
// }
// else
// EC200A_FTP_LOG("header err\r\n");
// v_shell_print("flashFunc \r\n");
// }
// // 注册
// SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), flashTest,flashFunc, myshlle-- flashTest test);
#endif
#endif /* EC200A_4G_EN */