赞
踩
SIM900A是一款高性能工业级 GSM/GPRS 模块。工作频段双频:900/1800Mhz,可以低功耗实现语音、SMS(短信,不支持彩信)、数据和传真信息的传输(GPRS功能)。SIM900A 模块支持通过串口与单片机进行通信,采用AT指令集的开发方式,并带硬件流控制,使得该模块可以非常方便的与单片机之间进行连接,从而实现语音、短信和 GPRS 数据传输等功能。该模块在使用之前需要安装通信运营商的SIM卡,以通过流量实现网络通信功能,这里我们采用的是一张中国移动SIM卡(需开通GPRS功能)。
AT指令不过多介绍,这里列出几个常用的AT指令:
1, AT+CPIN?
该指令用于查询 SIM 卡的状态,主要是 PIN 码,如果该指令返回:+CPIN:READY,则表明 SIM 卡状态正常,返回其他值,则有可能是没有 SIM 卡。
2, AT+CSQ
该指令用于查询信号质量,返回 SIM900A 模块的接收信号强度,如返回:+CSQ: 24,0,表示信号强度是 24(最大有效值是 31)。如果信号强度过低,则要检查天线是否接好了?
3, AT+COPS?
该指令用于查询当前运营商,该指令只有在连上网络后,才返回运营商,否则返回空,如返回:+COPS:0,0, “CHINA MOBILE”,表示当前选择的运营商是中国移动。
4, AT+CGMI
该指令用于查询模块制造商,如返回:SIMCOM_Ltd,说明 SIM900A 模块是 SIMCOM公司生产的。
5, AT+CGMM
该指令用于查询模块型号,如返回:SIMCOM_SIM900A,说明模块型号是 SIM900A。
6, AT+CGSN
该指令用于查询产品序列号(即 IMEI 号),每个模块的 IMEI 号都是不一样的,具有全球唯一性,如返回:869988012018905,说明模块的产品序列号是:869988012018905。
7, AT+CNUM
该指令用于查询本机号码,必须在 SIM 卡在位的时候才可查询,如返回:+CNUM: “”,“15902020353”,129,7,4,则表明本机号码为:15902020353。另外,不是所有的 SIM 卡都支持这个指令,有个别 SIM 卡无法通过此指令得到其号码。
8, ATE1
该指令用于设置回显模式(默认开启),即模块将收到的 AT 指令完整的返回给发送端,启用该功能,有利于调试模块。如果不需要开启回显模式,则发送 ATE0 指令即可关闭,这样收到的指令将不再返回给发送端,这样方便程序控制。
发送给模块的指令,如果执行成功,则会返回对应信息和"OK",如果执行失败/指令无效,则会返回"ERROR"。
对于语音通话和短信的收发本文不做介绍,这里仅介绍一下GPRS网络通信,基于MSP430F149单片机实现,并在第二部分附上源码。实现GPRS通信(TCP,UDP连接通信)将要用到的指令有如下 等 10 条 AT 指令:
1.AT+CGCLASS,用于设置移动台类别。
SIM900A 模块仅支持类别"B"和"CC",发送:AT+CGCLASS=“B”,设置移动台台类别为 B。即,模块支持包交换和电路交换模式,但不能同时支持。
2.AT+CGDCONT,用于设置 PDP 上下文。
发送AT+CGDCONT=1,“IP”,“CMNET”,设置 PDP 上下文标标志为 1,采用互联网协议(IP),接入点为"CMNET"。
3.AT+CGATT,用于设置附着和分离 GPRS 业务。
发送:AT+CGATT=1,附着 GPRS 业务。
4.AT+CIPCSGP,用于设置 CSD 或 GPRS 链接模式。
发送:AT+CIPCSGP=1, “CMNET”,设置为 GPRS 连接,接入点为”CMNET”。
5.AT+CLPORT,用于设置本地端口号。
发送:AT+CLPORT=“TCP”,“8888”,即设置 TCP
连接本地端口号为 8888。
6.AT+CIPSTART,用于建立 TCP 连接或注册 UDP 端口号。
发送: AT+CIPSTART=“TCP”,“113.111.214.69”,“8086”,模块将建立一个 TCP 连接,连接目标地址为:113.111.214.69,连接端口为 8086,连接成功会返回:CONNECT OK。
7.AT+CIPSEND,用于发送数据。
在连接成功以后发送:AT+CIPSEND,模块返回:>,此时可以输入要发送的数据,最大可以一次发送 1352 字节,数据输入完后,同发短信一样,输入十六进制的:1A(0X1A),启动发送数据。在数据发送完成后,模块返回:SEND OK,表示发送成功。
8.AT+CIPSTATUS,用于查询当前连接状态。
发送:AT+CIPSTATUS,模块即返回当前连接状态。
9.AT+CIPCLOSE,用于关闭 TCP/UDP 连接。
发送:AT+CIPCLOSE=1,即可快速关闭当前 TCP/UDP 连接。
10.AT+CIPSHUT,用于关闭移动场景。
发送:AT+SHUT,则可以关闭移动场景,关闭场景后连接状态为:IP INITIAL,可以通过发送:AT+CIPSTATUS,查询。另外,在连接建立后,如果收到:+PDP: DEACT,则必须发送:AT+CIPSHUT,关闭场景后,才能实现重连。
以上就是我们可能将要用到的一些 AT 指令的简单介绍,要实现模块与电脑的GPRS 通信,需要确保所用电脑具有公网 IP,否则无法实现通信,推荐在 ADSL 网络下进行测试,并最好关闭防火墙/杀毒软件。
对于 ADSL 用户(没用路由器),直接拥有 1 个公网 IP,你可以通过百度,搜索:IP,第一个条目,就是本机 IP。
TCP 是基于连接的协议,在收发数据前,必须先和对方建立可靠连接,是一种可靠的数据传输方式。我们将在SIM900A 模块和电脑调试助手(这里需要配置网络的映射规则DMZ,要将私网IP映射到公网才能进行网络通信)之间之间建立一个 TCP 连接,并实现数据的互相收发。我们也可以直接与云服务平台之间建立连接,不需要网络映射,更加方便。这里在我们配置好网络映射规则后建立TCP连接的主要指令步骤如下:
AT+CGCLASS=“B”
AT+CGDCONT=1,“IP”,“CMNET”
AT+CGATT=1
AT+CIPCSGP=1,“CMNET”
这几条指令前面已经介绍过,用于设置移动台类别、连接方式、接入点和附着 GPRS业务等。起到一个前期准备的作用。
之后,发送:AT+CLPORT=“TCP”,“2000”,设置本地 TCP 连接端口为 2000,然后发送:AT+CIPSTART=“TCP”,“113.111.214.69”,“8086”,建立 TCP 连接,连接到 IP:113.111.214.69,连接端口为:8086。等待 TCP 连接成功建立,模块返回:CONNECT OK。此时,SIM900A 模块和电脑便建立了一个 TCP 连接,可以互相发送数据了。
首先,我们来看如何通过SIM900A 模块给电脑发送数据。
通过给模块发送:AT+CIPSEND,此时模块返回:>,然后我们发送字符串(不用发送新行):SIM900A TCP 连接测试,最后发送十六进制的:1A,启动数据发送。然后等待模块回应:SEND OK,说明发送成功。
同样,我们通过电脑的网络调试助手向模块发送数据,模块也会受到。TCP数据的收发成功!
注意,TCP 连接需要心跳维持,如果长时间没有数据的收发,那么 TCP 连接很可能会被断开,下次数据通信,又得重新连接,所以实际应用的时候,都是需要添加心跳,来维持当前 TCP 连接的(在后面的代码中有具体实现方法,可仔细查看)。
最后,我们要关闭 TCP 连接,发送:AT+CIPCLOSE=1,关闭当前 TCP 连接,再发送:AT+CIPSHUT,关闭场景。
各源文件与头文件分类及实现功能如下:
1.main.c(主程序)
2.Config.h
Config.c(基础配置文件,包括IO口初始化,按键扫描,串口,时钟等基础配置)
3.SIM900A.h
SIM900A.c(SIM900A模块配置的主文件,包括一些配置函数及实现方法)
#include"SIM900A.h" //包含各种头文件,请依次向下查找 /* 解释说明: 1.注意:模块供电不足可能会一直回复IIII! 2.可以连接网页的云端服务器(如安信可) 3.可以通过开通隧道连接网络调试助手(即实现私网IP向公网的映射) 4.只要连接到网络后就可以访问网络服务器以进行各种操作(SNAT:获取实时网络时间,SMTP:进行邮件发送)等等 5.TCP连接通信过程中,不断和服务器进行心跳连接,每6S发送一次心跳信号,等待服务器回应,根据服务器返回的数据判断心跳是否成功,具体原理看程序。 */ void main( void ) { WDT_Init_OFF(); //关闭看门狗 Clock_Init_XT2(); //系统时钟初始化函数,XT2 Port_Init(); //P6口初始化,包括LED和按键KEY UART0_Init(); //MSP430串口0初始化 UART1_Init(); //MSP430串口1初始化 while(1) { sim900a_test(); //sim900a主测试程序 delay_ms(1000); } }
/*************************Config.h*********************************/ #define u8 unsigned char //无符号8位宏定义 #define u16 unsigned long //无符号16位宏定义 #define u32 unsigned int //无符号32位宏定义 #define CPU_F ((double)8000000) //内部延时函数所需的时钟大小,单位为Hz #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0)) //调用系统自定义函数__delay_cycles计算时钟 #define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) //调用系统自定义函数__delay_cycles计算时钟 #define S1_PRE 1 //按键S1 #define S2_PRE 2 //按键S2 #define S3_PRE 3 //按键S3 #define S4_PRE 4 //按键S4 void WDT_Init_OFF();//看门狗初始化函数,关闭看门狗 void Port_Init();//IO口配置,包括LED和按键 u8 KEY_Scan(u8 mode);//*按键扫描函数,返回按键值,mode:0:不支持连按;1:支持连按 /*****************************定时器配置**************************************************/ void TimerA_Init_MC0(void);//TIMERA初始化程序,增计数模式MC0 void TimerA_Init_MC1(void);//TIMERA初始化程序,连续计数模式MC1 /*****************************串口配置****************************************************/ //串口波特率计算,当BRCLK=CPU_F时用下面的公式可以计算,否则要根据设置加入分频系数 #define baud 115200 //设置波特率的大小 #define baud_setting (u32)((u16)CPU_F/((u16)baud)) //波特率计算公式 #define baud_h (u8)(baud_setting>>8) //提取高位 #define baud_l (u8)(baud_setting) //低位 /***********************MSP430串口0初始化*************************/ extern u8 USART_RX_BUF[200]; //串口0接收缓冲区,最大200个字节 extern u32 USART_RX_COUNT; //串口0接收字节计数 extern u8 USART1_RX_BUF[200]; //串口1接收缓冲区,最大200个字节 extern u32 USART1_RX_COUNT; //串口1接收字节计数 extern u8 USART1_TX_BUF[200]; //串口1最大发送字节缓存区 void UART0_Init();//MSP430串口0初始化 void Send_Byte(u8 data);//串口0发送数据函数 u8 UART0_RX_SR(void);//判断串口0是否接收完成 /***********************MSP430串口1初始化*************************/ void UART1_Init();//MSP430串口1初始化 void UART1_Setbaud(u16 bps); //串口1设置波特率函数 void UART1_Send_Byte(u8 data);//串口1发送数据函数 u8 UART1_RX_SR(void);//判断串口1是否接收完成 //串口1,printf函数 //确保一次性发送的数据不超过200个字节 void u3_printf(char* fmt,...); int putchar(int c);//向终端输出一个字符,为了支持printf函数 /*****************************时钟配置***********************************************************/ void Clock_Init_DCO();//设置内部DCO振荡器初始化函数 void Clock_Init_XT2();//设置外部高频XT2振荡器时钟初始化函数 void Clock_Init_XT1();//设置外部低频XT1振荡器时钟初始化函数 /*************************Config.c*********************************/ #include"stdarg.h" #include"stdio.h" #include"string.h" #include"Config.h" #include<msp430x14x.h> u8 INT_flag=0; //定时器中断标志 u8 USART_RX_BUF[200]; //串口0接收缓冲区,最大200个字节 u32 USART_RX_COUNT=0; //串口0接收字节计数 u8 USART1_RX_BUF[200]; //串口1接收缓冲区,最大200个字节 u32 USART1_RX_COUNT=0; //串口1接收字节计数 u8 USART1_TX_BUF[200]; //串口1最大发送字节缓存区 /*****************************看门狗配置**************************************************/ //看门狗初始化函数,关闭看门狗 void WDT_Init_OFF() { WDTCTL=WDTPW+WDTHOLD; //将WDTCTL寄存器的WDTPW和WDTHOLD位置1 } /*****************************IO口配置,包括LED和按键****************************************************/ void Port_Init() { P1SEL = 0x00; //P1普通IO功能,KEY,KEY,KEY P1DIR = 0xF0; //P10~P13输入模式,外部电路已接上拉电阻 P6SEL = 0x00; //P6口普通IO功能,LED,LED,LED P6DIR = 0xFF; //P6口输出模式 P6OUT = 0XFF; //设置P6初始值输出为0xFF } /******************************按键扫描函数,返回按键值,mode:0:不支持连按;1:支持连按***********************/ u8 KEY_Scan(u8 mode) { u8 key_check; static u8 key_up=1;//按键松开标志 if(mode)key_up=1; //支持连按 key_check=P1IN; //读取IO口状态 key_check &= 0x0F; if(key_up&&key_check!=0x0F) //判断是否有键按下 { delay_ms(10);//键盘消抖,延时10MS key_up=0; if(key_check==0x0E)return 1; else if(key_check==0x0D)return 2; else if(key_check==0x0B)return 3; else if(key_check==0x07)return 4; } else if(key_check!=0x0E&&key_check!=0x0D&&key_check!=0x0B&&key_check!=0x07)key_up=1;//必须确认按键全部松开,才能使按键松开标志置1,很重要!!! return 0;//无按键按下 } /*****************************定时器配置**************************************************/ //TIMERA初始化程序,增计数模式MC0 void TimerA_Init_MC0(void) { TACTL=TASSEL0+TACLR; //设置时钟源来自ACLK=32.768KHz,并清零计数器,需要先初始化时钟XT1 TACTL |= MC0; //设置增计数模式MC0 TACCR0=32768-1; //设置计时时间为一秒:32768/32768s TACTL |= TAIE; //允许计时器溢出中断 _EINT(); //开总中断 } //TIMERA初始化程序,连续计数模式MC1 void TimerA_Init_MC1(void) { TACTL=TASSEL0+TACLR; //设置时钟源来自ACLK=32.768KHz,并清零计数器,需要先初始化时钟XT1 TACTL |= MC1; //设置增计数模式MC1,65535/32768s TACTL |= TAIE; //允许计时器溢出中断 _EINT(); //开总中断 } //中断服务子函数 #pragma vector=TIMERA1_VECTOR __interrupt void TIMERA_IRQ(void) { switch(TAIV) //需要判断中断的类型 { case 2:break; case 4:break; case 10:INT_flag=1;break; //设置标志位Flag } } /*****************************串口0配置****************************************************/ /MSP430串口0初始化 void UART0_Init() { U0CTL|=SWRST; //复位SWRST U0CTL|=CHAR; //8位数据模式 U0TCTL|=SSEL1; //SMCLK为串口时钟 U0BR1=baud_h; //BRCLK=8MHZ,Baud=BRCLK/N U0BR0=baud_l; //N=UBR+(UxMCTL)/8 U0MCTL=0x00; //微调寄存器为0,波特率9600bps ME1|=UTXE0; //UART0发送使能 ME1|=URXE0; //UART0接收使能 U0CTL&=~SWRST; //IE1|=URXIE0; //接收中断使能位 P3SEL|= BIT4 + BIT5; //设置3.4,3.5为功能端口,UART口功能 P3DIR|= BIT4; //设置3.4口方向为输出 } //串口0发送数据函数 void Send_Byte(u8 data) { U0TXBUF=data; while((IFG1&UTXIFG0)==0); //等待中断标志位复位,应该会自动复位 } //处理来自串口 0 的接收中断 #pragma vector=UART0RX_VECTOR __interrupt void UART0_RX_ISR(void) { USART_RX_BUF[USART_RX_COUNT]=U0RXBUF; //将收到的数据放在缓存区 USART_RX_COUNT++; //收到的字节数加一 } //判断串口0是否接收完成 u8 UART0_RX_SR(void) { u32 temp=0; temp=USART_RX_COUNT; //当前计数值赋给temp delay_ms(10); //延时用来判断接收是否完成 if(temp==USART_RX_COUNT&&USART_RX_COUNT!=0) //判断计数值是否等于当前值,即判断是否接收完成 return 1; //接收完成,返回1 else return 0; //未接收完成,返回0 } //处理来自串口 0 的发送中断,预留 #pragma vector=UART0TX_VECTOR __interrupt void UART0_TX_ISR(void) { } //向终端输出一个字符,为了支持printf函数 int putchar(int c) { if(c=='\n') { U0TXBUF='\r'; while((IFG1&UTXIFG0)==0); //发送寄存器空的时候发送数据 } U0TXBUF=c; while((IFG1&UTXIFG0)==0); //发送寄存器空的时候发送数据 return c; } /*****************************串口1配置****************************************************/ void UART1_Init() { U1CTL|=SWRST; //复位SWRST U1CTL|=CHAR; //8位数据模式 U1TCTL|=SSEL1; //SMCLK为串口时钟 U1BR1=baud_h; //BRCLK=8MHZ,Baud=BRCLK/N U1BR0=baud_l; //N=UBR+(UxMCTL)/8 U1MCTL=0x00; //微调寄存器为0,波特率9600bps ME2|=UTXE1; //UART1发送使能 ME2|=URXE1; //UART1接收使能 U1CTL&=~SWRST; IE2|=URXIE1; //接收中断使能位 _EINT(); //开中断 ,为串口接收中断服务 P3SEL|= BIT6 + BIT7; //设置3.4,3.5为功能端口,UART口功能 P3DIR|= BIT6; //设置3.4口方向为输出 } void UART1_Setbaud(u16 bps) //串口1设置波特率函数 { u32 baud_set=(u32)((u16)CPU_F/((u16)bps)); //波特率计算公式 u8 bps_h=(u8)(baud_set>>8); //提取高位 u8 bps_l=(u8)(baud_set); //低位 U1BR1=bps_h; //BRCLK=8MHZ,Baud=BRCLK/N U1BR0=bps_l; //N=UBR+(UxMCTL)/8 U1MCTL=0x00; //微调寄存器为0 } //串口1发送数据函数 void UART1_Send_Byte(u8 data) { U1TXBUF=data; while((IFG2&UTXIFG1)==0); //等待中断标志位复位,应该会自动复位 } //处理来自串口 1 的接收中断 #pragma vector=UART1RX_VECTOR __interrupt void UART1_RX_ISR(void) { USART1_RX_BUF[USART1_RX_COUNT]=U1RXBUF; //将收到的数据放在缓存区 USART1_RX_COUNT++; //收到的字节数加一 } //判断是否接收完成 u8 UART1_RX_SR(void) { u32 temp=0; temp=USART1_RX_COUNT; //当前计数值赋给temp delay_ms(10); //延时用来判断接收是否完成 if(temp==USART1_RX_COUNT&&USART1_RX_COUNT!=0) //判断计数值是否等于当前值,即判断是否接收完成 return 1; //接收完成,返回1 else return 0; //未接收完成,返回0 } //处理来自串口 1 的发送中断,预留 #pragma vector=UART1TX_VECTOR __interrupt void UART1_TX_ISR(void) { } //串口1数据发送函数(主要用来和ESP8266之间的通信) //确保一次性发送的数据不超过200个字节 void u3_printf(char* fmt,...) { u16 i,j; va_list ap; va_start(ap,fmt); vsprintf((char*)USART1_TX_BUF,fmt,ap); va_end(ap); i=strlen((const char*)USART1_TX_BUF);//得到此次发送数据的长度 for(j=0;j<i;j++) //循环发送数据 { U1TXBUF=USART1_TX_BUF[j]; while((IFG2&UTXIFG1)==0); //等待中断标志位复位,应该会自动复位 } } /*****************************时钟配置***********************************************************/ //设置内部DCO振荡器初始化函数 void Clock_Init_DCO() { DCOCTL=DCO0+DCO1+DCO2; //DCO选为最大的频率,0,1,2三位均输出为1 BCSCTL1|=XT2OFF; //关闭外部高频晶体 BCSCTL1=RSEL0+RSEL1+RSEL2; //最大标称频率,大概5MHz } //设置外部高频XT2振荡器时钟初始化函数 void Clock_Init_XT2() { u8 i; BCSCTL1 &=~XT2OFF; //打开XT2晶振,XT2OFF写0 BCSCTL2 |=SELM1+SELS; //MCLK=SMCLK=XT2,8MHz,0x80+0x08 /*判断晶振是否稳定*/ do { IFG1 &=~OFIFG; //清除OFIFG标志位 for(i=0;i<100;i++) //等待晶振稳定 _NOP(); } while((IFG1 & OFIFG)!=0); IFG1 &=~OFIFG; //清除OFIFG标志位 } //设置外部低频XT1振荡器时钟初始化函数 void Clock_Init_XT1() { u8 i; BCSCTL1 |= XT2OFF; //关闭XT2晶振,XT1默认打开 BCSCTL2 |=SELM1+SELM0; //MCLK=XT1,32.768KHz,0x80+0x40,SMCLK默认来自DCO备注:SMCLK时钟不能由XT1提供!!!!!!! /*判断晶振是否稳定*/ do { IFG1 &=~OFIFG; //清除OFIFG标志位 for(i=0;i<100;i++) //等待晶振稳定 _NOP(); } while((IFG1 & OFIFG)!=0); IFG1 &=~OFIFG; //清除OFIFG标志位 }
/********************************SIM900A.h*********************************/ #include"Config.h" void sim900a_test(void); //sim900a主测试程序 u8 sim900a_send_cmd(u8 *data,u8 *ack,u16 waittime);//向SIM900A发送指定命令(带打印回显) u8 sim900a_send_data(u8 *data,u8 *ack,u16 waittime);//向SIM900A发送指定数据(不带打印回显) u8* sim900a_send_check_cmd(u8 *str); //SIM900A发送命令后,检测接收到的应答 void sim900a_mtest_ui(void); //显示一些模块信息 u8 sim900a_gsminfo_show(void); //GSM信息显示(运营商,信号质量,电池电量,日期时间),返回0正常 u8 sim900a_gprs_test(const u8 *ip,const u8 *port); //sim900a GPRS测试,返回0正常 /********************************SIM900A.c*********************************/ #include"SIM900A.h" /***************GPRS连接的IP地址和端口PORT*********************/ //备注:1.可以连接网页的云端服务器(如安信可) // 2.可以通过开通隧道连接网络调试助手 //连接端口号:8086,根据实际情况修改为其他端口. const u8* portnum="8086"; //定义为连接的IP地址(注意要是公网IP) const u8 *IP_address="113.111.214.69"; //sim900a主测试程序 void sim900a_test(void) { u8 key=0; u8 timex=0; u8 sim_ready=0; printf("SIM900A 测试程序,GPRS测试\n"); while(sim900a_send_cmd("AT","OK",100))//检测是否应答AT指令 { printf("未检测到模块!!!\n"); delay_ms(800); printf("尝试连接模块...\n"); delay_ms(400); } printf("SIM900A连接成功\n"); sim900a_send_cmd("ATE0","OK",200);//关闭回显 sim900a_mtest_ui(); //加载主页面,显示制造商,模块名称,序列号 if(sim900a_gsminfo_show()==0)sim_ready=1;//SIM卡就绪(显示运营商,信号质量,电池电量,日期时间) printf("\nS3_PRE:GPRS测试\n"); while(1) { if(sim_ready==1)//SIM卡就绪(显示运营商,信号质量,电池电量,日期时间) { key=KEY_Scan(0); if(key) { switch(key) { case S1_PRE: //sim900a_call_test();//拨号测试 break; case S2_PRE: //sim900a_sms_test(); //短信测试 break; case S3_PRE: sim900a_gprs_test(IP_address,portnum);//GPRS测试(入口参数为IP地址和端口号) break; } timex=0; } } if((timex%20)==0)P6OUT^=BIT0;//200ms闪烁 timex++; } } //显示一些模块信息 void sim900a_mtest_ui(void) { u8 *p1; u8 p[50]; if(sim900a_send_data("AT+CGMI","OK",200)==0) //查询制造商名称 { p1=(u8*)strstr((const char*)(USART1_RX_BUF+2),"\r\n");//查找回车 p1[0]=0; //加入结束符,目的是吧\r\n后面数据截断 sprintf((char*)p,"制造商:%s",USART1_RX_BUF+2); printf("%s\n",p); USART1_RX_COUNT=0; //串口1发送数据个数清零 } if(sim900a_send_data("AT+CGMM","OK",200)==0)//查询模块名字 { p1=(u8*)strstr((const char*)(USART1_RX_BUF+2),"\r\n"); //查找回车 p1[0]=0; //加入结束符,目的是吧\r\n后面数据截断 sprintf((char*)p,"模块型号:%s",USART1_RX_BUF+2); printf("%s\n",p); USART1_RX_COUNT=0; //串口1发送数据个数清零 } if(sim900a_send_data("AT+CGSN","OK",200)==0) //查询产品序列号 { p1=(u8*)strstr((const char*)(USART1_RX_BUF+2),"\r\n");//查找回车 p1[0]=0; //加入结束符,目的是吧\r\n后面数据截断 sprintf((char*)p,"序列号:%s",USART1_RX_BUF+2); printf("%s\n",p); USART1_RX_COUNT=0; //串口1发送数据个数清零 } } //SIM900A发送命令后,检测接收到的应答 //str:期待的应答结果 //返回值:0,没有得到期待的应答结果 // 其他,期待应答结果的位置(str的位置) u8* sim900a_send_check_cmd(u8 *str) { char *strx=0; if(UART1_RX_SR()) //接收到一次数据了 { USART1_RX_BUF[USART1_RX_COUNT]=0;//添加结束符 strx=strstr((const char*)USART1_RX_BUF,(const char*)str); } return (u8*)strx; } //向SIM900A发送指定数据 //data:发送的数据(不需要添加回车了) //ack:期待的应答结果,如果为空,则表示不需要等待应答 //waittime:等待时间(单位:10ms) //返回值:0,发送成功(得到了期待的应答结果) // 1,发送失败 u8 sim900a_send_cmd(u8 *data,u8 *ack,u16 waittime) { u8 res=0; USART1_RX_COUNT=0; //串口1发送数据个数清零 if((u32)data<=0XFF) //需要发送的是数据 { U1TXBUF=(u16)data; //将数据通过串口发送出去 while((IFG2&UTXIFG1)==0); //等待中断标志位复位,应该会自动复位 } else u3_printf("%s\r\n",data); //需要发送的是命令 if(ack&&waittime) //需要等待应答 { while(--waittime) //等待倒计时 { delay_ms(10); if(UART1_RX_SR())//接收到期待的应答结果 { if(sim900a_send_check_cmd(ack)) { printf("%s_ack:%s\r\n",data,(u8*)ack); break;//得到有效数据 } USART1_RX_COUNT=0;//串口1接受数据清零 } } if(waittime==0)res=1; } return res; } //向SIM900A发送指定数据 //data:发送的数据(不需要添加回车了) //ack:期待的应答结果,如果为空,则表示不需要等待应答 //waittime:等待时间(单位:10ms) //返回值:0,发送成功(得到了期待的应答结果) // 1,发送失败 u8 sim900a_send_data(u8 *data,u8 *ack,u16 waittime) { u8 res=0; USART1_RX_COUNT=0; //串口1发送数据个数清零 if((u32)data<=0XFF) //需要发送的是数据 { U1TXBUF=(u16)data; //将数据通过串口发送出去 while((IFG2&UTXIFG1)==0); //等待中断标志位复位,应该会自动复位 } else u3_printf("%s\r\n",data); //发送命令 if(ack&&waittime) //需要等待应答 { while(--waittime) //等待倒计时 { delay_ms(10); if(UART1_RX_SR())//接收到期待的应答结果 { if(sim900a_send_check_cmd(ack)) break; USART1_RX_COUNT=0;//串口1接受数据清零 } } if(waittime==0)res=1; } return res; } //GSM信息显示(运营商,信号质量,电池电量,日期时间) //返回值:0,正常 // 其他,错误代码 u8 sim900a_gsminfo_show(void) { u8 *p1,*p2; u8 p[50]; u8 res=0; USART1_RX_COUNT=0; //串口1接受数据清零 printf("\n"); if(sim900a_send_data("AT+CPIN?","OK",200))res|=1<<0; //查询SIM卡是否在位 USART1_RX_COUNT=0; //串口1接受数据清零 if(sim900a_send_data("AT+COPS?","OK",200)==0) //查询运营商名字 { p1=(u8*)strstr((const char*)(USART1_RX_BUF),"\""); if(p1)//有有效数据 { p2=(u8*)strstr((const char*)(p1+1),"\""); p2[0]=0;//加入结束符 sprintf((char*)p,"运营商:%s",p1+1); printf("%s\n",p); } USART1_RX_COUNT=0; //串口1接受数据清零 }else res|=1<<1; if(sim900a_send_data("AT+CSQ","+CSQ:",200)==0) //查询信号质量 { p1=(u8*)strstr((const char*)(USART1_RX_BUF),":"); p2=(u8*)strstr((const char*)(p1),","); p2[0]=0;//加入结束符 sprintf((char*)p,"信号质量:%s",p1+2); printf("%s\n",p); USART1_RX_COUNT=0; //串口1接受数据清零 }else res|=1<<2; if(sim900a_send_data("AT+CBC","+CBC:",200)==0) //查询电池电量 { p1=(u8*)strstr((const char*)(USART1_RX_BUF),","); p2=(u8*)strstr((const char*)(p1+1),","); p2[0]=0;p2[5]=0;//加入结束符 sprintf((char*)p,"电池电量:%s%% %smV",p1+1,p2+1); printf("%s\n",p); USART1_RX_COUNT=0; //串口1接受数据清零 }else res|=1<<3; if(sim900a_send_data("AT+CCLK?","+CCLK:",200)==0) //查询电池电量 { p1=(u8*)strstr((const char*)(USART1_RX_BUF),"\""); p2=(u8*)strstr((const char*)(p1+1),":"); p2[3]=0;//加入结束符 sprintf((char*)p,"日期时间:%s",p1+1); printf("%s\n",p); USART1_RX_COUNT=0; //串口1接受数据清零 }else res|=1<<4; return res; } //sim900a GPRS测试 //用于测试TCP/UDP连接 //返回值:0,正常 // 其他,错误代码 u8 sim900a_gprs_test(const u8 *ip,const u8 *port) { u8 *p2,*p3; u8 key; u16 timex=0; u32 rlen=0; u8 connectsta=0; //0,正在连接;1,连接成功;2,连接关闭;3.退出测试; u8 hbeaterrcnt=0; //心跳错误计数器,连续8次心跳信号无应答,则重新连接,9,退出测试 u8 p[50]; u8 p1[50]="华盛顿"; //要发送的数据缓存区 u8 p5[50]; //接受的数据缓存区 printf("ip=%s\n",ip); //ip地址显示 printf("port=%s\n",port); //端口显示 sim900a_send_cmd("AT+CIPCLOSE=1","CLOSE OK",100); //关闭连接 sim900a_send_cmd("AT+CIPSHUT","SHUT OK",100); //关闭移动场景 if(sim900a_send_cmd("AT+CGCLASS=\"B\"","OK",1000))return 1; //设置GPRS移动台类别为B,支持包交换和数据交换 if(sim900a_send_cmd("AT+CGDCONT=1,\"IP\",\"CMNET\"","OK",1000))return 2; //设置PDP上下文,互联网接协议,接入点等信息 if(sim900a_send_cmd("AT+CGATT=1","OK",500))return 3; //附着GPRS业务 if(sim900a_send_cmd("AT+CIPCSGP=1,\"CMNET\"","OK",500))return 4; //设置为GPRS连接模式 if(sim900a_send_cmd("AT+CIPHEAD=1","OK",500))return 5; //设置接收数据显示IP头(方便判断数据来源) printf("TCP连接\n"); sim900a_send_cmd("AT+CLPORT=\"TCP\",\"2000\"","OK",500);//设置本地 TCP 连接,端口为 2000 sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",\"%s\"",ip,port); if(!sim900a_send_cmd(p,"OK",500)) //发起连接 printf("TCP连接成功\r\n"); else printf("TCP连接失败\r\n"); printf("S2_PRE:退出测试状态下重新进行连接\n"); printf("S3_PRE:发送数据\n"); printf("S4_PRE:退出测试\n"); while(1) { key=KEY_Scan(0); if(key==S2_PRE&&(connectsta==3)&&(hbeaterrcnt==9)) //退出测试状态下重新进行连接 { printf("退出测试状态下重新进行连接\n"); connectsta=2; //正在连接 } if(key==S3_PRE&&(hbeaterrcnt==0)) //发送数据(心跳正常时发送) { printf("\n数据发送中...\n"); if(sim900a_send_data("AT+CIPSEND",">",500)==0) //发送数据 { printf("CIPSEND DATA:%s\r\n",p1); //发送数据打印到串口 u3_printf("%s",p1); //通过模块向外发送数据 delay_ms(10); if(sim900a_send_data((u8*)0X1A,"SEND OK",1000)==0)printf("数据发送成功\n");//发送结束符 else printf("数据发送成功\r\n"); } else sim900a_send_cmd((u8*)0X1B,0,0); //ESC,取消发送 } if(key==S4_PRE) //退出测试 { sim900a_send_cmd("AT+CIPCLOSE=1","CLOSE OK",500); //关闭连接 sim900a_send_cmd("AT+CIPSHUT","SHUT OK",500); //关闭移动场景 connectsta=3; //退出测试状态 hbeaterrcnt=9; //退出测试状态 printf("退出测试,按S2重新连接\n"); } if((timex%20)==0) //200ms动作: { P6OUT^=BIT0; //LED闪烁 if(connectsta==2||hbeaterrcnt==5)//连接中断了,或者连续5次心跳没有正确发送成功,则重新连接 { sim900a_send_cmd("AT+CIPCLOSE=1","CLOSE OK",500); //关闭连接 sim900a_send_cmd("AT+CIPSHUT","SHUT OK",500); //关闭移动场景 /*************************重新进行TCP连接***************************************/ printf("重新进行TCP连接\n"); sim900a_send_cmd("AT+CLPORT=\"TCP\",\"2000\"","OK",500);//设置本地 TCP 连接,端口为 2000 sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",\"%s\"",ip,port); if(!sim900a_send_cmd(p,"OK",500)) //发起连接 printf("重新进行TCP连接成功\n"); else printf("重新进行TCP连接失败\n"); connectsta=0; //连接状态指示为正在连接 hbeaterrcnt=0; //心跳计数清零 } } if(connectsta==0&&(timex%200)==0)//连接还没建立的时候,每2秒查询一次CIPSTATUS. { sim900a_send_cmd("AT+CIPSTATUS","OK",500); //查询连接状态 if(strstr((const char*)USART1_RX_BUF,"CLOSED"))connectsta=2; //连接关闭 if(strstr((const char*)USART1_RX_BUF,"CONNECT OK"))connectsta=1;//连接成功 } if(connectsta==1&&timex>=600)//连接正常的时候,每6秒发送一次心跳 { timex=0; if(sim900a_send_data("AT+CIPSEND",">",200)==0)//发送数据 { sim900a_send_data((u8*)0X00,0,0); //发送数据:0X00 delay_ms(20); //必须加延时 sim900a_send_data((u8*)0X1A,0,0); //CTRL+Z,结束数据发送,启动一次传输 }else sim900a_send_data((u8*)0X1B,0,0); //ESC,取消发送 hbeaterrcnt++; //心跳数加一,计数值不为零,则可以在接受中判断心跳应答 printf("hbeaterrcnt:%d\r\n",hbeaterrcnt); //方便调试代码 } delay_ms(10); //检查GSM模块发送过来的数据,及时上传给电脑 if(UART1_RX_SR()) //接收到一次数据了 { rlen=USART1_RX_COUNT; //得到本次接收到的数据长度 USART1_RX_BUF[rlen]=0; //添加结束符 if(hbeaterrcnt) //需要检测心跳应答 { if(strstr((const char*)USART1_RX_BUF,"SEND OK"))//如果心跳正常 hbeaterrcnt=0; //心跳计数清零 } p2=(u8*)strstr((const char*)USART1_RX_BUF,"+IPD"); //获得"+IPD"在USART1_RX_BUF的位置 if(p2) //收到"+IPD",即p2指针不为空,接收到的是TCP/UDP数据 { p3=(u8*)strstr((const char*)p2,","); p2=(u8*)strstr((const char*)p2,":"); p2[0]=0; //加入结束符 sprintf((char*)p5,"收到%s字节,内容如下:",p3+1);//接收到的字节数 printf("%s\n",p5);//显示接收到的数据长度 printf("%s\n",p2+1);//显示接收到的数据 } USART1_RX_COUNT=0; } timex++; } }
1.若是可以访问路由器,则可通过设置DMZ转发规则,使得内网向外网映射,网上有很多设置教程,不详细讲。
2.若是不可以访问路由器(如教育网,或者公司网络),则可以通过开辟隧道的方式进行映射(ngrok),具体教程网上也有,这里只附上登录网址:ngrok访问网址:https://www.ngrok.cc/user.html
通过该网站上的配置建立TCP连接隧道,然后通过配置文件连接隧道,使隧道在线。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。