343 lines
14 KiB
C
343 lines
14 KiB
C
#include "ec20http.h"
|
||
#include <stdlib.h>
|
||
#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: <err>” 表示请求出错*/
|
||
{
|
||
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 ;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|