前一阵之做了一个2G模块作客户端通过内置协议栈上网的项目,我只负责库文件,业务层的实现是由别人完成的。这里想就过程中遇到的问题做个总结,算是学习笔记。
注:以下内容都是本人在工作过程中的自己思考和向前辈工程师们讨教的结果,若有侵权,请与我联系。
首先想说一下什么是内置协议栈。说白了就是模块内部已经集成了TCP/IP协议栈,开发者只需要调用模块供应商提供的AT指令接口就可以实现数据的收发。就像调用标准的LINUX库函数一样,fread就能从一个文件描述符指向的地方读取到数据。
顺带一提外置协议栈。一般指在操作系统中通过ppp的方式拨号上网,应用程序进程和内核中的ppp进程交互,建立数据链路层通道,协商IP协议,协商传输层协议(具体请查阅ppp拨号上网的流程)。拨号成功之后,开发者利用标准的socket编程接口,就可以实现网络进程间的数据传输。
之所以没用外置协议栈的方法,是因为2G模块只有一个串口,既接收AT指令,又用来做数据收发,这样造成的问题是:在拨号成功后,该串口上走的都是ppp协议封装的数据,模块将不能识别应用程序发送的AT指令(将拨号进程kill掉之后可以),而模块的自动上报信息也不能被应用程序识别。若此时有语音电话打进来,模块上报RING,但是应用程序端不能识别,因此不会发出接电话的指令ATA。而内置协议栈将不会有这种问题,因为它完全通过AT指令来控制模块,应用程序和模块之间用串口通信进行交互。
(不同模块的查询方式和回复字符串不同,具体依照手册)
1、AT+CPIN?查看SIM卡是否已准备好
2、AT+CGREG查看模块是否已经注册GPRS网络,如果没有需等待模块注册
3、AT+CSQ查询网络信号值,信号较好时,一般在29---31,若只有12、13,可能连接不上服务器
4、AT+CGATT?查询是否附着GPRS网络,若没有,可手动附着AT+CGATT=1
5、AT+CIPSHUT关闭移动场景。在初始化阶段不确定模块的IP STATE是什么状态,可统一做这个操作,它将关闭所有已连接的TCP/UDP连接,并且将IP STATE置为IP INITIATE,即初始化状态。因为模块需要在该状态才能执行激活移动场景的操作。若模块返回SHUT OK,则可继续一下步骤
6、AT+CIPMUX=1,若=1表示可使用多IP连接,若=0表示单链接
7、AT+CSTT="apn","username","password" apn表示网络接入点,比如移动用CMNET,username和password表示入网的名户名和码,这将会在建立数据链路层的做验证的时候用到(PAP或者CHAP)
8、AT+CIICR激活移动场景。这条指令发出后,可能等待几十秒或更长,若模块返回OK,则表示移动场景已经激活。个人理解激活移动场景就是数据链路已经打通,可以在此基础上建立TCP/UDP连接了。
9、AT+CIFSR获取本地IP,必须有这一步才能使IP STATE变成IP STATUS,后续去建立具体的连接时才能成功。若指令正确,模块将返回随机IP
10、AT+CIPQSEND=1设置发送数据的回显格式,具体参考文档
以上就做完了初始化工作。接下来,如果应用程序需要与某个服务器建立连接。可使用指令:
AT+CIPSTART=id,"type","ipaddr",port 其中id为连接号,是一个整数,SIMCOM800模块规定为(0-5),这表示该模块最多同时支持6个连接。type为连接类型,支持TCP和UDP,ipaddr是服务器的IP地址,port为端口号。若之前第6步设置为0,则不用发送id。
若连接成功,模块将返回CONNECT OK,连接失败将返回CONNECT FAIL。还有一种情况,当时也是花了久才明白是什么意思。模块可能返回ALREADY CONNECT,一开始我以为是说这个连接已经建立,可以收发数据了,但是AT+CIPSEND或AT+CIPRXGET时都返回CME ERROR,也就是收发数据都错误。后来发现只是表示当前的id表示的这个连接,已经有一个连接正在进行,状态为CONNECTING,可能因为网络延迟,服务器错误等原因没有及时返回成功或失败。
接下来发送数据,有两种方式:
(1)AT+CIPSEND=id, length id就是之前建立连接的连接号,length表示本次将要发送的数据长度(单位为byte,SIM800模块要求小于1460个字节)。若可以发送,模块将回复 > ,然后在向串口发送n个字节长度的数据。这里要注意,如果n = length,也是最一般的情况,数据能正常发送到网络,模块将回复DATA ACCEPT:n,单连接回复SEND OK(这里其实也只是通过串口发送给模块内部的buffer而已,并不一定是发送到了网络侧)。若n > length,则超出的部分将被认为是AT指令,由于该部分一般都不可能是AT指令格式,所有模块很可能回复ERROR。若n<length,模块将仍处于发送数据模式,若此时应用程序再发一条AT指令,则该条指令的前length-n个字节将被认为是数据发送到网络侧,而AT指令将无法响应。
(2)AT+CIPSEND=id id的意义如上所述。没有指定发送长度,模块以收到0x1A为结束符,组合键为Ctrl+Z的键值。如果要发送的数据中包含0x1A,不建议使用这种方法。
接收数据,也有两种方式:
(1) AT+CIPRXGET=2,id,length 手动获取数据,2表示获取数据,id如上,length表示想要获取的长度,但模块不一定返回这么长,这要看当前的网络环境。使用这种方式需要在AT+CIICR之前发送指令AT+CIPRXGET=1
(2) 自动上报数据,不用发送指令,当模块收到数据后,自动通过串口发送过来。AT+CIPRXGET=0或者不设置,即默认状态就是这样。上报格式为+RECEIVE:id,length,data....,意义都很明确。
想关闭单个连接,可以使用AT+CIPCLOSE=id。想关闭移动场景,可使用AT+CIPSHUT