基于GPS,GPRS远程监控系统的实现
----一切只为找到你,不管在世界的哪个角落
自从寒假回家,目前为止桥哥已经在家宅了半个月,终于可以出篇技术贴了。为了迎接年后从事的物联网相关方面的工作,所以打算学学GPRS进行一下数据传输,不然以后怎么好意思讲是做物联网的呢。用GPRS传GPS数据是一件两全其美的事情,因为又可以顺带学习一下GPS相关知识。对于帖子的名字,通常我们都会取得比较正式又显得略微碉堡的样子通常以“基于****的实现”为题,搞起来像篇论文一样,但是实际上只是一篇随性的学习笔记而已,网络上很多前辈给我们提供了很多资料笔记,我们也应该回馈上去一点。
下面简单画画整个系统的框图:
从框图中可以清楚的看到整个系统的工作流程是非常简单的,被监测端采用的是Cortex-M3内核的stm32微控制器,GPS负责接收卫星信号,然后根据计算得到地标信息通过串口传送给stm32,stm32接收GPS数据进行相应的打包处理再通过串口发送给GPRS模块,GPRS模块通过基站传送到Internet网络中向指定IP的端口PC发送数据包,服务器端上位机通过监控该端口来捕获数据,然后上位机通过串口再次将GPS数据发送到Google地球或者其它导航软件中实现对接,以便实时监测观察。在朋友的提示和帮助下,事实上本系统还加入了基站辅助定位功能,由于GPS卫星信号的接收必须在室外,所以当被监控端处于室内或者在接收不到卫星信号的恶劣情况下,系统仍然能够通过基站信息定位到大致的区域。
GPS使用的是微科电子(香港)公司的VK1513模块,选择它主要还是价格方面比较经济实惠,实测效果也不错。GPS模块会每秒钟发出数据包,数据包是遵循NMEA-0183协议的,可以看看我实际接收下来的数据包:
主要包含定位信息的是GPGGA以及GPRMC,两个数据包中都包含有时间以及经纬度等信息,因为我主要需要的就是当前的经纬度信息就可以了,也为了减小GPRS数据传输负担,因此我只选择了将GPRMC这条数据进行传输,关于其数据包中具体每个数据的含义可以参照NMEA-0183的说明。值得注意的是,数据包都是以$开头,以回车换行结尾的。回车换行用转义符号表示便是rn,这点忽视的话,那么服务器端与地图软件的对接将不会成功。
因此我在stm32的程序上做了判断处理,只传送GPRMC数据包,并且同样为了再次减小GPRS传送压力,每接收到两个数据包传送一个就可以了。实现代码如下
接收GPS串口发来的数据通过串口中断实现,接收代码如下。对于这段程序有点比较有意思的事情,这段代码是在我单独调试GPS的时候写的,想法也很简单,以$触发开始接收,遇到’n’结束,然后当时就出现了一个很不合我逻辑的问题,反复读了几十遍代码没有发现问题,如我注释中写的为什么放入结束符的那个地址要减一,这是实际调试出来的,就没管它一直这样用了,前几天才意识到上面说的是以”rn”结尾的这个问题,这才恍然大悟,原来还有一个压根看不见摸不着的’r’。这也受我们平时用电脑的习惯有影响,当敲下键盘上的回车键,实际上执行了两步操作,回车和换行,这谁看得见呢。
GPRS模块采用的是华为GTM900C模块,比较常见的一般也就是SIM900和GTM900,选择华为的主要还是因为国产,数据手册肯定有中文官方原版的,所以学习起来不那么费劲,桥哥可不想在大好假日被英文给磨掉学习的信心。对于其TCP/IP链接的建立,可以参照华为GTM900TCP_IP使用指导书。我在程序中已列举了我所用到的AT指令:
实际上,这个指令定义的顺序已经表明了我在初始化GTM900C并进行TCP链接的初始化过程:
1、关闭回显,回显的意思是你给它发条指令它会原封不动的给你回发一份你发的指令,这里关掉,以便串口专心监测GTM900C实际返回的参数
2、设置GPRS网络附着,将模块附着到GPRS网络中,下面的操作都是基于GPRS网络下进行的,设置它的目的是保证下面的操作能够顺利进行。如果已经附着上GPRS网络,而再次发送此命令,会返回ERROR 8,不过没有关系不影响附着状态,以防万一每次建立链接我都发。
3、配置APN,即GPRS接入点的配置
4、进入TCPIP功能,网络传输基于这个协议,那是必须。
5、域名解析,这点很重要值得特别说明。实际上GTM900C建立一条TCP链接最后用的都必须是这条指令:AT%IPOPEN="TCP","111.164.240.229",1332,这个指令里面包含的是我们接收端作为服务器的IP地址和端口。我们知道网络上标识唯一一台电脑的地址用的是IP地址,这个由于不好记诞生了域名,例如www.360.cn,这个域名实际上也是指向了一个IP,为了让这个域名指向IP就需要一个域名服务器来解析。而家里拨号上网,每次拨号都会动态的分配到一个IP,况且桥哥家最近是一天掉线20多次,IP随时都会变化,不可能每次变化我都更改stm32的程序然后重新烧录,因此我用的一个固定的域名xiaoiqiao.oicp.net来跟我的IP进行实时绑定,而GTM900C模块每次建立链接都要先得到这个固定域名指向的IP,然后根据IP进行链接,确保无论我当前的IP是多少,它都可以正确连上我这里。而提供这个域名解析到IP服务的是花生壳这款软件,注册一个账号便得到一个免费的域名,并且为该域名提供免费的域名解析服务。
6、正式建立一条TCP链接。得到上步所说的返回IP之后,经过封装,可以形成下面这条指令AT%IPOPEN="TCP","113.14.177.44",1332;后面的1332是绑定的端口名,大于256的都可以只要跟上位机软件的监听端口对应就行了,这里我固定为1332,因为它是我手机尾号非常好记。此时监控端的上位机服务器要开启监听了,否则,它跟谁链接呢。
上位机软件启动后会自动获取到本机的IP地址,只要填好监听端口,然后点击启动服务就可以了。这里由于我的电脑经过了路由器,所以IP是一个子网IP,要是没有经过路由器,这里的IP应该是我们上面看到的那个113.14.177.44的外网IP。过路由器就会产生一些麻烦事情,因为路由器接收到GTM900C发的数据的时候,它怎么知道是给自己旗下的哪台电脑呢,并且其默认安全设置是不允许外界对它管辖的电脑发送链接请求的,因此,我们还得设置路由器的转发规则,跟它打好招呼,让其接收到1332端口数据的时候,自动转发到旗下的192.168.1.100这台电脑里。
因此在校园网下,很难做这个实验,校园网是一个大局域网,主路由器我们无法配置,永远收不到被监测端发来的数据。
7、接下来便进行判断是否已经正确建立了链接,如果正确链接了,模块会返回CONNET参数,如果链接不成功, 那么将会返回ERROR: N,表示错误的类型。为了不那么麻烦,发现链接不成功则从头开始继续以上操作进行 链接,唯独对于ERROR: 9进行了一步额外的处理。假如模块之前正确链接了我们的上位机服务器,而由于某种原因网络断线,导致我们跟模块的链接已经断开,而模块却没有收到移动服务台发出的断开链接信息,此 种情况之后我们让GTM900C链接服务器,它会认为之前的链接根本没有断开,因此返回ERROR: 9表示链接已经存在,而事实是已经断开了,即使返回第一步重新操作一遍也是一样的结果,这是非常危险的一个死循环,因此,我做的处理便是用指令”AT%IPCLOSE=5”把网络注销,注销网络必然断开了所有链接,然后再从第1步初始化。当然每次链接失败到返回重新初始化我有10s钟的延时时间。
8、成功链接之后,便是设置IO模式,我用的是明文ASCII模式,即不需要什么转换,stm32给它写什么字符串GTM900C就会发出什么字符串,注意stm32给GTM900C的发送数据一定要封装成以下格式:AT%IPSEND="发送字符串数据”;并且根据官方文档推荐的,每次数据传送不要超过512字节。
9、初始化配置完成之后,第一步便是获取当前所用基站的信息,然后传送给我们的监测服务器,以便我们用来做基站定位使用。
10、接下来便是一直在等待GPS的数据接收完成,每接收完两次GPRMC数据,便向服务器发送一次用来做GPS定位监测。并且我还开启了一个定时服务,系统运行每三分钟会检测一下是否与服务器还处在链接状态,避免不可抗力因素造成断线而GTM900C却被蒙在鼓里。如果链接断开,将会向服务器重新发起链接命令。如果链接正常,那么将会再次获取一下基站信息,发送给服务器。所以服务器监测端会每三分钟更新基站信息,基站定位本来就只是个大范围,没有必要实时更新。
实际上,我在stm32上移植了uCGUI,进行相关信息的显示,用以观察当前的模块状态与命令返回参数,以便进行调试,例如此时返回的便是域名解析的IP地址信息。前段时间也移植成功了uC-OS-II但是我并不敢用于这里,因为对于这个实时操作系统我还非常不了解,以免给我调试增加不必要的难度。
接下来,我们来看看服务器监测端,服务端就是我们用于监控的那端,说它是服务器是相对于GTM900C来讲的,因为GTM900C是以一个客户端的身份跟我们进行链接。实际上它就是一个可以接受GTM900C的链接和数据的软件而已。
此软件是在XP系统VC6.0的环境下用VC++编写的。使用的参考资料是明日科技出版的系列图书,明日科技出版的系列编程图书是不错的资料,实例丰富,对于我这不专业的人来讲,很多需要的代码都可以直接从上面拿来主义,做些简单的工控界面效果还是不错的。四天前我做的界面其实不包括右边部分的基站定位功能,用软件截图发了一条说说之后,一位朋友跟我聊天提到这个基站定位,在前天全部调试测试完成之后,我决定再用一天时间加上这么一个基站定位功能,实现这个功能涉及到的是一些数据库相关的操作。
实现GPRS数据的接收其实还是简单的,收到的数据里面也已经有了经纬度信息,这些经纬度信息如何才能显示在地图上,当然是使用现成的地图软件了,但是数据怎么发给它们,我们没有它们的源代码,就像一个黑匣子,我们只有接口,并不知道里面是怎么实现的。那我就直接利用这个接口给它发数据便是最简单的方法了。并且我使用的是硬件接口从而完全绕开软件接口的开发难度。因此我的方案便是用两个串口直接对接,GPRS接收服务器将接收到的GPS数据通过其中一个串口发送出去,然后另一个串口便会收到数据,这个串口的数据就会自动被地图软件读入,这样就简单的实现了与地图软件的对接。这方法亲测对Google地球是可行的,对一般的只要是可以接收串口数据的导航软件也必都是可以的,它没有理由不行!
基站定位的实现原理也是比较简单的,前面说了stm32会每三分钟获取一次基站信息然后传回服务器监测端,那么我们就可以通过传回的信息检出LAC码和CELL码,这两个码唯一对应一个基站,就跟我们的身份证一样,那么我们只要通过这两个码来搜索数据库找出这个基站所在的经纬度便可以了,我手上这份数据库是比较老的,只有3万多条记录,不过我只是拿来做个试验,提供一种实现方法而已也就够了,没有的记录我可以通过网站的在线查询补充进去就可以了。
从数据库中得到经纬度之后,为了能够在地图上显示,我需要做的便是将经纬度数据封装成一个符合NMEA协议的GPRMC数据包。前面说了GPRMC中包括经纬度信息但是也包含其它信息,但是那些信息对于我们定位不重要,因此其它的信息我随便整了数据包里面的字符代替之就可以了。值得注意的是,数据库里提取出的经纬度是以dd.dddddd的形式的,而GPRMC中的经纬度数据是以ddmm.mmmm格式的,还需要简单的单位换算处理下,有换算和处理必然带来不可避免的误差,还有需要注意的是NMEA协议规定的数据包的最后两位字符是校验值,也就是$和*之间所有字符的异或结果,如果校验结果算得不对,地图软件也是无法识别的,再者就是别忘记了末尾加上”rn”。在VC中实现代码如下
接下来,我们便来实测一下系统运行效果吧
首先,附上GRPS数据发送端的整体运行实物图:
GPS天线放在窗户外面就可以收到卫星信号了。然后打开监测端的上位机软件,开启GPRS服务模式,等待客户端链接,链接成功之后,首先发送过来的是基站信息,然后开始源源不断的发送GPS数据
可以发现基站信息已经显示,但是基站的经纬度还没有显示出来,原因是在GPS定位模式下,我默认是不进行对基站数据库搜索的。
然后打开串口,开始与Google地球进行对接定位:
与Google地球对接的时候,波特率一定是要选择4800的,Google地球端也需要进行如下配置:在Google地区的工具/GPS菜单下,选择实时模式,然后选择NMEA协议,勾选自动遵循路径复选框,点击开始,Google地球便自动搜索符合条件的串口数据进行定位。
几秒之后,Google地球自动华丽的飞越到GPS数据定位的地方
黄色小点的那个地方就是我家了,精度还是不错的。我家就是在那座小山前面。
下面,来试试基站定位,在右下方定位数据输出方式那里点击基站定位按钮,对比前面的图可以发现经纬度已经显示,这个经纬度是从数据库中搜索得到的,然后此时软件给Google地球发送的数据便不是GPS传的实时GPRMC数据了,而是软件根据基站的经纬度计算出来的伪GPRMC数据,因为在计算的时候也是完全遵循NMEA协议的,所以Google地球同样能够接收。
此时在Google地球上看看此基站在什么地方可以发现,那个基站在离我家不远的右上方,实际上它的真实位置在靠左边一点呵呵,前面说了计算误差,不过没有关系,基站定位本来就是一个大概。你再细心一点可以对比前面的画面发现,它显示的时间是1月13号1:14分与今天的时间相差很远,那是因为我在计算伪GPRMC数据包的时候除了经纬度之外,其它的信息用的是很久之前收下来的数据,不过这也没啥影响。
下面再用灵图导航软件来看看:
同样可以成功定位,不过它看起来没有Google地球好看,Google地区上能看到通进我们村的小路,这软件对于我们这种山沟地方肯定是懒得更新了。
好的整个系统构成就是这样,今天是小年夜,过几天便是新春佳节了,在这里祝大家新春愉快,合家欢乐!