9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
1695 lines
56 KiB
C
1695 lines
56 KiB
C
#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/plat_comm_task.h"
|
||
#include "mylog/mylog.h"
|
||
#include "usart.h"
|
||
#include "app_init/app_init.h"
|
||
#if(MY_SHELL_EN) //Shell 使能
|
||
#include "letter_shell/shell_port.h"
|
||
#endif
|
||
|
||
/** 本文件 FTP/升级/日志 AT 调试:不走 Plat_Comm 的 Plat_Comm_LOG,统一 MYLOG_MSG(0xFF) 直出 */
|
||
#define EC200A_4G_LOG(fmt, ...) MYLOG_MSG(0xFF, fmt, ##__VA_ARGS__)
|
||
|
||
S_4G_CTRL_DATA s_4g_ctrl;
|
||
|
||
#if 1 //串口收发接口
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 数据发送接口函数 发送
|
||
*
|
||
* @param[in] u8_sendBuf - 发送数据地址
|
||
* @param[in] u16_len - 发送数据长度
|
||
*
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-08-07
|
||
* ************************************************************************
|
||
*/
|
||
void v_4g_send_data(U8_T u8_id, U8_T *u8_sendBuf , U16_T u16_len)
|
||
{
|
||
u16_usart_send(COM_PORT_BS,u8_sendBuf, u16_len);
|
||
|
||
#if TEST_BS_PRINTF_FLAG //调试打印
|
||
printf("//4g send,len is [%d]\r\n", u16_len);
|
||
printf_char("---",u8_sendBuf,u16_len);
|
||
#endif
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 数据接收接口函数 接收
|
||
*
|
||
* @param[in] u8_recvBuf - 接收数据地址
|
||
* @param[in] u16_len - 预期接收数据长度
|
||
*
|
||
* @return U16_T - 已接收数据长度
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-08-07
|
||
* ************************************************************************
|
||
*/
|
||
U16_T u16_4g_recv_data(U8_T *u8_recvBuf ,U16_T u16_len)
|
||
{
|
||
U16_T recv_len;
|
||
|
||
recv_len = u16_usart_recv(COM_PORT_BS,u8_recvBuf,u16_len);
|
||
|
||
#if TEST_BS_PRINTF_FLAG //调试打印
|
||
if(recv_len == 0) return 0;
|
||
printf("\\4g recv,len is [%d]\r\n", recv_len);
|
||
printf_char("---",u8_recvBuf,recv_len);
|
||
#endif
|
||
|
||
return recv_len;
|
||
}
|
||
|
||
/** 排空模组口 RX 环形缓冲(丢弃)。FTP / QISEND 等共用 UART 去粘连 */
|
||
void v_4g_uart_rx_discard_fifo(void)
|
||
{
|
||
U8_T scrap[128];
|
||
U16_T n;
|
||
U32_T total = 0;
|
||
|
||
while (total < 8192U) {
|
||
n = u16_usart_recv(COM_PORT_BS, scrap, sizeof(scrap));
|
||
if (n == 0U) {
|
||
break;
|
||
}
|
||
total += n;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#if 1 //公共接口函数
|
||
/**
|
||
* ************************************************************************
|
||
* @brief -在接收缓存中,查找是否收到期望应答
|
||
*
|
||
* @param[in] src -缓存数据
|
||
* @param[in] des -目标值
|
||
* @param[out] s8_findResult - 目标值下标
|
||
*
|
||
* @author LQ
|
||
* @date 2025-08-22
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_FindAck(U8_T *src, U8_T *des)
|
||
{
|
||
S8_T s8_findResult = -1; // 查询结果
|
||
char *p_strstr = NULL;
|
||
|
||
p_strstr = strstr((const char *)src, (const char *)des);
|
||
if (p_strstr != NULL)
|
||
{
|
||
s8_findResult = p_strstr - (char *)src; // des所在下标
|
||
}
|
||
#if TEST_BS_PRINTF_FLAG //调试打印
|
||
printf("s8_FindAck start \r\n - res(%d) , %s -> %s\r\ns8_FindAck end \r\n",s8_findResult,des,src);
|
||
#endif
|
||
return s8_findResult;
|
||
}
|
||
/**
|
||
* @brief 在二进制数据中查找目标序列
|
||
* @param src: 源数据缓冲区
|
||
* @param srcLen: 源数据长度(字节数)
|
||
* @param des: 目标序列
|
||
* @param desLen: 目标序列长度(字节数)
|
||
* @retval 找到返回目标序列在源数据中的起始索引,未找到返回-1
|
||
*/
|
||
S16_T s16_FindAck_Ex(U8_T *src, U16_T srcLen, U8_T *des, U16_T desLen)
|
||
{
|
||
S16_T s16_findResult = -1;
|
||
U16_T i, j;
|
||
|
||
// 参数检查
|
||
if (src == NULL || des == NULL || srcLen == 0 || desLen == 0) {
|
||
//printf("\r\n is NULL (%d)(%d)(%d)(%d)\r\n",(src == NULL),(des == NULL),(srcLen==0),(desLen == 0));
|
||
return -1;
|
||
}
|
||
|
||
// 如果目标序列比源数据还长,肯定找不到
|
||
if (desLen > srcLen) {
|
||
//printf("\r\ndesLen[%d] > srcLen[%d]\r\n",desLen,srcLen);
|
||
return -1;
|
||
}
|
||
|
||
// 遍历源数据,查找目标序列
|
||
for (i = 0; i <= srcLen - desLen; i++) {
|
||
// 比较当前位置开始的desLen个字节是否与目标序列匹配
|
||
for (j = 0; j < desLen; j++) {
|
||
if (src[i + j] != des[j]) {
|
||
break; // 发现不匹配,跳出内层循环
|
||
}
|
||
}
|
||
|
||
// 如果内层循环完整执行,说明找到了匹配
|
||
if (j == desLen) {
|
||
s16_findResult = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return s16_findResult;
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief -AT指令设置命令
|
||
*
|
||
* @param[in] sendbuf -发送数据指针
|
||
* @param[in] sendLen -发送数据长度
|
||
* @param[in] ack -期望应答有效数据 NULL是期待回复值为OK
|
||
* @param[in] resendCnt -重发次数计数
|
||
* @param[in] delayTimeMs -延时时间
|
||
* @param[out] ackIndex - 期望应答有效数据 -1 未接收到 》=0 成功接收到期望应答数据
|
||
*
|
||
* @author LQ
|
||
* @date 2025-08-22
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_4g_Send_AT_Cmd(U8_T *sendBuf, U8_T sendLen, U8_T *ack, U8_T resendCnt, U16_T delayTimeMs)
|
||
{
|
||
U8_T count = 0;
|
||
U16_T rcvLen = 0;
|
||
S8_T ackIndex = -1;
|
||
U8_T ackOK[] = "OK";
|
||
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
|
||
|
||
// com_write(COM_PORT_BS, sendBuf, sendLen, 0);
|
||
v_4g_send_data(ID_4G, sendBuf, sendLen);
|
||
do
|
||
{
|
||
mSleep(delayTimeMs);
|
||
|
||
memset(rcvBuf, 0, BS_RECV_BUF_LEN);
|
||
// rcvLen = com_read(COM_PORT_BS, rcvBuf, 100, 0);
|
||
rcvLen = u16_4g_recv_data(rcvBuf, BS_RECV_BUF_LEN);
|
||
if (rcvLen > 0)
|
||
{
|
||
if (s8_FindAck(rcvBuf, ackOK) >= 0) // 收到OK,如果后面有数据,说明数据也接收完全
|
||
{
|
||
if (ack != NULL)
|
||
{
|
||
ackIndex = s8_FindAck(rcvBuf, ack); // 找到期望应答有效数据
|
||
if ((ackIndex >= 0))
|
||
return ackIndex;
|
||
}
|
||
else
|
||
return 1;
|
||
}
|
||
}
|
||
count++;
|
||
} while (count < resendCnt); // 如果没收到期望应答,重新发送
|
||
|
||
return -1;
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 具有返回值的 AT指令发送、接收函数
|
||
*
|
||
* @param[in] sendBuf - 发送数据指针
|
||
* @param[in] sendLen - 发送数据长度
|
||
* @param[in] rcvBuf - 接收的数据内容 外部传入,接收赋值后传出 方便外部直接使用
|
||
* @param[in] recvLen - 最大可支持数据内容,防止数组越界
|
||
* @param[in] ack - 期望应答有效数据 NULL是期待回复值为OK 非NULL时,期待回复值为期待值
|
||
* @param[in] resendCnt - 重发次数计数
|
||
* @param[in] delayTimeMs - 单次接收等待延时时间
|
||
*
|
||
* @return S8_T - 期望应答有效数据 -1 未接收到 》=0 成功接收到期望应答数据 并标志期待值的下标位置
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-24
|
||
* ************************************************************************
|
||
*/
|
||
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)
|
||
{
|
||
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(ID_4G, 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)
|
||
{
|
||
if(ack == NULL) //预期为空时 找OK
|
||
{
|
||
ackIndex = s16_FindAck_Ex(rcvBuf,rcvLen+2, ackOK ,sizeof(ackOK)-1); //+2
|
||
if ((ackIndex >= 0))
|
||
return ackIndex;
|
||
}
|
||
else // 预期不为空时 找预期内容
|
||
{
|
||
ackIndex = s16_FindAck_Ex(rcvBuf,rcvLen, ack,strlen((char *)ack));
|
||
if ((ackIndex >= 0))
|
||
return ackIndex;
|
||
}
|
||
}
|
||
count++;
|
||
} while (count < resendCnt); // 如果没收到期望应答,重新发送
|
||
|
||
return -1;
|
||
}
|
||
void v_4g_log_data_send_data(U8_T *u8_sendBuf , U16_T u16_len)
|
||
{
|
||
if (u8_sendBuf != NULL && u16_len > 0U) {
|
||
v_4g_send_data(ID_4G, u8_sendBuf, u16_len);
|
||
}
|
||
|
||
#if TEST_BS_PRINTF_FLAG //调试打印
|
||
printf("//4g log send,len is [%d]\r\n", u16_len);
|
||
printf_hex("---",u8_sendBuf,u16_len);
|
||
#endif
|
||
}
|
||
#endif
|
||
#if 1 //内部逻辑控制
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 查询信号强度
|
||
*
|
||
*
|
||
* @return S8_T - -1 查询失败 >0 查询成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_csq()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CSQ\r\n";
|
||
U8_T u8_ack[] = "+CSQ: ";
|
||
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
|
||
S8_T s8_ret;
|
||
S8_T ackIndex = -1;
|
||
int a, b;
|
||
|
||
s8_ret = s8_4g_Send_Recv_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1,rcvBuf,BS_RECV_BUF_LEN, u8_ack, 2, 50);
|
||
|
||
if(s8_ret >= 0)
|
||
{
|
||
if (sscanf((char *)&rcvBuf[s8_ret], "+CSQ: %d,%d", &a, &b) == 2) {
|
||
s_4g_ctrl.u8_csq = a;
|
||
#if TEST_BS_PRINTF_FLAG
|
||
printf("信号强度= %d\r\n",s_4g_ctrl.u8_csq);
|
||
#endif
|
||
}
|
||
else
|
||
s8_ret = -1;
|
||
}
|
||
return s8_ret;
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 查询sim卡号 ccid号
|
||
*
|
||
*
|
||
* @return S8_T - -1 查询失败 >0 查询成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_qccid()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+QCCID\r\n";
|
||
U8_T u8_ack[] = "+QCCID:";
|
||
S8_T ackIndex = -1;
|
||
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
|
||
S8_T s8_ret;
|
||
|
||
s8_ret = s8_4g_Send_Recv_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1,rcvBuf,BS_RECV_BUF_LEN, NULL, 2, 50);
|
||
|
||
if(s8_ret >= 0)
|
||
{
|
||
ackIndex = s8_FindAck(rcvBuf, u8_ack);
|
||
if ((ackIndex >= 0))
|
||
{
|
||
s8_ret = 1;
|
||
memset(s_4g_ctrl.u8_Sim_Card, 0, 20);
|
||
strncpy((char *)s_4g_ctrl.u8_Sim_Card, (char *)&rcvBuf[ackIndex + 8],20);
|
||
printf("*************** QCCID recv = %s\r\n", s_4g_ctrl.u8_Sim_Card);
|
||
}
|
||
else
|
||
{
|
||
s8_ret = -1;
|
||
}
|
||
|
||
}
|
||
return s8_ret;
|
||
}
|
||
|
||
static S8_T s8_ec200a_gmr(void)
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+GMR\r\n";
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
}
|
||
|
||
/////////////////////////////////
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 查询 PS 域网络注册状态
|
||
* 1. 当 AT+CGREG?/AT+CEREG? 结果中的<stat>等于 1 或者5,表示模块已在UMTS/LTE网络注册上PS业务
|
||
* 2. 等待60 s后,不管是否注册上PS业务,都进入下一个流程
|
||
*
|
||
*
|
||
* @return S8_T - -1 查询失败 >0 查询成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_ec200a_set_cgreg()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CGREG=2\r\n";
|
||
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
}
|
||
S8_T s8_ec200a_cgreg()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CGREG=2\r\n";
|
||
U8_T u8_sendBuf2[] = "AT+CGREG?\r\n";
|
||
U8_T u8_ack[] = "+CGREG:";
|
||
|
||
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
|
||
S8_T s8_ret;
|
||
int n, stat;
|
||
|
||
// s8_ret = s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
// if(s8_ret == -1)
|
||
// return s8_ret;
|
||
|
||
s8_ret = s8_4g_Send_Recv_Cmd(u8_sendBuf2, sizeof(u8_sendBuf2) - 1,rcvBuf,BS_RECV_BUF_LEN, u8_ack, 2, 50);
|
||
|
||
if(s8_ret >= 0)
|
||
{
|
||
// printf("CGREG recv = %s\n", rcvBuf);
|
||
|
||
// 使用sscanf直接提取两个数字
|
||
if (sscanf((char *)&rcvBuf[s8_ret], "+CGREG: %d,%d", &n, &stat) == 2) {
|
||
//printf("CGREG 成功提取: n=%d, stat=%d\r\n", n, stat);
|
||
// 判断stat 是否为1或者5 已注册,其余为未注册
|
||
if(stat != 1 && stat != 5)
|
||
s8_ret = -1;
|
||
else if(stat == 3)
|
||
EC200A_4G_LOG("REG reject\r\n");
|
||
}
|
||
else
|
||
s8_ret = -1;
|
||
}
|
||
|
||
// U8_T u8_test[] = "AT+COPS?\r\n";
|
||
// s8_4g_Send_AT_Cmd(u8_test, sizeof(u8_test) - 1, NULL, 2, 50);
|
||
|
||
return s8_ret;
|
||
}
|
||
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 查询(U)SIM卡状态
|
||
*
|
||
*
|
||
* @return S8_T - -1 未成功 >0成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-21
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_cpin()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CPIN?\r\n";
|
||
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
}
|
||
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - AT+QICSGP 配置 TCP/IP 场景参数
|
||
* AT+QICSGP=<contextID>[,<context_type>,<APN>[,<username>,<password>)[,<authentication>[,<CDMA_pwd>]]]]
|
||
* AT+QICSGP=1,1,"UNINET","","",1 //配置场景 1,APN 配置为"UNINET"(中国联通)。
|
||
*
|
||
* <contextID> 整型。场景 ID。范围:1~15。
|
||
* <context_type> 整型。协议类型。
|
||
* 1 IPv4
|
||
* 2 IPv6
|
||
* 3 IPv4v6
|
||
* <APN> 字符串类型。接入点名称。
|
||
* <username> 字符串类型。用户名。最大长度:127 字节。
|
||
* <password> 字符串类型。密码。最大长度:127 字节。
|
||
* <authentication> 整型。APN 鉴权方式。
|
||
* 0 None
|
||
* 1 PAP
|
||
* 2 CHAP
|
||
* 3 PAP 或 CHAP
|
||
* <CDMA_pwd> 整型。是否在 CDMA 网络下保存<username>和<password>。
|
||
* 0 不保存
|
||
* 1 保存
|
||
* @return S8_T - -1 未成功 >0成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_ec200a_qicsgp_ctx(U8_T context_id)
|
||
{
|
||
if(s_4g_ctrl.s_4g_para.u8_Sim_Type == 0) //公网卡 跳过设置APN
|
||
{
|
||
if(s_4g_ctrl.s_4g_para.apn_str == NULL ||
|
||
strlen((char *)s_4g_ctrl.s_4g_para.apn_str) == 0) //无apn配置
|
||
{
|
||
U8_T u8_sendBuf[48];
|
||
int n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
|
||
"AT+QICSGP=%u,3,\"\",\"\",\"\",1\r\n", (unsigned)context_id);
|
||
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
|
||
return -1;
|
||
}
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 2, 50);
|
||
}
|
||
else
|
||
{
|
||
U8_T u8_sendBuf[72] = {0};
|
||
int n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
|
||
"AT+QICSGP=%u,3,\"%s\",\"\",\"\",1\r\n",
|
||
(unsigned)context_id,
|
||
s_4g_ctrl.s_4g_para.apn_str);
|
||
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
|
||
return -1;
|
||
}
|
||
printf("apn 2 = %s\r\n", u8_sendBuf);
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 2, 50);
|
||
}
|
||
}
|
||
else if(s_4g_ctrl.s_4g_para.u8_Sim_Type == 1)
|
||
{
|
||
U8_T u8_sendBuf[56];
|
||
int n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
|
||
"AT+QICSGP=%u,3,\"M2MPLNAMR\",\"\",\"\",1\r\n", (unsigned)context_id);
|
||
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
|
||
return -1;
|
||
}
|
||
printf("apn 3 = %s\r\n", u8_sendBuf);
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 2, 50);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static S8_T s8_ec200a_qicsgp(void)
|
||
{
|
||
return s8_ec200a_qicsgp_ctx((U8_T)EC200A_TCP_PDP_CTX_ID);
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - AT+CGQMIN 服务质量参数(最低可接受的)
|
||
* 该命令允许 TE 指定最低可接受的配置文件参数,当激活 PDP 上下文时,由 MT 根据协商的参数检查
|
||
* 该最低可接受的配置文件。设置命令指定上下文标识符<cid>标识的上下文的参数。
|
||
* 设置命令的一种特殊形式 AT+CGQMIN=<cid>会使上下文标识符<cid>的最低可接受的配置文件处于
|
||
* 未定义状态,此时将不会对协商的配置文件进行检查。查询命令返回每个已定义 PDP 上下文的当前配置。
|
||
* 详情请参考 3GPP TS 23.107。
|
||
*
|
||
* @return S8_T - -1 未成功 >0成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_cgqmin()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CGQMIN?\r\n";
|
||
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - AT+CGQREQ 服务质量参数(请求的)
|
||
* 在 MT 激活 PDP 上下文时,该命令允许 MT 指定服务质量配置文件。
|
||
* 该设置命令指定上下文<cid>的配置文件。设置命令的一种特殊形式 AT+CGQREQ=<cid>会使上下文
|
||
* 标识符<cid>请求的参数处于未定义状态。查询命令返回每一个已定义的上下文的当前配置。详细信息请
|
||
* 参考 3GPP TS 23.107。
|
||
*
|
||
* @return S8_T - -1 未成功 >0成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_cgqreq()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CGQREQ?\r\n";
|
||
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - AT+QIACT 激活 PDP 场景
|
||
* 在用AT+QIACT激活PDP场景前,需要使用AT+QICSGP配置场景。场景激活后,可以通过AT+QIACT?查询 IP 地址。
|
||
* 虽然<contextID>的范围为 1~15,但是模块最多仅可同时激活 3 路 PDP 场景。受网络状态影响,执行
|
||
* AT+QIACT 后,等待返回结果 OK 或者 ERROR 的最大时间为 150 秒,在结果尚未返回之前,无法执行任何 AT 命令。
|
||
*
|
||
*
|
||
* @return S8_T - -1 未成功 >0成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_ec200a_qiact_ctx(U8_T context_id)
|
||
{
|
||
U8_T u8_sendBuf[24];
|
||
int n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QIACT=%u\r\n", (unsigned)context_id);
|
||
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
|
||
return -1;
|
||
}
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 20, 1000);
|
||
}
|
||
|
||
S8_T s8_ec200a_qiact(void)
|
||
{
|
||
return s8_ec200a_qiact_ctx((U8_T)EC200A_TCP_PDP_CTX_ID);
|
||
}
|
||
|
||
S8_T s8_ec200a_qideact_ctx(U8_T context_id)
|
||
{
|
||
U8_T u8_sendBuf[24];
|
||
int n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf), "AT+QIDEACT=%u\r\n", (unsigned)context_id);
|
||
if (n <= 0 || n >= (int)sizeof(u8_sendBuf)) {
|
||
return -1;
|
||
}
|
||
return s8_4g_Send_AT_Cmd(u8_sendBuf, (U16_T)n, NULL, 5, 100);
|
||
}
|
||
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - TLS 相关参数配置(使用模块内置 SSL 功能)
|
||
* 该函数示例性地通过 QSSLCFG 配置 SSL 上下文,具体参数需根据
|
||
* EC200A SSL AT 命令手册进行调整与完善。
|
||
*
|
||
* @return S8_T - -1 未成功 >0成功
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_ssl_cfg(void)
|
||
{
|
||
S8_T s8_ret;
|
||
U8_T u8_buf[64];
|
||
|
||
/* 如未启用 TLS,直接视为成功 */
|
||
if (s_4g_ctrl.s_4g_para.use_tls == 0U) {
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* 按《LTE Standard(A)系列 SSL 应用指导》配置常用 SSL 参数:
|
||
* 1) TLS 版本设为 TLS1.2:AT+QSSLCFG="sslversion",<ctx>,4
|
||
* 2) 身份验证模式设为 0(不验证服务器证书,便于联调):AT+QSSLCFG="seclevel",<ctx>,0
|
||
* 3) 忽略本地时间对证书有效性的影响:AT+QSSLCFG="ignorelocaltime",<ctx>,1
|
||
* 4) 启用 SNI:AT+QSSLCFG="sni",<ctx>,1
|
||
* 如需严格验证服务器证书,可将 seclevel 改为 1/2,并配置 CA 证书路径等。
|
||
*/
|
||
|
||
/* 1) TLS 版本:TLS1.2 */
|
||
snprintf((char *)u8_buf, sizeof(u8_buf), "AT+QSSLCFG=\"sslversion\",%d,4\r\n", EC200A_SSL_CTX_ID);
|
||
s8_ret = s8_4g_Send_AT_Cmd(u8_buf, (U8_T)strlen((char *)u8_buf), NULL, 2, 200);
|
||
if (s8_ret < 0) {
|
||
return s8_ret;
|
||
}
|
||
|
||
/* 2) 身份验证模式:0=不验证服务器证书(联调阶段),后续可改为 1/2 */
|
||
snprintf((char *)u8_buf, sizeof(u8_buf), "AT+QSSLCFG=\"seclevel\",%d,0\r\n", EC200A_SSL_CTX_ID);
|
||
s8_ret = s8_4g_Send_AT_Cmd(u8_buf, (U8_T)strlen((char *)u8_buf), NULL, 2, 200);
|
||
if (s8_ret < 0) {
|
||
return s8_ret;
|
||
}
|
||
|
||
/* 3) 忽略证书有效期对验证结果的影响(模块本地时间可能尚未与网络时间同步) */
|
||
snprintf((char *)u8_buf, sizeof(u8_buf), "AT+QSSLCFG=\"ignorelocaltime\",%d,1\r\n", EC200A_SSL_CTX_ID);
|
||
s8_ret = s8_4g_Send_AT_Cmd(u8_buf, (U8_T)strlen((char *)u8_buf), NULL, 2, 200);
|
||
if (s8_ret < 0) {
|
||
return s8_ret;
|
||
}
|
||
|
||
/* 4) 启用 SNI,便于服务器根据主机名匹配证书 */
|
||
snprintf((char *)u8_buf, sizeof(u8_buf), "AT+QSSLCFG=\"sni\",%d,1\r\n", EC200A_SSL_CTX_ID);
|
||
s8_ret = s8_4g_Send_AT_Cmd(u8_buf, (U8_T)strlen((char *)u8_buf), NULL, 2, 200);
|
||
if (s8_ret < 0) {
|
||
return s8_ret;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
#if !EC200A_TCP_USE_TRANSPARENT_MODE
|
||
|
||
/** memchr-style:在 buf[0..len) 中查找 needle(二进制安全,不要求 '\0') */
|
||
static int ec200a_buf_contains(const U8_T *buf, U16_T len, const char *needle)
|
||
{
|
||
U16_T nl;
|
||
U16_T i;
|
||
|
||
if (buf == NULL || needle == NULL) {
|
||
return 0;
|
||
}
|
||
nl = (U16_T)strlen(needle);
|
||
if (nl == 0U || len < nl) {
|
||
return 0;
|
||
}
|
||
for (i = 0; i + nl <= len; i++) {
|
||
if (memcmp(buf + i, needle, nl) == 0) {
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/** 缓冲 TCP:经 `v_4g_send_data` 与 AT 路径共用模组 UART(`COM_PORT_BS`) */
|
||
static U16_T u16_4g_uart_rx_modem_raw(U8_T *buf, U16_T len)
|
||
{
|
||
if (buf == NULL || len == 0U) {
|
||
return 0;
|
||
}
|
||
return u16_usart_recv(COM_PORT_BS, buf, len);
|
||
}
|
||
|
||
static U16_T u16_4g_uart_rx_modem(U8_T *buf, U16_T len)
|
||
{
|
||
U16_T n;
|
||
|
||
n = u16_4g_uart_rx_modem_raw(buf, len);
|
||
#if TEST_BS_PRINTF_FLAG
|
||
if (n > 0U) {
|
||
printf("\\4g buf recv,len is [%d]\r\n", n);
|
||
printf_char("---", buf, n);
|
||
}
|
||
#endif
|
||
return n;
|
||
}
|
||
|
||
/** 非透传:发送 AT 后累计 UART,直到解析到 +QIOPEN / +QSSLOPEN 成功 URC */
|
||
static S8_T s8_ec200a_qiopen_accumulate(const U8_T *cmd, U16_T cmd_len)
|
||
{
|
||
U8_T acc[EC200A_QIOPEN_ACCUM_SZ];
|
||
U16_T acc_len = 0;
|
||
U16_T wait_i;
|
||
|
||
memset(acc, 0, sizeof(acc));
|
||
v_4g_send_data(ID_4G, (U8_T *)(void *)cmd, cmd_len);
|
||
|
||
for (wait_i = 0; wait_i < 200U; wait_i++) {
|
||
unsigned cid = 0, err = 1;
|
||
char *p;
|
||
|
||
mSleep(100);
|
||
if (acc_len + 1U < sizeof(acc)) {
|
||
acc_len += u16_4g_uart_rx_modem(acc + acc_len, (U16_T)(sizeof(acc) - 1U - acc_len));
|
||
acc[acc_len < sizeof(acc) ? acc_len : sizeof(acc) - 1U] = '\0';
|
||
}
|
||
|
||
p = strstr((char *)acc, "+QIOPEN:");
|
||
if (p == NULL) {
|
||
p = strstr((char *)acc, "+QSSLOPEN:");
|
||
}
|
||
if (p != NULL) {
|
||
int nfield = 0;
|
||
if (strncmp(p, "+QIOPEN:", 8) == 0) {
|
||
nfield = sscanf(p, "+QIOPEN: %u,%u", &cid, &err);
|
||
} else if (strncmp(p, "+QSSLOPEN:", 10) == 0) {
|
||
nfield = sscanf(p, "+QSSLOPEN: %u,%u", &cid, &err);
|
||
}
|
||
if (nfield >= 2 && err == 0U) {
|
||
s_4g_ctrl.u8_tcp_socket_id = s_4g_ctrl.s_4g_para.use_tls ? EC200A_SSL_CLIENT_ID : 0U;
|
||
(void)cid;
|
||
return 1;
|
||
}
|
||
}
|
||
if (strstr((char *)acc, "+CME ERROR:") != NULL || strstr((char *)acc, "\r\nERROR\r\n") != NULL) {
|
||
break;
|
||
}
|
||
}
|
||
EC200A_4G_LOG("4G QIOPEN buffered: timeout or error\r\n");
|
||
return -1;
|
||
}
|
||
|
||
/** AT+QISEND / AT+QSSLSEND:<id>,<len> 后等待 '>',再发二进制,再等 SEND OK */
|
||
static S8_T s8_ec200a_sock_send_once(U8_T sock_id, const U8_T *data, U16_T chunk_len, U8_T use_tls)
|
||
{
|
||
U8_T u8_cmd[56];
|
||
U8_T rb[EC200A_QISEND_WAIT_RB_SZ];
|
||
U16_T rb_len = 0;
|
||
U16_T i;
|
||
U8_T attempt;
|
||
const char *at_tag = use_tls ? "QSSLSEND" : "QISEND";
|
||
|
||
if (data == NULL || chunk_len == 0U) {
|
||
return -1;
|
||
}
|
||
snprintf((char *)u8_cmd, sizeof(u8_cmd), "AT+%s=%u,%u\r\n",
|
||
at_tag, (unsigned)sock_id, (unsigned)chunk_len);
|
||
|
||
/* FTP 与 QISEND/QSSLSEND 共用 UART:先排空 RX,必要时重发一遍 AT */
|
||
for (attempt = 0; attempt < 2U; attempt++) {
|
||
v_4g_uart_rx_discard_fifo();
|
||
if (attempt > 0U) {
|
||
mSleep(50);
|
||
}
|
||
|
||
v_4g_send_data(ID_4G, u8_cmd, (U16_T)strlen((char *)u8_cmd));
|
||
|
||
memset(rb, 0, sizeof(rb));
|
||
rb_len = 0;
|
||
for (i = 0; i < 300U; i++) {
|
||
mSleep(10);
|
||
if (rb_len + 1U < sizeof(rb)) {
|
||
rb_len += u16_4g_uart_rx_modem(rb + rb_len, (U16_T)(sizeof(rb) - 1U - rb_len));
|
||
rb[rb_len < sizeof(rb) ? rb_len : sizeof(rb) - 1U] = '\0';
|
||
}
|
||
if (memchr(rb, '>', (size_t)rb_len) != NULL) {
|
||
goto sock_send_got_prompt;
|
||
}
|
||
if (ec200a_buf_contains(rb, rb_len, "\r\nERROR\r\n") || ec200a_buf_contains(rb, rb_len, "\nERROR\r\n")) {
|
||
EC200A_4G_LOG("4G %s: AT ERROR (no >)\r\n", at_tag);
|
||
return -1;
|
||
}
|
||
if (rb_len + 64U >= sizeof(rb)) {
|
||
EC200A_4G_LOG("4G %s: rx buf full no >\r\n", at_tag);
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
EC200A_4G_LOG("4G %s: no prompt >\r\n", at_tag);
|
||
return -1;
|
||
|
||
sock_send_got_prompt:
|
||
v_4g_send_data(ID_4G, (U8_T *)(void *)data, chunk_len);
|
||
|
||
memset(rb, 0, sizeof(rb));
|
||
rb_len = 0;
|
||
for (i = 0; i < 600U; i++) {
|
||
mSleep(10);
|
||
if (rb_len + 1U < sizeof(rb)) {
|
||
rb_len += u16_4g_uart_rx_modem(rb + rb_len, (U16_T)(sizeof(rb) - 1U - rb_len));
|
||
rb[rb_len < sizeof(rb) ? rb_len : sizeof(rb) - 1U] = '\0';
|
||
}
|
||
if (rb_len + 32U >= sizeof(rb) && rb_len > EC200A_QISEND_RB_TAIL_KEEP) {
|
||
U16_T drop = (U16_T)(rb_len - EC200A_QISEND_RB_TAIL_KEEP);
|
||
memmove(rb, rb + drop, (size_t)(rb_len - drop));
|
||
rb_len = (U16_T)(rb_len - drop);
|
||
rb[rb_len < sizeof(rb) ? rb_len : sizeof(rb) - 1U] = '\0';
|
||
}
|
||
if (ec200a_buf_contains(rb, rb_len, "SEND OK")) {
|
||
return 1;
|
||
}
|
||
if (ec200a_buf_contains(rb, rb_len, "\r\nERROR\r\n") || ec200a_buf_contains(rb, rb_len, "\nERROR\r\n") ||
|
||
ec200a_buf_contains(rb, rb_len, "SEND FAIL")) {
|
||
EC200A_4G_LOG("4G %s: fail\r\n", at_tag);
|
||
return -1;
|
||
}
|
||
}
|
||
EC200A_4G_LOG("4G %s: no SEND OK\r\n", at_tag);
|
||
return -1;
|
||
}
|
||
|
||
/** 从 +QIRD: / +QSSLRECV: 应答行提取负载(格式相同,仅前缀不同) */
|
||
static U16_T ec200a_parse_sock_recv_payload(const char *tag, const U8_T *rb, U16_T rb_len,
|
||
U8_T *out, U16_T out_max)
|
||
{
|
||
const U16_T tag_len = (U16_T)strlen(tag);
|
||
U16_T i, j;
|
||
unsigned datalen = 0;
|
||
int had_len_digits = 0;
|
||
|
||
if (rb == NULL || out == NULL || rb_len == 0U || out_max == 0U) {
|
||
return 0;
|
||
}
|
||
|
||
/* 多次应答拼在同一缓冲时,取最后一次 tag 行 */
|
||
i = rb_len;
|
||
for (j = 0; j + tag_len <= rb_len; j++) {
|
||
if (memcmp(rb + j, tag, tag_len) == 0) {
|
||
i = j;
|
||
}
|
||
}
|
||
if (i + tag_len > rb_len) {
|
||
return 0;
|
||
}
|
||
|
||
j = (U16_T)(i + tag_len);
|
||
while (j < rb_len && (rb[j] == ' ' || rb[j] == '\t')) {
|
||
j++;
|
||
}
|
||
/* 支持 +TAG: <len> 或 +TAG: <mux>,<len> */
|
||
datalen = 0;
|
||
while (j < rb_len && rb[j] >= '0' && rb[j] <= '9') {
|
||
had_len_digits = 1;
|
||
datalen = datalen * 10U + (unsigned)(rb[j] - '0');
|
||
j++;
|
||
}
|
||
if (j < rb_len && rb[j] == ',') {
|
||
j++;
|
||
datalen = 0;
|
||
had_len_digits = 0;
|
||
while (j < rb_len && rb[j] >= '0' && rb[j] <= '9') {
|
||
had_len_digits = 1;
|
||
datalen = datalen * 10U + (unsigned)(rb[j] - '0');
|
||
j++;
|
||
}
|
||
}
|
||
if (!had_len_digits) {
|
||
return 0;
|
||
}
|
||
|
||
while (j < rb_len && rb[j] != '\n') {
|
||
j++;
|
||
}
|
||
if (j >= rb_len) {
|
||
return 0;
|
||
}
|
||
j++; /* 跳过 \n(允许上一字节为 \r) */
|
||
|
||
if (datalen == 0U) {
|
||
return 0;
|
||
}
|
||
|
||
if ((U32_T)j + datalen > rb_len) {
|
||
return 0;
|
||
}
|
||
|
||
if (datalen > out_max) {
|
||
datalen = out_max;
|
||
}
|
||
memcpy(out, rb + j, datalen);
|
||
return (U16_T)datalen;
|
||
}
|
||
|
||
static U16_T ec200a_parse_qird_payload(const U8_T *rb, U16_T rb_len, U8_T *out, U16_T out_max)
|
||
{
|
||
return ec200a_parse_sock_recv_payload("+QIRD:", rb, rb_len, out, out_max);
|
||
}
|
||
|
||
static U16_T ec200a_parse_qsslrecv_payload(const U8_T *rb, U16_T rb_len, U8_T *out, U16_T out_max)
|
||
{
|
||
return ec200a_parse_sock_recv_payload("+QSSLRECV:", rb, rb_len, out, out_max);
|
||
}
|
||
|
||
/** 判定 UART 缓冲内「最后一次 +TAG:」应答是否已收全 */
|
||
static int ec200a_sock_recv_rx_complete(const char *tag, const U8_T *rb, U16_T rb_len)
|
||
{
|
||
const U16_T tag_len = (U16_T)strlen(tag);
|
||
U16_T i, j;
|
||
unsigned datalen = 0;
|
||
int had_len_digits = 0;
|
||
|
||
if (rb == NULL || rb_len < tag_len) {
|
||
return 0;
|
||
}
|
||
i = rb_len;
|
||
for (j = 0; j + tag_len <= rb_len; j++) {
|
||
if (memcmp(rb + j, tag, tag_len) == 0) {
|
||
i = j;
|
||
}
|
||
}
|
||
if (i + tag_len > rb_len) {
|
||
return 0;
|
||
}
|
||
j = (U16_T)(i + tag_len);
|
||
while (j < rb_len && (rb[j] == ' ' || rb[j] == '\t')) {
|
||
j++;
|
||
}
|
||
datalen = 0;
|
||
while (j < rb_len && rb[j] >= '0' && rb[j] <= '9') {
|
||
had_len_digits = 1;
|
||
datalen = datalen * 10U + (unsigned)(rb[j] - '0');
|
||
j++;
|
||
}
|
||
if (j < rb_len && rb[j] == ',') {
|
||
j++;
|
||
datalen = 0;
|
||
had_len_digits = 0;
|
||
while (j < rb_len && rb[j] >= '0' && rb[j] <= '9') {
|
||
had_len_digits = 1;
|
||
datalen = datalen * 10U + (unsigned)(rb[j] - '0');
|
||
j++;
|
||
}
|
||
}
|
||
while (j < rb_len && rb[j] != '\n') {
|
||
j++;
|
||
}
|
||
if (j >= rb_len) {
|
||
return 0;
|
||
}
|
||
j++;
|
||
if (!had_len_digits) {
|
||
return 0;
|
||
}
|
||
if (datalen == 0U) {
|
||
return 1;
|
||
}
|
||
return ((U32_T)j + datalen <= rb_len) ? 1 : 0;
|
||
}
|
||
|
||
static int ec200a_qird_rx_complete(const U8_T *rb, U16_T rb_len)
|
||
{
|
||
return ec200a_sock_recv_rx_complete("+QIRD:", rb, rb_len);
|
||
}
|
||
|
||
static int ec200a_qsslrecv_rx_complete(const U8_T *rb, U16_T rb_len)
|
||
{
|
||
return ec200a_sock_recv_rx_complete("+QSSLRECV:", rb, rb_len);
|
||
}
|
||
|
||
/**
|
||
* @brief 非透传模式下发送 TCP/TLS 应用层数据(经模组缓冲 Socket)。
|
||
*
|
||
* 明文 TCP 使用 `AT+QISEND`;`QSSLOPEN` 建立的 wss 通道须用 `AT+QSSLSEND`(对 SSL socket 发 QISEND 会直接 ERROR)。
|
||
* 按 `EC200A_TCP_APP_RW_MAX` 分包,UART 与 FTP/其它 AT 共用。
|
||
* 任一分包失败则停止后续发送并打日志。
|
||
*
|
||
* @param[in] u8_id 预留与平台抽象一致,当前未使用。
|
||
* @param[in] u8_sendBuf 待发数据缓冲。
|
||
* @param[in] u16_len 待发总长度(字节)。
|
||
*
|
||
* @note 仅在 `EC200A_TCP_USE_TRANSPARENT_MODE == 0` 时编译;透传路径请用 `v_4g_send_data`。
|
||
*/
|
||
void v_ec200a_tcp_app_send(U8_T u8_id, U8_T *u8_sendBuf, U16_T u16_len)
|
||
{
|
||
U8_T sock_id = s_4g_ctrl.u8_tcp_socket_id;
|
||
U8_T use_tls = s_4g_ctrl.s_4g_para.use_tls;
|
||
U16_T off;
|
||
|
||
(void)u8_id;
|
||
if (u8_sendBuf == NULL || u16_len == 0U) {
|
||
return;
|
||
}
|
||
|
||
{
|
||
U16_T chunk_cap = EC200A_TCP_APP_RW_MAX;
|
||
if (chunk_cap > EC200A_QISEND_AT_LEN_MAX) {
|
||
chunk_cap = EC200A_QISEND_AT_LEN_MAX;
|
||
}
|
||
for (off = 0; off < u16_len; off += chunk_cap) {
|
||
U16_T chunk = (U16_T)(u16_len - off);
|
||
if (chunk > chunk_cap) {
|
||
chunk = chunk_cap;
|
||
}
|
||
if (s8_ec200a_sock_send_once(sock_id, u8_sendBuf + off, chunk, use_tls) < 0) {
|
||
EC200A_4G_LOG("4G tcp buffered send break off=%u (%s)\r\n",
|
||
(unsigned)off, use_tls ? "QSSLSEND" : "QISEND");
|
||
v_4g_uart_rx_discard_fifo();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 非透传模式下从模组 Socket 接收缓冲区读取一段数据(AT+QIRD 或 AT+QSSLRECV)。
|
||
*
|
||
* wss 使用 `AT+QSSLRECV=<sock_id>,want`;明文 TCP 使用 `AT+QIRD=...`。轮询 UART 直至应答收齐,
|
||
* 再从最后一次 `+QSSLRECV:` / `+QIRD:` 行拷贝负载。
|
||
* 单次调用最多返回 `min(u16_len, EC200A_TCP_APP_RW_MAX, EC200A_QIRD_AT_READ_MAX)` 字节;更大负载需上层循环多次调用。
|
||
*
|
||
* @param[out] u8_recvBuf 接收缓冲。
|
||
* @param[in] u16_len 期望拷贝的最大字节数(同时限制单次 QIRD 请求长度)。
|
||
*
|
||
* @return 本次实际拷贝的应用负载字节数;无数据或解析失败返回 0。
|
||
*
|
||
* @note 仅在 `EC200A_TCP_USE_TRANSPARENT_MODE == 0` 时编译。
|
||
*/
|
||
U16_T u16_ec200a_tcp_app_recv(U8_T *u8_recvBuf, U16_T u16_len)
|
||
{
|
||
U8_T sock_id = s_4g_ctrl.u8_tcp_socket_id;
|
||
U8_T use_tls = s_4g_ctrl.s_4g_para.use_tls;
|
||
U8_T cmd[48];
|
||
U8_T rb[EC200A_QIRD_RSP_MAX];
|
||
U16_T want;
|
||
U16_T rb_len = 0;
|
||
U16_T i;
|
||
U16_T paylen;
|
||
const char *recv_at = use_tls ? "QSSLRECV" : "QIRD";
|
||
const char *empty_tag = use_tls ? "+QSSLRECV: 0\r\n" : "+QIRD: 0\r\n";
|
||
|
||
if (u8_recvBuf == NULL || u16_len == 0U) {
|
||
return 0;
|
||
}
|
||
|
||
want = u16_len;
|
||
if (want > EC200A_TCP_APP_RW_MAX) {
|
||
want = EC200A_TCP_APP_RW_MAX;
|
||
}
|
||
if (want > EC200A_QIRD_AT_READ_MAX) {
|
||
want = EC200A_QIRD_AT_READ_MAX;
|
||
}
|
||
|
||
snprintf((char *)cmd, sizeof(cmd), "AT+%s=%u,%u\r\n", recv_at, (unsigned)sock_id, (unsigned)want);
|
||
memset(rb, 0, sizeof(rb));
|
||
v_4g_send_data(ID_4G, cmd, (U16_T)strlen((char *)cmd));
|
||
|
||
for (i = 0; i < 500U; i++) {
|
||
mSleep(5);
|
||
if (rb_len + 2U < sizeof(rb)) {
|
||
rb_len += u16_4g_uart_rx_modem(rb + rb_len, (U16_T)(sizeof(rb) - 2U - rb_len));
|
||
rb[rb_len < sizeof(rb) ? rb_len : sizeof(rb) - 1U] = '\0';
|
||
}
|
||
if (ec200a_buf_contains(rb, rb_len, "+CME ERROR:") || ec200a_buf_contains(rb, rb_len, "\r\nERROR\r\n")) {
|
||
break;
|
||
}
|
||
if (use_tls) {
|
||
if (ec200a_qsslrecv_rx_complete(rb, rb_len)) {
|
||
break;
|
||
}
|
||
} else if (ec200a_qird_rx_complete(rb, rb_len)) {
|
||
break;
|
||
}
|
||
if (rb_len + 32U >= sizeof(rb)) {
|
||
EC200A_4G_LOG("4G %s: rsp buf overflow risk len=%u\r\n", recv_at, (unsigned)rb_len);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (use_tls) {
|
||
paylen = ec200a_parse_qsslrecv_payload(rb, rb_len, u8_recvBuf, u16_len);
|
||
} else {
|
||
paylen = ec200a_parse_qird_payload(rb, rb_len, u8_recvBuf, u16_len);
|
||
}
|
||
|
||
if (paylen == 0U && EC200A_QIRD_EMPTY_POLL_DELAY_MS > 0U &&
|
||
(use_tls ? ec200a_qsslrecv_rx_complete(rb, rb_len) : ec200a_qird_rx_complete(rb, rb_len)) &&
|
||
!ec200a_buf_contains(rb, rb_len, "+CME ERROR:") &&
|
||
!ec200a_buf_contains(rb, rb_len, "\r\nERROR\r\n") &&
|
||
ec200a_buf_contains(rb, rb_len, empty_tag)) {
|
||
mSleep(EC200A_QIRD_EMPTY_POLL_DELAY_MS);
|
||
}
|
||
|
||
if (paylen == 0U) {
|
||
int complete = use_tls ? ec200a_qsslrecv_rx_complete(rb, rb_len)
|
||
: ec200a_qird_rx_complete(rb, rb_len);
|
||
int err = ec200a_buf_contains(rb, rb_len, "+CME ERROR:") ||
|
||
ec200a_buf_contains(rb, rb_len, "\r\nERROR\r\n");
|
||
if (!complete || err) {
|
||
v_4g_uart_rx_discard_fifo();
|
||
}
|
||
}
|
||
|
||
return paylen;
|
||
}
|
||
|
||
/** Quectel TCP/IP:AT+QISDE 控制 QISEND 数据是否在 UART 回显;失败不阻断建链(旧固件或无此命令时可能 ERROR) */
|
||
static S8_T s8_ec200a_qisde_apply(void)
|
||
{
|
||
U8_T u8_buf[24];
|
||
int n;
|
||
|
||
n = snprintf((char *)u8_buf, sizeof(u8_buf), "AT+QISDE=%u\r\n", (unsigned)EC200A_QISDE_ECHO);
|
||
if (n <= 0 || n >= (int)sizeof(u8_buf)) {
|
||
return -1;
|
||
}
|
||
return s8_4g_Send_AT_Cmd(u8_buf, (U16_T)n, NULL, 3, 200);
|
||
}
|
||
|
||
#endif /* !EC200A_TCP_USE_TRANSPARENT_MODE */
|
||
|
||
S8_T s8_ec200a_tcp_socket_close(void);
|
||
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - AT+QIOPEN 打开 Socket 服务
|
||
* 该命令用于打开 Socket 服务。服务类型可通过<service_type>指定,数据访问模式(缓存模式,直吐
|
||
* 模式和透传模式)可通过<access_mode>配置,URC +QIOPEN 将指示 Socket 服务是否成功打开。
|
||
* AT+QIOPEN=1,0,"TCP","220.180.239.212",8009,0,2 //场景为 1,<connectID>为 0。执行 AT+QIOPEN
|
||
* 之前,Host 需使用 AT+QIACT 来激活场景。
|
||
* CONNECT //连接成功,等待 URC 响应结果 CONNECT 建议
|
||
* 等待 150 秒。若 150 秒内 URC 无响应,Host 可通过 AT+QICLOSE 断开 Socket 连接。
|
||
*
|
||
* @return S8_T - -1 未成功 >0成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
static S8_T s8_ec200a_qiopen()
|
||
{
|
||
U8_T u8_sendBuf[64] = {0};
|
||
U8_T u8_ack[] = "CONNECT";
|
||
U8_T rcvBuf[BS_RECV_BUF_LEN] = {0};
|
||
int cmd_n;
|
||
|
||
if(s_4g_ctrl.s_4g_para.socketA_IP == NULL)
|
||
return -1;
|
||
|
||
/* 建链前先关残留 Socket(异常复位后模组侧可能仍占着 SSL 连接,导致平台 404 拒绝 WS 升级) */
|
||
(void)s8_ec200a_tcp_socket_close();
|
||
|
||
/* 根据配置选择普通 TCP 还是模块内置 TLS 通道;
|
||
* TLS:AT+QSSLOPEN=<PDP>,<SSL_ctx>,<clientID>,"host",port,<access_mode>
|
||
* TCP:AT+QIOPEN=1,0,"TCP","host",port,0,<access_mode>
|
||
*/
|
||
if (s_4g_ctrl.s_4g_para.use_tls)
|
||
{
|
||
cmd_n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
|
||
"AT+QSSLOPEN=%d,%d,%d,\"%s\",%d,%d\r\n",
|
||
EC200A_SSL_PDP_CTX_ID,
|
||
EC200A_SSL_CTX_ID,
|
||
EC200A_SSL_CLIENT_ID,
|
||
s_4g_ctrl.s_4g_para.socketA_IP,
|
||
s_4g_ctrl.s_4g_para.socketA_port,
|
||
EC200A_TCP_QIOPEN_ACCESS_MODE);
|
||
}
|
||
else
|
||
{
|
||
cmd_n = snprintf((char *)u8_sendBuf, sizeof(u8_sendBuf),
|
||
"AT+QIOPEN=%u,0,\"TCP\",\"%s\",%d,0,%d\r\n",
|
||
(unsigned)EC200A_TCP_PDP_CTX_ID,
|
||
s_4g_ctrl.s_4g_para.socketA_IP,s_4g_ctrl.s_4g_para.socketA_port,
|
||
EC200A_TCP_QIOPEN_ACCESS_MODE);
|
||
}
|
||
|
||
if (cmd_n <= 0 || cmd_n >= (int)sizeof(u8_sendBuf)) {
|
||
return -1;
|
||
}
|
||
|
||
#if EC200A_TCP_USE_TRANSPARENT_MODE
|
||
return s8_4g_Send_Recv_Cmd(u8_sendBuf, (U16_T)cmd_n, rcvBuf, BS_RECV_BUF_LEN, u8_ack, 10, 1000);
|
||
#else
|
||
return s8_ec200a_qiopen_accumulate(u8_sendBuf, (U16_T)cmd_n);
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* 关闭业务 TCP/SSL Socket(不拆 PDP)。QICLOSE 失败(如已关)不视为致命错误。
|
||
*/
|
||
S8_T s8_ec200a_tcp_socket_close(void)
|
||
{
|
||
U8_T cmd[48];
|
||
int n;
|
||
|
||
if (s_4g_ctrl.s_4g_para.socketA_IP == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
if (s_4g_ctrl.s_4g_para.use_tls) {
|
||
n = snprintf((char *)cmd, sizeof(cmd), "AT+QSSLCLOSE=%u\r\n",
|
||
(unsigned)s_4g_ctrl.u8_tcp_socket_id);
|
||
} else {
|
||
n = snprintf((char *)cmd, sizeof(cmd), "AT+QICLOSE=%u\r\n",
|
||
(unsigned)s_4g_ctrl.u8_tcp_socket_id);
|
||
}
|
||
if (n <= 0 || n >= (int)sizeof(cmd)) {
|
||
return -1;
|
||
}
|
||
(void)s8_4g_Send_AT_Cmd(cmd, (U16_T)n, NULL, 3, 400);
|
||
mSleep(120);
|
||
v_4g_uart_rx_discard_fifo();
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* 业务 PDP 已就绪且 e_4g_init_step==STEP_INIT_OK:仅 QICLOSE/QSSLCLOSE 再 QIOPEN/QSSLOPEN,不重跑驻网/PDP。
|
||
* QICLOSE 失败(例如已关闭)不阻断后续 QIOPEN。
|
||
*/
|
||
S8_T s8_ec200a_tcp_socket_soft_reopen(void)
|
||
{
|
||
S8_T r;
|
||
|
||
if (s_4g_ctrl.e_4g_init_step != STEP_INIT_OK) {
|
||
return -1;
|
||
}
|
||
if (s_4g_ctrl.s_4g_para.socketA_IP == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
(void)s8_ec200a_tcp_socket_close();
|
||
(void)s8_ec200a_qisde_apply();
|
||
r = s8_ec200a_qiopen();
|
||
return r;
|
||
}
|
||
|
||
static S8_T s8_ec200a_qpowd()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+QPOWD=1\r\n";
|
||
|
||
S8_T s8_ret = s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 2, 50);
|
||
if(s8_ret >= 0)
|
||
mSleep(12*1000);
|
||
|
||
return s8_ret;
|
||
}
|
||
|
||
static S8_T s8_ec200a_cfun()
|
||
{
|
||
U8_T u8_sendBuf[] = "AT+CFUN=0\r\n";
|
||
U8_T u8_sendBuf2[] = "AT+CFUN=1\r\n";
|
||
S8_T s8_ret;
|
||
|
||
// s8_ret = s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 5, 500);
|
||
// if(s8_ret >= 0)
|
||
{
|
||
s8_ret = s8_4g_Send_AT_Cmd(u8_sendBuf2, sizeof(u8_sendBuf2) - 1, NULL, 5, 500);
|
||
}
|
||
return s8_ret;
|
||
}
|
||
#endif
|
||
|
||
U8_T u8_ec200a_modem_tcp_stack_ok(void)
|
||
{
|
||
return (s_4g_ctrl.e_4g_init_step == STEP_INIT_OK) ? 1U : 0U;
|
||
}
|
||
|
||
|
||
#if 1 //切换到指令模式
|
||
/**
|
||
* ************************************************************************
|
||
* @brief -切换到指令模式
|
||
*
|
||
* @param[in]
|
||
* @param[out] -
|
||
*
|
||
* @author LQ
|
||
* @date 2025-08-22
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_work_mode_cmd() // 切换到指令模式
|
||
{
|
||
U8_T u8_sendAT[] = "AT\r\n";
|
||
U8_T u8_sendBuf[] = "+++";
|
||
|
||
s8_4g_Send_AT_Cmd(u8_sendBuf, sizeof(u8_sendBuf) - 1, NULL, 3, 500);
|
||
|
||
return s8_4g_Send_AT_Cmd(u8_sendAT, sizeof(u8_sendAT) - 1, NULL, 2, 50);
|
||
}
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 重启4G模块 发送关机--开机 延时足够时间
|
||
*
|
||
* @param[in] u8_flag - 0 仅开机 1先关机 再开机
|
||
*
|
||
* @return S8_T -
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-25
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_cat_reboot(U8_T u8_flag)
|
||
{
|
||
S8_T s8_at_res,s8_ret = -1;
|
||
U8_T u8_cnt = 20;
|
||
|
||
//printf("cat 4g reboot %d",u8_flag);
|
||
if(u8_flag)
|
||
{
|
||
while (u8_cnt)
|
||
{
|
||
s8_at_res = s8_work_mode_cmd();
|
||
if(s8_at_res >= 0)
|
||
{
|
||
s8_at_res = s8_ec200a_qpowd();
|
||
if(s8_at_res >= 0)
|
||
{
|
||
s8_ret = 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
PWR_4G(0); //执行开机
|
||
mSleep(2000);
|
||
PWR_4G(1);
|
||
u8_cnt --;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//s8_ret = s8_ec200a_qpowd();
|
||
s8_ret = 1;
|
||
|
||
PWR_4G(0);
|
||
mSleep(3000);
|
||
PWR_4G(1);
|
||
}
|
||
|
||
return s8_ret;
|
||
}
|
||
#endif
|
||
|
||
#if 1 //对外接口函数
|
||
/**
|
||
* ************************************************************************
|
||
* @brief -4G 模块初始化函数 包括设置4G联网参数
|
||
*
|
||
* @param[in] s_4g_para - 4G联网参数配置
|
||
*
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-22
|
||
* ************************************************************************
|
||
*/
|
||
void v_4g_data_init(S_4G_CONFIG_PARA *s_4g_para)
|
||
{
|
||
memset(&s_4g_ctrl , 0 ,sizeof(S_4G_CTRL_DATA));
|
||
|
||
memcpy(&s_4g_ctrl.s_4g_para ,s_4g_para,sizeof(S_4G_CONFIG_PARA));
|
||
|
||
s8_cat_reboot(0);
|
||
}
|
||
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 4G 模块初始化函数
|
||
*
|
||
*
|
||
* @return S8_T - 0配网成功 非0 配网中(1-15配网步骤)
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-21
|
||
* ************************************************************************
|
||
*/
|
||
S8_T s8_ec200a_4g_init()
|
||
{
|
||
S8_T s8_ret = 1,s8_at_res;
|
||
|
||
// static U16_T u16_cnt = 0;
|
||
|
||
// if(u16_cnt++ < 10)
|
||
// return s8_ret;
|
||
// u16_cnt = 0;
|
||
|
||
// char testmag[] = "AT+QIOPEN=1,0,\"TCP\",\"182.92.157.86\",8088,0,2\r\nCONNECT";
|
||
// U8_T u8_ack[] = "CONNECT";
|
||
|
||
// printf("test %d\r\n",s8_FindAck(testmag, u8_ack));
|
||
// return s8_ret;
|
||
|
||
//printf("4g config\r\n");
|
||
/*
|
||
使用 TCP/IP AT 命令的流程
|
||
通过 TCP/IP AT 命令,Host 可以配置 PDP 场景、激活/去激活 PDP 场景、打开/关闭 Socket 服务,并
|
||
通过 Socket 服务发送/接收数据。TCP/IP AT 命令的使用流程如下图所示:
|
||
(1)建议开关机流程:
|
||
1 .关机:发送AT+QPOWD,等待12 s以上,直接断电。
|
||
2. 开机:先使Power Key按键处于低电平状态,等待2 s后,再拉高Power Ke y
|
||
(2)查询(U)SIM卡状态:
|
||
执行AT+CPIN,如果2 0 s后命令"AT+CPIN"无法识别(U)SIM卡状态,建议重启模块
|
||
(3)PS 业务:
|
||
1. 当 AT+CGREG?/AT+CEREG? 结果中的<stat>等于 1 或者5,表示模块已在UMTS/LTE网络注册上PS业务
|
||
2. 等待60 s后,不管是否注册上PS业务,都进入下一个流程
|
||
|
||
AT+CGREG PS 域网络注册状态
|
||
AT+CEREG EPS 网络注册状态
|
||
(4)配置PDP Context激活参数:
|
||
1 . 通过 AT+QICSGP 命令配置APN,U ser Name,Password 和Auth Type
|
||
2. 通过 AT+CGQMIN/AT+CGEQMIN/ AT+CGQREQ/AT+CGEQREQ 配置 QoS 参数
|
||
|
||
AT+QICSGP=<contextID>[,<context_type>,<APN>[,<username>,<password>)[,<authentication>[,<CDMA_pwd>]]]]
|
||
AT+QICSGP=1,1,"UNINET","","",1 //配置场景 1,APN 配置为"UNINET"(中国联通)。
|
||
|
||
AT+CGQMIN 服务质量参数(最低可接受的)
|
||
AT+CGEQMIN 3G 服务质量参数(最低可接受)
|
||
AT+CGQREQ 服务质量参数(请求的)
|
||
AT+CGEQREQ 3G 服务质量参数(请求的)
|
||
(5)激活PDP Context:
|
||
1. 通过 AT+QIACT=<contextID>激活PDP Context
|
||
2 . 通过 AT+QIACT? 查询PDP Context IP地址
|
||
注意事项:
|
||
a) 以上命令必须按顺序执行
|
||
b) 如果等待150 s后,AT+QIACT没有响应,建议重启模块.
|
||
c) 如果连续3次反激活PDP Context失败,建议重启模块
|
||
3. 反激活PDP Context:如果等待40s后AT+QIDEACT没有响应,建议重启模块
|
||
|
||
AT+QIACT 激活 PDP 场景
|
||
在用AT+QIACT激活PDP场景前,需要使用AT+QICSGP配置场景。场景激活后,可以通过AT+QIACT?查询 IP 地址。
|
||
虽然<contextID>的范围为 1~15,但是模块最多仅可同时激活 3 路 PDP 场景。受网络状态影响,执行
|
||
AT+QIACT 后,等待返回结果 OK 或者 ERROR 的最大时间为 150 秒,在结果尚未返回之前,无法执行任何 AT 命令。
|
||
AT+QIDEACT 去激活 PDP 场景
|
||
该命令用于去激活特定场景,断开在这个场景内建立的所有 TCP/IP 连接。受网络状态影响,执行
|
||
AT+QIDEACT 后,等待返回结果 OK 或者 ERROR 的最大时间为 40 秒。在结果尚未返回之前,无法执行任何 AT 命令
|
||
(6)打开连接:
|
||
1. 如果等待150 s,没有收到AT+QIOPEN的响应,请关闭连接。
|
||
2 .如果连续5次打开连接失败,先反激活PDP Context,然后重新激活PDP Context并再次打开连接。
|
||
(7)TCP连接的维护和检测:
|
||
1. 通过 AT+QISEND=<connectID>[,<send_length>] 发送数据。建议定期向远程端发送小数据包以维护和检测TCP连接。
|
||
2. 通过 AT+QISEND=<connectID>,0 查询ACK的发送。如果在2分钟后(每5 s查询一次,一共查询2 4次),对方没有收到数据,
|
||
说明TCP连接可能出现异常,请执行AT+QICLOSE命令关闭这个TCP连接,然后通过AT+QIOPEN重新建立TCP连接。
|
||
*/
|
||
|
||
switch (s_4g_ctrl.e_4g_init_step)
|
||
{
|
||
case STEP_SWCHTO_CMD_MODE: //切换到指令模式
|
||
{
|
||
s8_at_res = s8_work_mode_cmd();
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_QUERY_SIM_STATE; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_QUERY_SIM_STATE: //查询SIM卡状态
|
||
{
|
||
s8_at_res = s8_ec200a_cpin(); //查询SIM卡状态
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_QUERY_CCID; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_QUERY_CCID: //查询sim卡号
|
||
{
|
||
s8_at_res = s8_ec200a_qccid(); //获取SIM卡号
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_CFUN; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
|
||
case STEP_SET_CFUN: //设置功能模式
|
||
{
|
||
s8_at_res = s8_ec200a_cfun(); //设置功能模式
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_CGREG; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
}
|
||
case STEP_SET_CGREG: //设置PS域 网络注册状态
|
||
{
|
||
s8_at_res = s8_ec200a_set_cgreg(); //
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_QUERY_CGREG; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
}
|
||
case STEP_QUERY_CGREG: //查询PS域 网络注册状态
|
||
{
|
||
s8_at_res = s8_ec200a_cgreg(); //查询PS域 网络注册状态
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_QICSGP_APN; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_SET_QICSGP_APN: //设置TCP场景参数 设置APN
|
||
{
|
||
s8_at_res = s8_ec200a_qicsgp(); //设置TCP场景参数 设置APN
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_CGQMIN; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_SET_CGQMIN: //设置服务质量参数(最低可接受的)
|
||
{
|
||
s8_at_res = s8_ec200a_cgqmin(); //设置服务质量参数(最低可接受的)
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_CGQREQ; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_SET_CGQREQ: //设置服务质量参数(请求的)
|
||
{
|
||
s8_at_res = s8_ec200a_cgqreq(); //设置服务质量参数(请求的)
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_QUERY_CSQ; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_QUERY_CSQ: //查询信号强度
|
||
{
|
||
s8_at_res = s8_ec200a_csq(); //查询信号强度
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_QIACT; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_SET_QIACT: //激活PDP场景
|
||
{
|
||
s8_at_res = s8_ec200a_qiact(); //激活PDP场景
|
||
if(s8_at_res >= 0){
|
||
/* 若启用 TLS,则在激活 PDP 后先做 SSL 配置,否则直接进入打开 socket 步骤 */
|
||
if (s_4g_ctrl.s_4g_para.use_tls) {
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_SSL_CFG;
|
||
} else {
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_SOCKET_AND_OPEN;
|
||
}
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
case STEP_SET_SSL_CFG: //TLS 配置
|
||
{
|
||
s8_at_res = s8_ec200a_ssl_cfg();
|
||
if (s8_at_res >= 0) {
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_SOCKET_AND_OPEN; //TLS 配置完成,进入打开 socket
|
||
s_4g_ctrl.u8_out_cnt = 0;
|
||
}
|
||
break;
|
||
}
|
||
case STEP_SET_SOCKET_AND_OPEN: //设置socket 参数并打开
|
||
{
|
||
#if !EC200A_TCP_USE_TRANSPARENT_MODE
|
||
(void)s8_ec200a_qisde_apply(); /* AT+QISDE:关闭 QISEND 数据 UART 回显(失败仍尝试建链) */
|
||
#endif
|
||
s8_at_res = s8_ec200a_qiopen(); //设置socket 参数并打开
|
||
if(s8_at_res >= 0){
|
||
s_4g_ctrl.e_4g_init_step = STEP_SAVE; //状态跳转
|
||
s_4g_ctrl.u8_out_cnt = 0; //超时计数清空
|
||
}
|
||
break;
|
||
}
|
||
|
||
case STEP_SAVE: //保存当前设置并重启
|
||
case STEP_INIT_OK: //设置完成
|
||
{
|
||
s_4g_ctrl.e_4g_init_step = STEP_INIT_OK;
|
||
s8_ret = 0;
|
||
return s8_ret;
|
||
break;
|
||
}
|
||
case STEP_INIT_DEBUG: //调试模式 等待shell命令配置
|
||
{
|
||
mSleep(1000);
|
||
return s8_ret;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
mSleep(50);
|
||
|
||
|
||
//4G模块配置超时 重新初始化
|
||
if(s_4g_ctrl.u8_out_cnt == 0)
|
||
{
|
||
s_4g_ctrl.u32_outtime = get_current_seconds(); //超时计数清空
|
||
s_4g_ctrl.u8_out_cnt++;
|
||
}
|
||
else
|
||
{
|
||
s_4g_ctrl.u8_out_cnt = u32_safe_seconds_since(s_4g_ctrl.u32_outtime);
|
||
#if TEST_BS_PRINTF_FLAG //调试打印
|
||
printf("test out time (%d)[%d]\r\n",s_4g_ctrl.e_4g_init_step,s_4g_ctrl.u8_out_cnt);
|
||
#endif
|
||
s8_ec200a_csq();
|
||
s8_ec200a_gmr();
|
||
}
|
||
|
||
if(s_4g_ctrl.u8_out_cnt > 150)
|
||
{
|
||
EC200A_4G_LOG("4G cfg TO, retry state=%d\r\n",s_4g_ctrl.e_4g_init_step);
|
||
|
||
s_4g_ctrl.u8_out_cnt = 0;
|
||
s_4g_ctrl.e_4g_init_step = STEP_SWCHTO_CMD_MODE;
|
||
|
||
s8_cat_reboot(1);
|
||
}
|
||
else if(s_4g_ctrl.e_4g_init_step == STEP_QUERY_CGREG &&
|
||
s_4g_ctrl.u8_out_cnt > 60)
|
||
{
|
||
EC200A_4G_LOG("4G PS reg TO, force jump\r\n");
|
||
s_4g_ctrl.u8_out_cnt = 0;
|
||
s_4g_ctrl.e_4g_init_step = STEP_SET_QICSGP_APN;
|
||
|
||
}
|
||
|
||
return s8_ret;
|
||
}
|
||
|
||
/**
|
||
* ************************************************************************
|
||
* @brief - 获取sim卡号
|
||
*
|
||
* @param[in] u8_data - 卡号数组
|
||
* @param[in] buf_len - 数组缓存区大小
|
||
*
|
||
* @return U8_T - 0失败 1成功
|
||
*
|
||
* @author jiankalka
|
||
* @date 2025-11-26
|
||
* ************************************************************************
|
||
*/
|
||
U8_T u8_get_sim_card(U8_T *u8_data ,U8_T buf_len)
|
||
{
|
||
memset(u8_data , 0 , buf_len);
|
||
strncpy((char *)u8_data ,(char *)s_4g_ctrl.u8_Sim_Card, buf_len );
|
||
return 1;
|
||
}
|
||
U8_T u8_UAT_check_signal_quality()
|
||
{
|
||
return s_4g_ctrl.u8_csq;
|
||
}
|
||
#endif
|
||
|
||
|
||
#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;
|
||
void cat1Func(int type , int on,char *mag)
|
||
{
|
||
v_shell_print("cat1Func %d %d\r\n",type,on);
|
||
|
||
if(type == 0)
|
||
{
|
||
if(on)
|
||
s_4g_ctrl.e_4g_init_step = STEP_INIT_DEBUG;
|
||
else
|
||
s_4g_ctrl.e_4g_init_step = STEP_SWCHTO_CMD_MODE;
|
||
|
||
}
|
||
else
|
||
{
|
||
if(mag == NULL)
|
||
v_shell_print("cat1Func mag is null\r\n");
|
||
else
|
||
{
|
||
char test_mag[64] = {0};
|
||
snprintf(test_mag,64,"%s\r\n",mag);
|
||
s8_4g_Send_AT_Cmd((U8_T *)test_mag,strlen(test_mag), NULL, 2, 100);
|
||
|
||
v_shell_print("cat1Func mag is send :(%d) %s\r\n",strlen(test_mag),test_mag);
|
||
|
||
|
||
}
|
||
}
|
||
}
|
||
// 注册
|
||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), catCtrl,cat1Func, myshlle-- 4 g cat test);
|
||
|
||
#endif
|
||
#endif /* EC200A_4G_EN */ |