#include "ec20http.h" #include #include "syslib.h" //#define STR2(R) STR1(R) /******************************************************************************** * @file ec20http.c * @author 晏诚科技 Mr.Wang * @version V1.0.0 * @date 11-Dec-2018 * @brief 提供Quectel模块EC20关于HTTP驱动程序 ****************************************************************************** * @attention * * 约定基本名词如下: * contextID:链路ID connetcID:通道ID channal:连接通道 * EC20模块链路ID范围1~16,每一个链路ID都会对应一个本地IP; 通道ID范围0~11。 * 每一个链路ID可以有12个通道ID。 * 本驱动强制规定,HTTP协议只用一个链路1D(即contextID=2)用于HTTP链路。 * @use: * 先调用Http_Init()初始化HTTP链路等参数,接着调用Send_Post发送POST请求,最后通过Http_Read读取接收到的POST数据 *******************************************************************************/ /***************************************** *内部常量 ****************************************/ #define HTTP_CMDPACK_LEN 300 //EC20 HTTP相关命令字符串的最大长度 #define HTTP_CONTEXTID 2 //1~16 本驱动强制规定,HTTP协议只用一个链路1D(即contextID=2)用于HTTP链路 /******************************************************** ec20模块TCP/IP相关AT指令处理 *********************************************************/ enum eHttpCmdNum { HTTPCONTEST =0, ENREQHEADER =1, CLOSETIME =2, BODYTYPE =3, SETURLCMD =4, HTTPURL =5, POSTREQCMD =6, POSTREQBUF =7, HTTPREAD =8 } ; //枚举ec20模块http相关指令 volatile EC20_CMD_DATA_s sHttpCmd[9]= { // cmdNum cmdStr, timeout(100ms), trueStr, trueOffset, falseStr revResult rtyNum {HTTPCONTEST, "AT+QHTTPCFG=\"contextid\","STR2(HTTP_CONTEXTID)"\r\n", 5, "OK", -1, "ERROR", TIMEOUT, 2 }, //配置http使用的contextid {ENREQHEADER, "AT+QHTTPCFG=\"requestheader\",1\r\n" , 5, "OK" , -1, "ERROR", TIMEOUT, 2 }, //配置是否输出接收头数据 {CLOSETIME, "AT+QHTTPCFG=\"closewaittime\",100\r\n" , 5, "OK" , -1, "ERROR", TIMEOUT, 2 }, //配置http请求超时时间 {BODYTYPE, "AT+QHTTPCFG=\"contenttype\",1\r\n" , 5, "OK" , -1, "ERROR", TIMEOUT, 2 }, //设置content_type为 1 text/plain类型 {SETURLCMD, "AT+QHTTPURL=%d,60\r\n" , (60*10), "CONNECT", -1, "ERROR", TIMEOUT, 1 }, //设置URL {HTTPURL, "%s" , 5, "OK" , -1, "ERROR", TIMEOUT, 2 }, //发起post请求 {POSTREQCMD, "AT+QHTTPPOST=%d,6,6\r\n" , (6*10), "CONNECT", -1, "ERROR", TIMEOUT, 1 }, {POSTREQBUF, "%s" , (8*10), "+QHTTPPOST: 0,200", -1, "ERROR", TIMEOUT, 1 }, //读取http返回的数据 //{HTTPREAD, "AT+QHTTPREAD=6\r\n" , (6*10), "OK", -1, "ERROR", TIMEOUT, 2 } {HTTPREAD, "AT+QHTTPREAD=6\r\n" , (10*10), "+QHTTPREAD", -1, "ERROR", TIMEOUT, 1 } } ; //EC20模块HTTP相关指令的EC20_CMD_DATA_s结构体类型参数 /************************************************************************************************** * 名 称: static const char *HttpCmdNumToString(enum eHttpCmdNum result) * 功能说明: 输出枚举成员名的字符串指针。 * 入口参数: eHttpCmdNum类型的枚举 * 出口参数: 为枚举的成员名字符串指针 **************************************************************************************************/ static inline const char *HttpCmdNumToString(enum eHttpCmdNum result) { switch (result) { ENUM_CHIP_TYPE_CASE(HTTPCONTEST) ENUM_CHIP_TYPE_CASE(ENREQHEADER) ENUM_CHIP_TYPE_CASE(CLOSETIME) ENUM_CHIP_TYPE_CASE(BODYTYPE) ENUM_CHIP_TYPE_CASE(SETURLCMD) ENUM_CHIP_TYPE_CASE(HTTPURL) ENUM_CHIP_TYPE_CASE(POSTREQCMD) ENUM_CHIP_TYPE_CASE(POSTREQBUF) ENUM_CHIP_TYPE_CASE(HTTPREAD) } return "无此命令"; } /************************************************************************************************** * 名 称: RunResult EC20_SendHttpCmd( uint8_t cmdNum, char *format,... ) * 功能说明: MCU串口向EC20发送http相关命令 * 入口参数: * @param1 cmdNum EC20_CMD_DATA_s中cmdNum成员命令编号 * @param2 char *format,... 可变参变量 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_SendHttpCmd( uint8_t cmdNum, char *format,... ) { uint8_t revTimes = 0 ; RunResult status = TIMEOUT ; uint8_t retryTimes = sHttpCmd[cmdNum].rtyNum ; char *cmdPack = NULL ; format = sHttpCmd[cmdNum].cmdStr ; cmdPack = portMalloc(HTTP_CMDPACK_LEN*sizeof(uint8_t)) ; va_list ap; va_start (ap, format); int outLen = vsnprintf(cmdPack, HTTP_CMDPACK_LEN, (const char*)format, ap) ; if((outLen<=0)||( outLen > HTTP_CMDPACK_LEN)) //vsprintf (temp, cmd, ap); //到此为止,所有的参数情况已经汇总到temp了 { ErrorLogPrintf("Http cmdPack 溢出!--增加HTTP_CMDPACK_LEN数值。") ; goto httpCmdOut ; } while(retryTimes--) { Ec20AtBufReset() ; revTimes = 0 ; //UARTx_Printf(EC20_UART, (uint8_t *)"%s", (uint8_t *)cmdPack); UARTx_SendData(EC20_UART, cmdPack, outLen) ; //DMA发送 while( revTimes++ < (sHttpCmd[cmdNum].timeout*2) ) { Wait_For_Nms(50); sHttpCmd[cmdNum].trueOffset = kmp(ec20AtBuf, sHttpCmd[cmdNum].trueStr) ; if( sHttpCmd[cmdNum].trueOffset >= 0) { status = RUNOK ; goto httpCmdOut ; } else if( kmp(ec20AtBuf, sHttpCmd[cmdNum].falseStr) >= 0) { status = RUNERR ; goto httpCmdOut ; } } Wait_For_Nms( 1000 ) ; } httpCmdOut:portFree(cmdPack) ; va_end (ap); DebugLogPrintf("%s %s", HttpCmdNumToString((enum eHttpCmdNum)cmdNum), RunResultToString(status) ) ; return (status) ; } /************************************************************************************************** * 名 称: RunResult Http_Config(void) * 功能说明: http相关的基本配置,包括请求头、超时时间、请求的数据类型等 **************************************************************************************************/ RunResult Http_Config(void) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendHttpCmd(HTTPCONTEST, NULL ) ; if( RUNOK != runStatus ) { return (runStatus) ; } runStatus = EC20_SendHttpCmd(ENREQHEADER, NULL ) ; if( RUNOK != runStatus ) { return (runStatus) ; } if( (kmp(sEc20Param.ec20SoftVer , "EC20CEFDKGR06A03M2G") < 0) ) //EC20该版本需要增加下面指令 { runStatus = EC20_SendHttpCmd(CLOSETIME, NULL ) ; //ec20SoftVer if( RUNOK != runStatus ) { return (runStatus) ; } } // runStatus = EC20_SendHttpCmd(CLOSETIME, NULL ) ; //ec20SoftVer // if( RUNOK != runStatus ) // { // return (runStatus) ; // } runStatus = EC20_SendHttpCmd(BODYTYPE, NULL ) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Http_PDP_Init(void) * 功能说明: http协议的链路ID contextid配置,强制设置为HTTP_CONTEXTID **************************************************************************************************/ RunResult Http_PDP_Init( void ) { RunResult runResult = TIMEOUT ; uint8_t *httpLocalIp = portMalloc(MAX_IP_LEN) ; runResult = Query_Context( HTTP_CONTEXTID, httpLocalIp ) ; //查询HTTP_CONTEXTID是否激活 if( RUNOK == runResult ) /*HTTP_CONTEXTID已激活*/ //去激活->再次激活 { // runResult = Deact_Context(HTTP_CONTEXTID) ; // if( RUNOK != runResult ) /*HTTP_CONTEXTID去激活失败*/ //直接返回错误 // { // return RUNERR ; // } return RUNOK ; } runResult = ActivePDP(HTTP_CONTEXTID, httpLocalIp) ; if( RUNOK == runResult ) { AppLogPrintf("HTTP本地IP:%s", httpLocalIp) ; } portFree(httpLocalIp) ; return(runResult) ; } /************************************************************************************************** * 名 称: RunResult Set_HttpURL(char *host) * 功能说明: 设置http请求的主机地址 * 入口参数: * @param1 *host 主机地址 **************************************************************************************************/ RunResult Set_HttpURL(char *host) { RunResult runStatus = TIMEOUT ; int hostLen = strlen(host) ; char *url = portMalloc(10+hostLen) ; snprintf(url, 10+hostLen, "%s%s", "http://", host) ; runStatus = EC20_SendHttpCmd(SETURLCMD, NULL, strlen((const char*)url) ) ; if( RUNOK == runStatus ) { runStatus = EC20_SendHttpCmd(HTTPURL, NULL, url ) ; if( RUNOK == runStatus ) { AppLogPrintf("POST URL:%s.", url) ; } } portFree(url) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Http_PDP_Init(void) * 功能说明: http初始化,包括基本参数、和链路初始化 **************************************************************************************************/ RunResult Http_Init( void ) { RunResult runStatus = TIMEOUT ; if( RUNOK != Http_Config() ) return (runStatus) ; runStatus = Http_PDP_Init() ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Send_Post( POSTP_s *psHttpP, char* postBody ) * 功能说明: http发送post请求 * 入口参数: * @param1 *psHttpP POSTP_s类型数据指针,包含http请求的参数 * @param2 *postBody post请求的负载数据 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 * 如果返回RUNOK说明已经请求成功,接下来就可以调用Http_Read()函数读取存储在缓冲区内的数据了 **************************************************************************************************/ RunResult Send_Post( POSTP_s *psHttpP, char* postBody ) { int outLen = 0 ; RunResult runStatus = TIMEOUT ; Set_HttpURL(psHttpP->host) ; //设置HTTP请求URL char *headerBodyBuf = portMalloc(HTTP_CMDPACK_LEN) ; outLen = snprintf(headerBodyBuf, HTTP_CMDPACK_LEN, psHttpP->postBuf, psHttpP->host, psHttpP->httpPort, psHttpP->host,strlen(postBody), postBody ) ; if((outLen<=0)||(outLen>HTTP_CMDPACK_LEN)) { runStatus = RUNERR ; ErrorLogPrintf("headerBodyBuf 溢出!--增加HTTP_CMDPACK_LEN数值。") ; goto PostOut ; } runStatus = EC20_SendHttpCmd(POSTREQCMD, NULL, outLen ) ; //发送POST请求命令 if( RUNOK != runStatus ) { goto PostOut ; } runStatus = EC20_SendHttpCmd(POSTREQBUF, NULL, headerBodyBuf ) ;//发送POST请求数据 //DebugLogPrintf("%s",headerBodyBuf) ; PostOut: portFree(headerBodyBuf) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult Http_Read( void ) * 功能说明: 读取http返回的数据, 将读取到的http数据存于ec20HttpBuf中 **************************************************************************************************/ RunResult Http_Read( void ) { uint8_t revTimes = 0 ; int errCode = 0 ; RunResult runStatus = TIMEOUT ; Ec20HttpBufReset() ; //ec20HttpBuf清空 准备接下来接收数据 httpDataMode = true ; //将EC20_UART串口接收模式改为:接收http数据模式 UARTx_SendData(EC20_UART, sHttpCmd[HTTPREAD].cmdStr, strlen((const char*)sHttpCmd[HTTPREAD].cmdStr) ) ; //DMA发送 while( revTimes++ < (sHttpCmd[HTTPREAD].timeout) ) { Wait_For_Nms(100) ; //等待100ms sHttpCmd[HTTPREAD].trueOffset = kmp(ec20HttpBuf, sHttpCmd[HTTPREAD].trueStr) ; //在ec20HttpBuf缓冲区中查找 sHttpCmd[HTTPREAD].trueStr字串 if( sHttpCmd[HTTPREAD].trueOffset >= 0) { if( ec20HttpBuf[sHttpCmd[HTTPREAD].trueOffset+12] == 0x30 ) /*"AT+QHTTPREAD"指令返回“+QHTTPREAD: 0” 表示请求成功*/ { errCode = 0 ; runStatus = RUNOK ; } else /*"AT+QHTTPREAD"指令返回“+QHTTPREAD: ” 表示请求出错*/ { errCode = (ec20HttpBuf[sHttpCmd[HTTPREAD].trueOffset+1]-0x30)*100+(ec20HttpBuf[sHttpCmd[HTTPREAD].trueOffset+2]-0x30)*10+ (ec20HttpBuf[sHttpCmd[HTTPREAD].trueOffset+3]) ; runStatus = RUNERR ; } HttpErrorCode(errCode) ; break ; } else if( kmp(ec20AtBuf, sHttpCmd[HTTPREAD].falseStr) >= 0) { runStatus = RUNERR ; break ; } } httpDataMode = false ; //关闭 接收http数据模式 DebugLogPrintf("%s %s", HttpCmdNumToString(HTTPREAD), RunResultToString(runStatus) ) ; return (runStatus) ; } /************************************************************************************************** * 名 称: void HttpErrorCode( int errCode ) * 功能说明: 解析HTTP返回的错误码,请参照《Quectel_EC2x&EG9x&EM05_HTTP(S)_AT_Commands_Manual_V1.0.pdf》手册P35 * 入口参数: * @param1 errCode 错误码 **************************************************************************************************/ void HttpErrorCode( int errCode ) { switch( errCode) { case 0: AppLogPrintf("Http Operation successful.") ; break ; case 701: AppLogPrintf("HTTP(S) unknown error.") ; break ; case 702: AppLogPrintf("HTTP(S) timeout.") ; break ; default: AppLogPrintf("HTTP(S) other error.") ; break ; } }