#include "ec20tcp.h" #include #include "syslib.h" //#define STR2(R) STR1(R) /******************************************************************************** * @file ec20tcp.c * @author 晏诚科技 Mr.Wang * @version V1.0.0 * @date 11-Dec-2018 * @brief 提供Quectel模块EC20关于TCP/IP硬件驱动程序 ****************************************************************************** * @attention * 约定基本名词如下: * contextID:链路ID connetcID:通道ID channal:连接通道 * EC20模块链路ID范围1~16,每一个链路ID都会对应一个本地IP; 通道ID范围0~11。 * 每一个链路ID可以有12个通道ID。 * 本驱动强制规定,TCP/IP协议只用一个链路1D(即contextID=1)用于TCP链路。 * 通过不同的通道ID我们可以多台不同IP和端口的服务器。 * 结构体ChannalP_s封装了每一路连接通道的参数 * @use: * 先调用Tcp_PDP_Init()初始化TCP链路,接着调用Tcp_Channal_Init打开Socket,最后通过Tcp_SendData发送TCP数据 *******************************************************************************/ /***************************************** *内部使用的常变量定义 ****************************************/ #define TCP_CMDPACK_LEN 128 //EC20 TCP相关命令字符串的最大长度 /******************************************************** ec20模块TCP/IP相关AT指令处理 *********************************************************/ enum eTcpCmdNum { OPENSOCKET =0, CLOSESOCKET =1, QUERYSOCKET =2, TCPSENDCMD =3, TCPSENDBUF =4 } ; //枚举ec20模块TCP相关指令 volatile EC20_CMD_DATA_s sTcpCmd[5]= { // cmdNum cmdStr, timeout(100ms), trueStr, trueOffset falseStr revResult rtyNum {OPENSOCKET, "AT+QIOPEN="STR2(TCP_CONTEXTID)",%d,\"TCP\",\"%s\",%d,%d,1\r\n", (20*10), "+QIOPEN:", -1,"ERROR", TIMEOUT, 1 }, //打开socket 手册回码等待150S {CLOSESOCKET, "AT+QICLOSE=%d,10\r\n" , (10*10), "OK" , -1, "ERROR", TIMEOUT, 2 }, //关闭socket {QUERYSOCKET, "AT+QISTATE=1,%d\r\n" , 5, "+QISTATE:" , -1, "ERROR", TIMEOUT, 2 }, //查询socket状态 {TCPSENDCMD, "AT+QISEND=%d,%d\r\n" , (2*10), ">" , -1, "ERROR", TIMEOUT, 1 }, //通过socket发送数据 {TCPSENDBUF, "%s" , (2*10), "SEND OK" , -1, "ERROR", TIMEOUT, 1 } //SOCKET发送负载数据 } ; //EC20模块TCP相关指令的EC20_CMD_DATA_s结构体类型参数 /************************************************************************************************** * 名 称: static const char *TcpCmdNumToString(enum eTcpCmdNum result) * 功能说明: 输出枚举成员名的字符串指针。 * 入口参数: eTcpCmdNum类型的枚举 * 出口参数: 为枚举的成员名字符串指针 **************************************************************************************************/ static inline const char *TcpCmdNumToString(enum eTcpCmdNum result) { switch (result) { ENUM_CHIP_TYPE_CASE(OPENSOCKET) ENUM_CHIP_TYPE_CASE(CLOSESOCKET) ENUM_CHIP_TYPE_CASE(QUERYSOCKET) ENUM_CHIP_TYPE_CASE(TCPSENDCMD) ENUM_CHIP_TYPE_CASE(TCPSENDBUF) } return "无此命令"; } /************************************************************************************************** * 名 称: RunResult Tcp_PDP_Init( void ) * 功能说明: 初始化TCP链路 * 出口参数: * @param1 runResult RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Tcp_PDP_Init( void ) { RunResult runResult = TIMEOUT ; uint8_t *tcpLocalIp = portMalloc(MAX_IP_LEN) ; runResult = Query_Context( TCP_CONTEXTID, tcpLocalIp ) ; //查询TCP_CONTEXTID是否激活 if( RUNOK == runResult ) /*TCP_CONTEXTID已激活*/ //去激活->再次激活 { // runResult = Deact_Context(TCP_CONTEXTID) ; // if( RUNOK != runResult ) /*TCP_CONTEXTID去激活失败*/ //直接返回错误 // { // return RUNERR ; // } return RUNOK ; } runResult = ActivePDP(TCP_CONTEXTID, tcpLocalIp) ; if( RUNOK == runResult ) { AppLogPrintf("TCP本地IP:%s", tcpLocalIp) ; } portFree(tcpLocalIp) ; return(runResult) ; } /************************************************************************************************** * 名 称: RunResult Tcp_Channal_Init( ChannalP_s *channal ) * 功能说明: TCP连接通道初始化 * 入口参数: * @param1 *channal:ChannalP_s结构体变量,存放TCP连接通道相关参数 * 出口参数: * @param1 runResult RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Tcp_Channal_Init( ChannalP_s *channal ) { RunResult runResult = TIMEOUT ; runResult = Close_Socket(channal->connectId) ; if( runResult != RUNOK ) //关闭SOCKET失败直接返回失败状态 return(runResult) ; runResult = Open_Socket(channal->connectId, channal->serverIP, channal->serverPort, channal->localPort) ; if( RUNOK == runResult ) Query_Socket(channal->connectId) ; return(runResult) ; } /************************************************************************************************** * 名 称: RunResult Tcp_SendData(ChannalP_s *channal, uint8_t *sendBuf, uint16_t sendLen) * 功能说明: 通过连接通道channal将长度为sendLen的sendBuf缓冲区数据发送到服务器 * 入口参数: * @param1 *channal:ChannalP_s结构体变量,存放TCP连接通道相关参数 * @param2 *sendBuf:发送数据缓冲区地址 * @param3 sendLen:发送数据的长度 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Tcp_SendData(ChannalP_s *channal, uint8_t *sendBuf, uint16_t sendLen) { if( sendLen <=0 ) { return (RUNOK) ; } uint8_t times = 0, enableSendData = 0 ; uint16_t revTimes = 0 ; RunResult runStatus = TIMEOUT ; Ec20AtBufReset() ; UARTx_SendString(EC20_UART, (uint8_t*)sTcpCmd[TCPSENDCMD].cmdStr, channal->connectId, sendLen ); while( times++ < 70 ) { Wait_For_Nms(20) ; if( NULL != strchr((const char*)ec20AtBuf, '>') ) { enableSendData = 1 ; break ; } } if( enableSendData == 1 ) //已经成功收到‘>’,串口可以发送TCP需要上传的数据 { UARTx_SendData(EC20_UART, (char*)sendBuf, sendLen) ; while( revTimes++ < sTcpCmd[TCPSENDBUF].timeout ) { Wait_For_Nms(5); sTcpCmd[TCPSENDBUF].trueOffset = kmp(ec20AtBuf, sTcpCmd[TCPSENDBUF].trueStr) ; if( sTcpCmd[TCPSENDBUF].trueOffset >= 0) { runStatus = RUNOK ; DebugLogPrintf("%s %s", TcpCmdNumToString(TCPSENDBUF), RunResultToString(runStatus)) ; break ; } else if( kmp(ec20AtBuf, sTcpCmd[TCPSENDBUF].falseStr) >= 0) { runStatus = RUNERR ; ErrorLogPrintf("%s %s %s", TcpCmdNumToString(TCPSENDBUF), RunResultToString(runStatus), ec20AtBuf ) ; break ; } } //EC20_SendTcpCmd( TCPSENDBUF, NULL ) ; } else { ErrorLogPrintf("%s %s %s %s", TcpCmdNumToString(TCPSENDCMD), RunResultToString(runStatus), "未收到\">\"", ec20AtBuf ) ; } return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_SendTcpCmd( uint8_t cmdNum, char *format,... ) * 功能说明: MCU串口向EC20发送Tcp相关命令 * 入口参数: * @param1 cmdNum EC20_CMD_DATA_s中cmdNum成员命令编号 * @param2 char *format,... 可变参变量 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_SendTcpCmd( uint8_t cmdNum, char *format,... ) { uint8_t revTimes = 0 ; RunResult status = TIMEOUT ; uint8_t retryTimes = sTcpCmd[cmdNum].rtyNum ; char *cmdPack = NULL ; format = sTcpCmd[cmdNum].cmdStr ; cmdPack = portMalloc(TCP_CMDPACK_LEN*sizeof(uint8_t)) ; va_list ap; va_start (ap, format); int outLen = vsnprintf(cmdPack, TCP_CMDPACK_LEN, (const char*)format, ap); //vsprintf (temp, cmd, ap); //到此为止,所有的参数情况已经汇总到temp了 if((outLen<=0)||( outLen > TCP_CMDPACK_LEN)) { ErrorLogPrintf("Tcp cmdPack 溢出!--增加TCP_CMDPACK_LEN数值。") ; status = RUNERR ; goto tcpCmdOut ; } while(retryTimes--) { Ec20AtBufReset() ; revTimes = 0 ; UARTx_SendData(EC20_UART, cmdPack, outLen ) ; //DMA发送 while( revTimes++ < sTcpCmd[cmdNum].timeout ) { Wait_For_Nms(100) ; sTcpCmd[cmdNum].trueOffset = kmp(ec20AtBuf, sTcpCmd[cmdNum].trueStr) ; if( sTcpCmd[cmdNum].trueOffset >= 0) { status = RUNOK ; goto tcpCmdOut ; } else if( kmp(ec20AtBuf, sTcpCmd[cmdNum].falseStr) >= 0) { status = RUNERR ; goto tcpCmdOut ; } } Wait_For_Nms( 1000 ) ; } tcpCmdOut: portFree(cmdPack) ; va_end(ap) ; DebugLogPrintf("%s %s", TcpCmdNumToString((enum eTcpCmdNum)cmdNum), RunResultToString(status) ) ; return (status) ; } /************************************************************************************************** * 名 称: RunResult Open_Socket(uint8_t connectId, uint8_t *serverIp, uint16_t serverPortNum, uint16_t localPortNum ) * 功能说明: 打开TCP Socket * 入口参数: * @param1 connectId:TCP连接通道ID * @param2 *serverIp:存放服务器IP的指针 * @param3 serverPortNum:服务器端口号 * @param4 localPortNum:模块本地端口号 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Open_Socket(uint8_t connectId, uint8_t *serverIp, uint16_t serverPortNum, uint16_t localPortNum ) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendTcpCmd(OPENSOCKET, NULL, connectId, serverIp, serverPortNum, localPortNum ) ; if( RUNOK == runStatus ) { if( (ec20AtBuf[sTcpCmd[OPENSOCKET].trueOffset+9] == (0X30+connectId)) && (ec20AtBuf[sTcpCmd[OPENSOCKET].trueOffset+11] == '0') ) { runStatus = RUNOK ; } else { runStatus = RUNERR ; } } return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Close_Socket(uint8_t connectId) * 功能说明: 关闭TCP Socket * 入口参数: * @param1 connectId:TCP连接通道ID * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Close_Socket(uint8_t connectId) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendTcpCmd(CLOSESOCKET, NULL, connectId ) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Query_Socket(uint8_t connectId) * 功能说明: 查询TCP Socket状态 * 入口参数: * @param1 connectId:TCP连接通道ID * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Query_Socket(uint8_t connectId) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendTcpCmd(QUERYSOCKET, NULL, connectId ) ; if( RUNOK == runStatus ) { uint8_t serverPort[5] = {0} ; ChannalP_s *sReturnP ; sReturnP = portMalloc(sizeof(ChannalP_s)) ; sReturnP->connectId = ec20AtBuf[sTcpCmd[QUERYSOCKET].trueOffset+10] ; CopyValues(sReturnP->serverIP, (uint8_t*)&ec20AtBuf[sTcpCmd[QUERYSOCKET].trueOffset+19], ',', MAX_IP_LEN) ; CopyValues(serverPort, (uint8_t*)&ec20AtBuf[sTcpCmd[QUERYSOCKET].trueOffset+20+strlen((const char*)sReturnP->serverIP)], ',', 5) ; DebugLogPrintf("TCP Channal:%c 。 服务器IP:%s。 服务器端口:%s", sReturnP->connectId, sReturnP->serverIP, serverPort) ; portFree(sReturnP) ; } return (runStatus) ; } /************************************************************************************************** * 名 称: TcpUrcType TcpUrcHandle( char *recvBuf, uint16_t recvLen ) * 功能说明: TCP/IP协议中模块返回的一写URC分类处理 * 入口参数: * @param1 recvBuf 接收的数据 * @param2 recvLen 接收的数据长度 * 出口参数: * @param TcpUrcType TcpUrcType枚举类型变量,返回URC类型 **************************************************************************************************/ TcpUrcType TcpUrcHandle( char *recvBuf, uint16_t recvLen ) { TcpUrcType urcType = UNKNOWM ; if( kmp(recvBuf, "+QIURC: \"closed\"") > 0 ) { urcType = CLOSED ; } else if( kmp(recvBuf, "+QIURC: \"pdpdeact\"") > 0 ) { urcType = PDPDEACT ; } else if( kmp(recvBuf, "+QIURC: \"incoming\"") > 0 ) { urcType = INCOMING_FULL ; } else if( kmp(recvBuf, "+QIURC: \"incoming full\"") > 0 ) { urcType = INCOMING_CONT ; } else { urcType = UNKNOWM ; } return urcType ; }