#include "ec20net.h" #include "syslib.h" /******************************************************************************** * @file ec20.c * @author 晏诚科技 Mr.Wang * @version V1.0.0 * @date 11-Dec-2018 * @brief 提供Quectel模块EC20公共硬件驱动程序(激活去激活ContextID链路、EC20串口回调函数) ****************************************************************************** * @attention * 约定基本名词如下: * contextID:链路ID * EC20模块链路ID范围1~16,每一个链路ID都会对应一个本地IP; 通道ID范围0~11。 * 每一个链路ID可以单独相同/不同通信协议(TCP/IP、UDP、HTTP、FTP)。 * @use: * 先调用EC20_Uart_Init()初始化通讯串口,接着调用ActivePDP()激活相关链路,最后通过链路实现 * TCP\HTTTP\FTP通信 *******************************************************************************/ /***************************************** *ec20 NET 驱动内部使用常变量 ****************************************/ #define NET_CMDPACK_LEN 128 //EC20 NET相关命令字符串的最大长度 #define APN "CMIOT" //APN,使用的SIM运营商不同对应的APN可能不通 /******************************************************** ec20模块PDP连接相关AT指令处理 *********************************************************/ enum eNetCmdNum { QUERYICCID =0, SETCS =1, QUERYCS =2 , SETPS =3, QUERYPS =4, QUERYNETINFO =5, QUERYCSQ =6, COFCONTEXT =7, ACTCONTEXT =8, DEACTCONTEXT =9, QUERYCONTEXT =10 } ; //枚举ec20模块NET相关指令 volatile EC20_CMD_DATA_s sNetCmd[11]= { // cmdNum cmdStr, timeout(100ms), trueStr, trueOffset falseStr revResult rtyNum {QUERYICCID, "AT+QCCID\r\n", 5, "+QCCID" , -1, "ERROR", TIMEOUT, 3 }, {SETCS, "AT+CREG=2\r\n", 3, "OK" , -1, "ERROR", TIMEOUT, 2 }, {QUERYCS, "AT+CREG?\r\n", 3, "+CREG:" , -1, "ERROR", TIMEOUT, 2 }, {SETPS, "AT+CGREG=2\r\n", 3, "OK" , -1, "ERROR", TIMEOUT, 2 }, {QUERYPS, "AT+CGREG?\r\n", 3, "+CGREG:" , -1, "ERROR", TIMEOUT, 2 }, {QUERYNETINFO, "AT+QNWINFO\r\n", 3, "+QNWINFO:" , -1, "ERROR", TIMEOUT, 1 }, {QUERYCSQ, "AT+CSQ\r\n", 3, "+CSQ:" , -1, "ERROR", TIMEOUT, 2 }, {COFCONTEXT, "AT+QICSGP=%d,1,\"%s\",\"\",\"\",1\r\n", 3, "OK" , -1, "ERROR", TIMEOUT, 2 }, //配置TCP context {ACTCONTEXT, "AT+QIACT=%d\r\n", (150*10), "OK" , -1, "ERROR", TIMEOUT, 1 }, {DEACTCONTEXT, "AT+QIDEACT=%d\r\n", (40*10), "OK" , -1, "ERROR", TIMEOUT, 1 }, {QUERYCONTEXT, "AT+QIACT?\r\n", 3, "+QIACT:", -1, "ERROR", TIMEOUT, 2 } } ; //EC20模块NET相关指令的EC20_CMD_DATA_s结构体类型参数 /************************************************************************************************** * 名 称: static const char *NetCmdNumToString(enum eNetCmdNum result) * 功能说明: 输出枚举成员名的字符串指针。 * 入口参数: eNetCmdNum类型的枚举 * 出口参数: 为枚举的成员名字符串指针 **************************************************************************************************/ static inline const char *NetCmdNumToString(enum eNetCmdNum result) { switch (result) { ENUM_CHIP_TYPE_CASE(QUERYICCID) ENUM_CHIP_TYPE_CASE(SETCS) ENUM_CHIP_TYPE_CASE(QUERYCS) ENUM_CHIP_TYPE_CASE(SETPS) ENUM_CHIP_TYPE_CASE(QUERYPS) ENUM_CHIP_TYPE_CASE(QUERYNETINFO) ENUM_CHIP_TYPE_CASE(QUERYCSQ) ENUM_CHIP_TYPE_CASE(COFCONTEXT) ENUM_CHIP_TYPE_CASE(ACTCONTEXT) ENUM_CHIP_TYPE_CASE(DEACTCONTEXT) ENUM_CHIP_TYPE_CASE(QUERYCONTEXT) } ErrorLogPrintf("EC20 无效eNetCmdNum!") ; return "无此命令"; } /************************************************************************************************** * 名 称: RunResult EC20_SendNetCmd( uint8_t cmdNum, char *format,... ) * 功能说明: MCU串口向EC20发送PDP相关命令 * 入口参数: * @param1 cmdNum EC20_CMD_DATA_s中cmdNum成员命令编号 * @param2 char *format,... 可变参变量 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_SendNetCmd( uint8_t cmdNum, char *format,... ) { uint8_t revTimes = 0 ; RunResult status = TIMEOUT ; uint8_t retryTimes = sNetCmd[cmdNum].rtyNum ; char *cmdPack = NULL ; format = sNetCmd[cmdNum].cmdStr ; cmdPack = portMalloc(NET_CMDPACK_LEN*sizeof(uint8_t)) ; va_list ap; va_start (ap, format); int outLen = vsnprintf(cmdPack, NET_CMDPACK_LEN, (const char*)format, ap); //vsprintf (temp, cmd, ap); //到此为止,所有的参数情况已经汇总到temp了 if((outLen<=0)||( outLen > NET_CMDPACK_LEN)) //vsprintf (temp, cmd, ap); //到此为止,所有的参数情况已经汇总到temp了 { ErrorLogPrintf("%s,%d:cmdPack spillover!",__FILE__, __LINE__) ; //增加NET_CMDPACK_LEN数值 status = RUNERR ; goto netCmdOut ; } while(retryTimes--) { Ec20AtBufReset() ; revTimes = 0 ; //UARTx_Printf(EC20_UART, (uint8_t *)"%s", (uint8_t *)cmdPack); UARTx_SendData(EC20_UART, cmdPack, outLen) ; //DMA发送 while( revTimes++ < sNetCmd[cmdNum].timeout ) { Wait_For_Nms(100); sNetCmd[cmdNum].trueOffset = kmp(ec20AtBuf, sNetCmd[cmdNum].trueStr) ; if( sNetCmd[cmdNum].trueOffset >= 0) { status = RUNOK ; goto netCmdOut ; } else if( kmp(ec20AtBuf, sNetCmd[cmdNum].falseStr) >= 0) { status = RUNERR ; goto netCmdOut ; } } Wait_For_Nms( 1000 ) ; } netCmdOut:portFree(cmdPack) ; va_end (ap); DebugLogPrintf("%s %s", NetCmdNumToString((enum eNetCmdNum)cmdNum), RunResultToString(status) ) ; return (status) ; } /************************************************************************************************** * 名 称: RunResult Config_Context( uint8_t contextId ) * 功能说明: 对contextID进行配置( , , 等信息) * 入口参数: * @param1 contextId: 取值范围1~16 * 出口参数: * @param1 runStatus RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Config_Context( uint8_t contextId ) { RunResult runStatus = EC20_SendNetCmd(COFCONTEXT, NULL, contextId, APN ) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Act_Context( uint8_t contextId ) * 功能说明: 激活contextID * 入口参数: * @param1 contextId: 取值范围1~16 * 出口参数: * @param1 runStatus RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Act_Context( uint8_t contextId ) { RunResult runStatus = EC20_SendNetCmd(ACTCONTEXT, NULL, contextId) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Deact_Context( uint8_t contextId ) * 功能说明: 去激活contextID * 入口参数: * @param1 contextId: 取值范围1~16 * 出口参数: * @param1 runStatus RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult Deact_Context( uint8_t contextId ) { RunResult runStatus = EC20_SendNetCmd(DEACTCONTEXT, NULL, contextId) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Query_Context( uint8_t contextId, uint8_t *localIp ) * 功能说明: 查询contextID激活状态,并获取对应本地IP地址 * 入口参数: * @param1 contextId: 取值范围1~16 * @param2 *localIp: 保存本地IP数据的地址 * 出口参数: * @param1 runStatus RunResult枚举类型变量,返回函数运行结果 * 注 意:目前只支持contextID小于等于3链路的本地IP获取 **************************************************************************************************/ RunResult Query_Context( uint8_t contextId, uint8_t *localIp ) { RunResult runStatus = EC20_SendNetCmd(QUERYCONTEXT, NULL, contextId) ; if( RUNOK == runStatus ) { switch(contextId) { case 1: sNetCmd[QUERYCONTEXT].trueOffset = kmp(ec20AtBuf, "+QIACT: 1" ); break ; case 2: sNetCmd[QUERYCONTEXT].trueOffset = kmp(ec20AtBuf, "+QIACT: 2" ); break ; case 3: sNetCmd[QUERYCONTEXT].trueOffset = kmp(ec20AtBuf, "+QIACT: 3" ); break ; default: sNetCmd[QUERYCONTEXT].trueOffset = kmp(ec20AtBuf, "+QIACT: 1" ); break ; } if( (sNetCmd[QUERYCONTEXT].trueOffset>=0) && (ec20AtBuf[sNetCmd[QUERYCONTEXT].trueOffset+8] == (0x30+contextId))&& (ec20AtBuf[sNetCmd[QUERYCONTEXT].trueOffset+10] == '1') ) { runStatus = RUNOK ; CopyValues(localIp, (uint8_t*)&ec20AtBuf[sNetCmd[QUERYCONTEXT].trueOffset+15], '"', MAX_IP_LEN) ; //AppLogPrintf("ContextId %c 本地IP:%s",contextId, localIp) ; } else { runStatus = RUNERR ; } } return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_QueryCsServiceStatus(void) * 功能说明: 注册CS网络 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_QueryCsServiceStatus(void) { RunResult runStatus = TIMEOUT ; int checkCsTimes = 45 ; //45*2S内没有连接到CS Server则表示连接失败《TCP(IP)_AT_Commands》手册P8 要求90S等待 equals to 1 or 5 EC20_SendNetCmd(SETCS, NULL ) ; while( checkCsTimes-- ) { Wait_For_Nms(2000) ; runStatus = EC20_SendNetCmd(QUERYCS, NULL ) ; if( RUNOK == runStatus ) { if( (sNetCmd[QUERYCS].trueOffset > 0)&& ((ec20AtBuf[sNetCmd[QUERYCS].trueOffset+9] == 0x31)||(ec20AtBuf[sNetCmd[QUERYCS].trueOffset+9] == 0x35) )) { runStatus =RUNOK ; break; } else { runStatus =RUNERR ; } } } return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_QueryPsServiceStatus(void) * 功能说明: 注册PS网络 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_QueryPsServiceStatus(void) { RunResult runResult = TIMEOUT ; int checkPsTimes = 30 ; //30*2S内没有连接到CS Server则表示连接失败 《TCP(IP)_AT_Commands》手册P8 要求60S等待 equals to 1 or 5 EC20_SendNetCmd(SETPS, NULL ) ; //Enable network registration unsolicited result code with location information+CREG: [,,[,]] while( checkPsTimes-- ) { Wait_For_Nms(2000) ; runResult = EC20_SendNetCmd(QUERYPS, NULL ) ; //查询模块连接PS server的状态 if( RUNOK == runResult ) { if( (sNetCmd[QUERYPS].trueOffset > 0)&& ((ec20AtBuf[sNetCmd[QUERYPS].trueOffset+10] == 0x31)||(ec20AtBuf[sNetCmd[QUERYPS].trueOffset+10] == 0x35)) ) { runResult =RUNOK ; break ; } else { runResult =RUNERR ; } } } return (runResult) ; } /************************************************************************************************** * 名 称: RunResult EC20_Query_SimIccid(char *simICCID) * 功能说明: 查询SIM卡的ICCID编号 * 入口参数: * @param1 *simICCID: 存放ICCID的内存地址 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Query_SimIccid(char *simICCID) { char *start ; memset(simICCID, 0, SIM_ICCID_LEN); RunResult runStatus = EC20_SendNetCmd(QUERYICCID, NULL ) ; if( RUNOK == runStatus ) { CopyValues((uint8_t*)simICCID, (uint8_t*)&ec20AtBuf[(sNetCmd[QUERYICCID].trueOffset+8)], 0x0D, SIM_ICCID_LEN) ; } return (runStatus) ; } /************************************************************************************************** * 名 称: char* EC20_Query_NetInfo(void) * 功能说明: 查询网络CSQ(信号质量) * 出口参数: * @param1 网络信息数据存放地址 **************************************************************************************************/ char* EC20_Query_NetInfo(void) { char *end ; RunResult runResult = EC20_SendNetCmd(QUERYNETINFO, NULL ) ; if( RUNOK == runResult ) { //start = strchr( (const char*)ec20ReceBuffer, ':' ) ; end = strchr((const char*)&ec20AtBuf[sNetCmd[QUERYNETINFO].trueOffset], 0x0D) ; *end = 0x00 ; //添加字符串结尾 return (char*)&ec20AtBuf[sNetCmd[QUERYNETINFO].trueOffset+10] ; } else { return "获取网络信息失败!" ; } } /************************************************************************************************** * 名 称: RunResult EC20_Query_CSQ(char *csq) * 功能说明: 查询网络CSQ(信号质量) * 入口参数: * @param1 *csq 存放csq的内存地址 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Query_CSQ(char *csq) { memset(csq, 0, CSQ_LEN+1); RunResult runResult = EC20_SendNetCmd(QUERYCSQ, NULL ) ; if( RUNOK == runResult ) { CopyValues((uint8_t*)csq, (uint8_t*)&ec20AtBuf[sNetCmd[QUERYCSQ].trueOffset+6], ',', SIM_ICCID_LEN) ; } return (runResult) ; } /************************************************************************************************** * 名 称: RunResult EC20_Net_Reg(char *errInfo, uint8_t errLen) * 功能说明: 模块注册到CS\PS服务器 * 入口参数: * @param1 *errInfo 存放错误信息 * @param2 errLen 错误信息的长度 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Net_Reg(char *errInfo, uint8_t errLen) { RunResult runResult = TIMEOUT ; runResult = EC20_Query_SimIccid(sEc20Param.simICCID) ; if( RUNOK != runResult ) { snprintf(errInfo, errLen, "请插入SIM卡") ; ErrorLogPrintf("未检测到SIM卡") ; return (RUNERR) ; } AppLogPrintf("Sim卡ICCID:%.*s", SIM_ICCID_LEN, sEc20Param.simICCID) ; runResult = EC20_QueryCsServiceStatus() ; if( RUNOK != runResult ) { snprintf(errInfo, errLen, "SIM卡欠费") ; ErrorLogPrintf("SIM欠费无法注册上网络") ; return (RUNERR) ; } EC20_QueryPsServiceStatus() ; AppLogPrintf("网络信息:%s", EC20_Query_NetInfo()) ; EC20_Query_CSQ(sEc20Param.csq) ; snprintf(errInfo, errLen, "信号质量:%s", sEc20Param.csq) ; AppLogPrintf("LTE CSQ:%.*s", CSQ_LEN, sEc20Param.csq) ; return (runResult) ; } /************************************************************************************************** * 名 称: RunResult ActivePDP( uint8_t contextId, uint8_t *localIp) * 功能说明: 配置、去激活、激活、查询PDP * 入口参数: * @param1 contextId: 取值范围1~16 * @param2 *localIp: 保存本地IP数据的地址 * 出口参数: * @param1 runStatus RunResult枚举类型变量,返回函数运行结果 * 异常处理:去激活如果超时直接返回TIMEOUT。否则激活失败执行重复去激活再激活流程三次 **************************************************************************************************/ RunResult ActivePDP( uint8_t contextId, uint8_t *localIp) { RunResult runResult = TIMEOUT ; Config_Context(contextId) ; //配置PDP VPN等信息 runResult = Act_Context(contextId) ; //激活PDP if( runResult == RUNOK ) { Query_Context(contextId, localIp) ; //激活成功->获取PDP的IP } return (runResult) ; } /************************************************************************************************** * 名 称: RunResult DeactivePDP( uint8_t contextId, uint8_t *localIp) * 功能说明: 去激活PDP链路 * 入口参数: * @param1 contextId: 取值范围1~16 * 出口参数: * @param1 runStatus RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult DeactivePDP( uint8_t contextId) { RunResult runResult = TIMEOUT ; runResult = Deact_Context(contextId) ; }