/********************************************************************************** * File Name: Modem.c * Function Describe:device for modem * Relate Module: * Explain: the modem must be using ZTE MC8332 * Writer: ShiLiangWen * Date: 2015.1.20 ***********************************************************************************/ #define THIS_FILE_ID 2 /* Includes ------------------------------------------------------------------*/ #include "includes.h" char ModemMsgBuf[MODEM_AT_MSG_BUF]; void ModemWaitSomeTime(int sec); /*********************************************************** 将字符串IP地址转为4字节的IP地址 如"xxx.xxx.xxx.xxx"转为4个字节 ************************************************************/ void Str2IP(char *strIP,unsigned char IP[4]) { int iip[4]; sscanf(strIP, "%d.%d.%d.%d",&iip[0],&iip[1],&iip[2],&iip[3]); IP[0]=(unsigned char)iip[0]; IP[1]=(unsigned char)iip[1]; IP[2]=(unsigned char)iip[2]; IP[3]=(unsigned char)iip[3]; } unsigned short StrToNum(char *str) { unsigned short num; if(str[0]>0x2f && str[0]<0x3a){ if(str[1]>0x2f && str[1]<0x3a){ if(str[2]>0x2f && str[2]<0x3a){ if(str[3]>0x2f && str[3]<0x3a){ num=1000*(str[0]-0x30); num+=100*(str[1]-0x30); num+=10*(str[2]-0x30); num+=(str[3]-0x30); }else{ num=100*(str[0]-0x30); num+=10*(str[1]-0x30); num+=(str[2]-0x30); } }else{ num=10*(str[0]-0x30); num+=(str[1]-0x30); } }else{ num= str[0]-0x30; } }else{ num=0; } return num; } /************************************************************************************ * *************************************************************************************/ void ModemSendAT(char *p) { unsigned short len; len=strlen(p); Uart3Send(p,len); } /************************************************************************************ * *************************************************************************************/ void ModemSendData(unsigned char *pData,unsigned short datalen) { Uart3Send((char *)pData,datalen); } /********************************************************************************** 延时并抛弃模块消息 ***********************************************************************************/ void ModemDelayAndDiscardMsg(int time) { int r; while(time--){ os_dly_wait(1); r=MsgQueueAccept(&ModemMsgQueue,ModemMsgBuf,sizeof(ModemMsgBuf)); if(r>0){ sutModemStatus.UartInactiveCt=0; SlwTrace(DEBUG,ModemMsgBuf); } } } void ModemClearMsg(void) { int r; while(1){ r=MsgQueueAccept(&ModemMsgQueue,ModemMsgBuf,sizeof(ModemMsgBuf)); if(r==0)return; } } /********************************************************************************** CSQ2Num ***********************************************************************************/ int CSQ2Num(char *str) { int csq; csq=atoi(str); if(csq<0 || csq>99)return 99; else return csq; } /********************************************************************************** ModemStrCmp 比较msg内与str相同的串。其中str可以通过"|"分开N个子字串 返回: 没有匹配的返回0 第1个字串匹配返回1 第2个匹配返回2,依次类推 比如:msg="12345" str="123" , 将返回1 比如:msg="123 45" str="12345" , 将返回0 比如:msg="123 456" str="abcd|123|123 4|aa ab|123 456" , 将返回3 ***********************************************************************************/ int ModemStrCmp(char *msg,char *str) { char d; char *p1=str; char *p2=msg; int token=1; //SlwTrace(DEBUG,msg); //SlwTrace(DEBUG,str); while(0!=(d=*p1)){ //搜索到分隔符'|',前面的都匹配,则前面的字串匹配 if(d=='|')return token;//结束搜索,返回匹配字串标记 if(d!=*p2){//有不相等字符,找下一个字串,如果后面没字串了则返回0 //移到下一个分隔符 while(0!=(d=*p1)){ if(d=='|')break; p1++; } if(d!='|')return 0;//没有分隔符了,说明没有一个字串匹配,返回0 //找到分隔符,分隔符后面的字串开始重新判断匹配 p1++; p2=msg; token++; }else{ p1++; p2++; } } return token; } /* 等待n秒,n秒内仍处理AT响应 */ void ModemWaitSomeTime(int sec) { int r,k; int timeout=sec*100; while(timeout--){ r=MsgQueueAccept(&ModemMsgQueue,ModemMsgBuf,sizeof(ModemMsgBuf)); if(0==r){ os_dly_wait(1); }else{ sutModemStatus.UartInactiveCt=0; if(r>0){ SlwTrace(DEBUG,ModemMsgBuf);//打印MODEM信息 AtHandle(ModemMsgBuf,r);//其他消息也需要处理 }else{ SlwTrace(DEBUG,"ModemMsgBuf OVER!\r\n"); } } } } /********************************************************************************** ModemWaitAckMsg 等待模块发回的消息与*pAck匹配,超时返回0,有匹配内容将返回非零。 其中pAck指向字符串,多个字符串可用'|'分隔,如果第一个字串匹配则返回1,第2个匹配将返回2..以此类推 pMsgBuf 返回消息内容 pMsgLen 返回返回消息长度 ***********************************************************************************/ int ModemWaitAckMsg(char *pAck,char **ppMsgBuf,int *pMsgLen,int timeout) { int r,k; while(timeout){ r=MsgQueueAccept(&ModemMsgQueue,ModemMsgBuf,sizeof(ModemMsgBuf)); if(0==r){ os_dly_wait(1); timeout--; }else if(r>0){ sutModemStatus.UartInactiveCt=0; SlwTrace(DEBUG,ModemMsgBuf);//打印MODEM信息 *ppMsgBuf=ModemMsgBuf; *pMsgLen=r; k=ModemStrCmp(ModemMsgBuf,pAck); if(k){ //SlwTrace(DEBUG,ModemMsgBuf); return k; }else{ AtHandle(ModemMsgBuf,r);//其他消息也需要处理 } }else{ SlwTrace(DEBUG,"ModemMsgBuf OVER!\r\n"); } } *ppMsgBuf=NULL; return 0; } /* 等待"OK"消息,并清掉此消息以及之前的所有消息 用途:在发下一条AT命令之前,上一个AT命令如果有多余的OK消息,而下一条AT又依赖于"OK"消息,则很容易出现上一条AT的"OK"干扰了下一条AT 为此,在上一条AT命令处理完之后,调用此函数清掉OK消息,然后再发下一条AT */ void ModemClearOKMsg(int timout) { char *pMsgBuf; int MsgBufLen; int r; r=ModemWaitAckMsg("OK",&pMsgBuf,&MsgBufLen,timout); } /********************************************************************************** ModemSendAT2WaitAckMsg Input: pATCmd--AT Command string pAck --期待回的ACK,头相同即可,可用'|'分割多个字串 timeout --超时,10ms为单位 Output: return 0--timeout n--与ACK第n项匹配 pMsgBuf --实际消息内容 pMsgBufLen --实际消息长度 ***********************************************************************************/ int ModemSendAT2WaitAckMsg(char *pATCmd,char *pAck,char **ppMsgBuf,int *pMsgBufLen,int timeout) { int r; ModemSendAT(pATCmd); r=ModemWaitAckMsg(pAck,ppMsgBuf,pMsgBufLen,timeout); return r; } /********************************************************************************** 发送数据,等待回OK 在发送TCP数据时有用 ***********************************************************************************/ //M_RESULT ModemSendDataAndWaitOK(unsigned char *Data,unsigned short len,int timeout) //{ // char *pMsgBuf; // int MsgBufLen; // int r; // ModemClearMsg(); // ModemSendData(Data,len); // r=ModemWaitAckMsg("OK|ERROR",&pMsgBuf,&MsgBufLen,timeout); // if(r==1)return SUCCEED; // else if(r==2)return FAIL; // else return TIMEOUT; //} /********************************************************************************** 发送数据 EC20发送数据特点: AT+QISEND=socket,len\r\n后要等待回>才能发实际数据 即使发送成功,也有两种情况: 1、如果服务器没有回复数据,会收到SEND OK\r\n 2、如果服务器直接回复数据,可能收不到SEND OK,而是直接收到:+QIURC: "recv",0,20\r\nXXX..XX\r\n ***********************************************************************************/ M_RESULT ModemSendToSocket(unsigned char socket,unsigned char *pData,unsigned short len) { char AtBuf[20]; char AckBuf[20]; char *pMsg; int MsgLen; int i,r; if(socket>11)return FAIL; ModemClearMsg(); snprintf(AtBuf,sizeof(AtBuf),"AT+QISEND=%d,%d\r\n",socket,len); r=ModemSendAT2WaitAckMsg(AtBuf,">|ERROR",&pMsg,&MsgLen,1000); if(r==1){ ModemSendData(pData,len); r=ModemWaitAckMsg("SEND FAIL|ERROR",&pMsg,&MsgLen,100); if(r==0){ snprintf(AtBuf,sizeof(AtBuf),"AT+QISEND=%d,0\r\n",socket); for(i=0;i<120;i++){//5*24=120 sec r=ModemSendAT2WaitAckMsg(AtBuf,"+QISEND: |ERROR",&pMsg,&MsgLen,100); if(r==1){ r=GetSubFromStr(pMsg,2,AckBuf,sizeof(AckBuf));//提取第2个逗号后面的字串 if(r>0 && AckBuf[0]=='0'){ return SUCCEED; }else{ ModemWaitSomeTime(1);//等待1秒,期间仍需要处理ATHandle } }else if(r==2){ return FAIL; } } }else{ return FAIL; } } return TIMEOUT; } M_RESULT ModemSendToUdpSocket(unsigned char socket,unsigned char *pData,unsigned short len) { char AtBuf[50]; char AckBuf[20]; char *pMsg; int MsgLen; int i,r; if(socket>11)return FAIL; ModemClearMsg(); snprintf(AtBuf,sizeof(AtBuf),"AT+QISEND=%d,%d\r\n",socket,len); SlwTrace(DEBUG, AtBuf); r=ModemSendAT2WaitAckMsg(AtBuf,">|ERROR",&pMsg,&MsgLen,1000); if(r==1){ ModemSendData(pData,len); //r=ModemWaitAckMsg("SEND FAIL|ERROR",&pMsg,&MsgLen,100); return SUCCEED; } return TIMEOUT; } /************************************************************************* ModemGetCCID 获取SIM/UIM卡的ICCID号,20位 **************************************************************************/ M_RESULT ModemGetCCID(char *pCCID) { char *pMsg; int MsgLen; char num[20]={0}; char *pTemp; int i,j,len; int r; RunMake(THIS_FILE_ID); //+QCCID: 89860415151870037004 ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+QCCID\r\n","+QCCID: |+CME ERROR: ",&pMsg,&MsgLen,300); if(r==1){ j=0; for(i=8;i<28;i++){ if(pMsg[i]<=0x20){ break; } pCCID[j++]=pMsg[i]; } if(i==28)return SUCCEED; else return FAIL; } return TIMEOUT; } /************************************************************************* ModemGetSimCardStatus 获取SIM卡状态 **************************************************************************/ M_RESULT ModemGetCardStatus(void) { char *pMsg; int MsgLen; char num[20]={0}; char *pTemp; int i,j,len; int r; RunMake(THIS_FILE_ID); ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+CPIN?\r\n","+CPIN: READY|+CME ERROR:",&pMsg,&MsgLen,500); if(r==1)return SUCCEED; else if(r==2){ os_dly_wait(100); return FAIL; }else return TIMEOUT; } /************************************************************************* ModemGetIMEI **************************************************************************/ M_RESULT ModemGetIMEI(char *pIMEI) { char *pMsg; int MsgLen; int r,i; r=ModemSendAT2WaitAckMsg("AT+CGSN\r\n","86",&pMsg,&MsgLen,300);//"861164030090890" if(r==1){ for(i=0;i<15;i++){ pIMEI[i]=pMsg[i]; } return SUCCEED; } return TIMEOUT; } unsigned short GetCheckSum(unsigned char *data,int len) { int i; unsigned short checksum=0; for(i=0;i0)return SUCCEED; } os_dly_wait(100); } return TIMEOUT; } /* 在str里查找第n个',提取后面的字串到val中,返回字串长度 n 指定第几个,之后开始提取字串 vallen 指定用于存储字串的val允许最大长度 返回字串长度。 */ int GetSubFromStr(char *str,int n,char *val,unsigned short vallen) { int i=0; char *p=str; char data=p[0]; //先找到第n个逗号 while(data){ data=*p; if(data==',')i++; if(i==n)break; p++; } if(*p!=',')return 0; p++; //从现在开始提取字串 i=0; data =*p; while(data){ data=*p; if(data==',' || data<=0x20){//结束 return i; } *val=data; val++; p++; i++; if(i>=vallen)return 0; } return 0; } /**************************************************************** ModemGetCSQ 获取CSQ值 *****************************************************************/ M_RESULT ModemGetCSQ(unsigned char *CSQ) { char *pMsg; int MsgLen; int r,i; int csq; r=ModemSendAT2WaitAckMsg("AT+CSQ\r\n","+CSQ:",&pMsg,&MsgLen,4000); if(r==1 && MsgLen>10){//+CSQ: 0, 99 或 +CSQ: 99, 99 //找到逗号后将其修改为0 if(pMsg[7]==',')pMsg[7]=0; else if(pMsg[8]==',')pMsg[8]=0; csq=atoi(&pMsg[6]); if(csq>=0 && csq<=199){ *CSQ=csq; return SUCCEED; } } return TIMEOUT; } void ModemPowerOff(void) { char *pMsg; int MsgLen; int r,i; int csq; ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+QPOWD\r\n","POWERED DOWN",&pMsg,&MsgLen,1300); USART_ITConfig(USART3, USART_IT_RXNE, DISABLE); USART_Cmd(USART3, DISABLE); os_dly_wait(300); MODEM_PWREN_DISABLE; } /* 重启模块 */ void ModemReboot(int t) { static int i; //1秒 10秒 30秒 1分 5分 static unsigned short time1[]={100,1000,3000,6000,30000};// 10ms为单位 //10分 30分 1小时 3小时 6小时 static unsigned short time2[]={10,30,60,180,360};// 1分钟为单位 ModemPowerOff(); //delay time if(t<5){ os_dly_wait(time1[t]); }else if(t<10){ for(i=0;i=10 for(i=0;i<360;i++){ os_dly_wait(6000); } } ModemInit(); } /**************************************************************** *获取PDP链路状态 1,,[,] ... 16,,[,] *****************************************************************/ M_RESULT ModemGetPDP(void) { char *pMsg; int MsgLen; int r; ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+QIACT?\r\n","+QIACT:|ERROR",&pMsg,&MsgLen,300); if(r==1){ r=ModemWaitAckMsg("1,1|1,0",&pMsg,&MsgLen,10); if(r==1){ ModemClearMsg(); return SUCCEED; }else{ ModemClearMsg(); return FAIL; } }else if(r==2)return FAIL; else return TIMEOUT; } /***************************************************************** 打开PDP链路 ******************************************************************/ M_RESULT ModemOpenPDP(void) { char *pMsg; int MsgLen; int r; ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+QIACT=1\r\n","OK|ERROR",&pMsg,&MsgLen,150000); if(r==1)return SUCCEED; else if(r==2)return FAIL; else return TIMEOUT; } /***************************************************************** 查询PDP链路 ******************************************************************/ M_RESULT ModemQueryPDP(void) { char *pMsg; int MsgLen; int r; ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+QIACT?\r\n","+QIACT: 1,1|ERROR",&pMsg,&MsgLen,150000); if(r==1)return SUCCEED; else if(r==2)return FAIL; else return TIMEOUT; } /***************************************************************** 关闭PDP链路 ******************************************************************/ M_RESULT ModemClosePDP(void) { char *pMsg; int MsgLen; int r; ModemClearMsg(); r=ModemSendAT2WaitAckMsg("AT+QIDEACT=1\r\n","OK|ERROR",&pMsg,&MsgLen,4000); if(r==1)return SUCCEED; else if(r==2)return FAIL; else return TIMEOUT; } /* 在字符串str中,找第n个与ch相等的字符 找到返回 */ char* strchrN(const char *str,char ch,int n) { char *p=(char *)str; while(*p!=0){ if(*p==','){ if(--n==0)return p; } p++; } return NULL; } /**************************************************************** Query Socket Service Status +QISTATE:,,, ,,,,,, *****************************************************************/ M_RESULT ModemGetSocket(int socket,int timeout) { char AtBuf[20]; char AckBuf[20]; char *pMsg; int MsgLen; int r; if(socket>11)return FAIL; snprintf(AtBuf,sizeof(AtBuf),"AT+QISTATE=1,%d\r\n",socket); snprintf(AckBuf,sizeof(AckBuf),"+QISTATE:%d,|OK|ERROR",socket); while(timeout--){ ModemClearMsg(); r=ModemSendAT2WaitAckMsg(AtBuf,AckBuf,&pMsg,&MsgLen,300); if(r==1){ //+QISTATE: 0,"TCP","120.77.66.129",9137,5995,4,1,0,1,"uart1" pMsg=strchrN(pMsg,',',5); if(pMsg!=NULL)pMsg++; if(*pMsg=='1'){ return SUCCEED; }else if(*pMsg=='0'){ return FAIL; }else{ os_dly_wait(100); } }else if(r>=2)return FAIL; else return TIMEOUT; } return TIMEOUT; } /**************************************************************** AT+QIOPEN=,,,/,[,[,]] AT+QIOPEN=1,0,"TCP","120.27.39.29",30009,0,1 *****************************************************************/ M_RESULT ModemOpenSocket(int socket,char *tcp_udp,char *server,unsigned short Port) { char AtBuf[120]; char AckBuf[50]; char *pMsg; int MsgLen; int r; if(socket>11)return FAIL; ModemClearMsg(); snprintf(AtBuf,sizeof(AtBuf),"AT+QIOPEN=1,%d,\"%s\",\"%s\",%d,0,1\r\n",socket,tcp_udp,server,Port); SlwTrace(DEBUG, AtBuf); snprintf(AckBuf, sizeof(AckBuf), "+QIOPEN: %d,|ERROR",socket); r=ModemSendAT2WaitAckMsg(AtBuf,AckBuf,&pMsg,&MsgLen,2000); if(r==1){ if(pMsg[11]=='0')return SUCCEED; else return FAIL; }else if(r==2){ return FAIL; } else return TIMEOUT; } /**************************************************************** Query Socket Service Status *****************************************************************/ M_RESULT ModemCloseSocket(int socket) { char AtBuf[20]; char *pMsg; int MsgLen; int r; ModemClearMsg(); snprintf(AtBuf,sizeof(AtBuf),"AT+QICLOSE=%d\r\n",socket); r=ModemSendAT2WaitAckMsg(AtBuf,"OK|ERROR",&pMsg,&MsgLen,1100); if(r==1)return SUCCEED; else if(r==2)return FAIL; else return TIMEOUT; } /* 设置SIM/UIM卡的APN和用户名,密码 */ M_RESULT ModemSetAPN(char *pApn,char *pUsrName, char *pUsrPass) { char *pMsg; int MsgLen; char sendbuf[100]; int i,r; ModemClearOKMsg(100); //先查询,如果已经相同,则直接激活 snprintf(sendbuf,sizeof(sendbuf),"+QICSGP: 1,\"%s\",\"%s\",\"%s\",1",pApn,pUsrName,pUsrPass); if(1==ModemSendAT2WaitAckMsg("AT+QICSGP=1\r\n",sendbuf,&pMsg,&MsgLen,100)){ ModemClearOKMsg(100); if(1==ModemSendAT2WaitAckMsg("AT+QIACT=1\r\n","OK",&pMsg,&MsgLen,100)){ return SUCCEED; } } ModemClearOKMsg(100); //不同则设置后激活 snprintf(sendbuf,sizeof(sendbuf),"AT+QICSGP=1,1,\"%s\",\"%s\",\"%s\",1\r\n",pApn,pUsrName,pUsrPass); ModemClearOKMsg(100); if(1==ModemSendAT2WaitAckMsg(sendbuf,"OK",&pMsg,&MsgLen,100)){ if(1==ModemSendAT2WaitAckMsg("AT+QIACT=1\r\n","OK",&pMsg,&MsgLen,100)){ return SUCCEED; } } return FAIL; } /********************************************************************************** 等待模块启动完成 定时发AT直到回OK, times: 发多少次AT,每秒1次 ***********************************************************************************/ M_RESULT ModemWaitOpened(int times) { int i,r; char *pMsg; int MsgLen; ModemClearMsg(); for(i=0;i