基于GPS,GPRS遠程監(jiān)控系統(tǒng)的實現(xiàn)
----一切只為找到你,不管在世界的哪個角落
自從寒假回家,目前為止橋哥已經(jīng)在家宅了半個月,終于可以出篇技術(shù)貼了。為了迎接年后從事的物聯(lián)網(wǎng)相關(guān)方面的工作,所以打算學學GPRS進行一下數(shù)據(jù)傳輸,不然以后怎么好意思講是做物聯(lián)網(wǎng)的呢。用GPRS傳GPS數(shù)據(jù)是一件兩全其美的事情,因為又可以順帶學習一下GPS相關(guān)知識。對于帖子的名字,通常我們都會取得比較正式又顯得略微碉堡的樣子通常以“基于****的實現(xiàn)”為題,搞起來像篇論文一樣,但是實際上只是一篇隨性的學習筆記而已,網(wǎng)絡(luò)上很多前輩給我們提供了很多資料筆記,我們也應(yīng)該回饋上去一點。
下面簡單畫畫整個系統(tǒng)的框圖:
從框圖中可以清楚的看到整個系統(tǒng)的工作流程是非常簡單的,被監(jiān)測端采用的是Cortex-M3內(nèi)核的stm32微控制器,GPS負責接收衛(wèi)星信號,然后根據(jù)計算得到地標信息通過串口傳送給stm32,stm32接收GPS數(shù)據(jù)進行相應(yīng)的打包處理再通過串口發(fā)送給GPRS模塊,GPRS模塊通過基站傳送到Internet網(wǎng)絡(luò)中向指定IP的端口PC發(fā)送數(shù)據(jù)包,服務(wù)器端上位機通過監(jiān)控該端口來捕獲數(shù)據(jù),然后上位機通過串口再次將GPS數(shù)據(jù)發(fā)送到Google地球或者其它導(dǎo)航軟件中實現(xiàn)對接,以便實時監(jiān)測觀察。在朋友的提示和幫助下,事實上本系統(tǒng)還加入了基站輔助定位功能,由于GPS衛(wèi)星信號的接收必須在室外,所以當被監(jiān)控端處于室內(nèi)或者在接收不到衛(wèi)星信號的惡劣情況下,系統(tǒng)仍然能夠通過基站信息定位到大致的區(qū)域。
GPS使用的是微科電子(香港)公司的VK1513模塊,選擇它主要還是價格方面比較經(jīng)濟實惠,實測效果也不錯。GPS模塊會每秒鐘發(fā)出數(shù)據(jù)包,數(shù)據(jù)包是遵循NMEA-0183協(xié)議的,可以看看我實際接收下來的數(shù)據(jù)包:
主要包含定位信息的是GPGGA以及GPRMC,兩個數(shù)據(jù)包中都包含有時間以及經(jīng)緯度等信息,因為我主要需要的就是當前的經(jīng)緯度信息就可以了,也為了減小GPRS數(shù)據(jù)傳輸負擔,因此我只選擇了將GPRMC這條數(shù)據(jù)進行傳輸,關(guān)于其數(shù)據(jù)包中具體每個數(shù)據(jù)的含義可以參照NMEA-0183的說明。值得注意的是,數(shù)據(jù)包都是以$開頭,以回車換行結(jié)尾的?;剀嚀Q行用轉(zhuǎn)義符號表示便是rn,這點忽視的話,那么服務(wù)器端與地圖軟件的對接將不會成功。
因此我在stm32的程序上做了判斷處理,只傳送GPRMC數(shù)據(jù)包,并且同樣為了再次減小GPRS傳送壓力,每接收到兩個數(shù)據(jù)包傳送一個就可以了。實現(xiàn)代碼如下
接收GPS串口發(fā)來的數(shù)據(jù)通過串口中斷實現(xiàn),接收代碼如下。對于這段程序有點比較有意思的事情,這段代碼是在我單獨調(diào)試GPS的時候?qū)懙?,想法也很簡單,?觸發(fā)開始接收,遇到’n’結(jié)束,然后當時就出現(xiàn)了一個很不合我邏輯的問題,反復(fù)讀了幾十遍代碼沒有發(fā)現(xiàn)問題,如我注釋中寫的為什么放入結(jié)束符的那個地址要減一,這是實際調(diào)試出來的,就沒管它一直這樣用了,前幾天才意識到上面說的是以”rn”結(jié)尾的這個問題,這才恍然大悟,原來還有一個壓根看不見摸不著的’r’。這也受我們平時用電腦的習慣有影響,當敲下鍵盤上的回車鍵,實際上執(zhí)行了兩步操作,回車和換行,這誰看得見呢。
GPRS模塊采用的是華為GTM900C模塊,比較常見的一般也就是SIM900和GTM900,選擇華為的主要還是因為國產(chǎn),數(shù)據(jù)手冊肯定有中文官方原版的,所以學習起來不那么費勁,橋哥可不想在大好假日被英文給磨掉學習的信心。對于其TCP/IP鏈接的建立,可以參照華為GTM900TCP_IP使用指導(dǎo)書。我在程序中已列舉了我所用到的AT指令:
實際上,這個指令定義的順序已經(jīng)表明了我在初始化GTM900C并進行TCP鏈接的初始化過程:
1、關(guān)閉回顯,回顯的意思是你給它發(fā)條指令它會原封不動的給你回發(fā)一份你發(fā)的指令,這里關(guān)掉,以便串口專心監(jiān)測GTM900C實際返回的參數(shù)
2、設(shè)置GPRS網(wǎng)絡(luò)附著,將模塊附著到GPRS網(wǎng)絡(luò)中,下面的操作都是基于GPRS網(wǎng)絡(luò)下進行的,設(shè)置它的目的是保證下面的操作能夠順利進行。如果已經(jīng)附著上GPRS網(wǎng)絡(luò),而再次發(fā)送此命令,會返回ERROR 8,不過沒有關(guān)系不影響附著狀態(tài),以防萬一每次建立鏈接我都發(fā)。
3、配置APN,即GPRS接入點的配置
4、進入TCPIP功能,網(wǎng)絡(luò)傳輸基于這個協(xié)議,那是必須。
5、域名解析,這點很重要值得特別說明。實際上GTM900C建立一條TCP鏈接最后用的都必須是這條指令:AT%IPOPEN="TCP","111.164.240.229",1332,這個指令里面包含的是我們接收端作為服務(wù)器的IP地址和端口。我們知道網(wǎng)絡(luò)上標識唯一一臺電腦的地址用的是IP地址,這個由于不好記誕生了域名,例如www.#,這個域名實際上也是指向了一個IP,為了讓這個域名指向IP就需要一個域名服務(wù)器來解析。而家里撥號上網(wǎng),每次撥號都會動態(tài)的分配到一個IP,況且橋哥家最近是一天掉線20多次,IP隨時都會變化,不可能每次變化我都更改stm32的程序然后重新燒錄,因此我用的一個固定的域名xiaoiqiao.oicp.net來跟我的IP進行實時綁定,而GTM900C模塊每次建立鏈接都要先得到這個固定域名指向的IP,然后根據(jù)IP進行鏈接,確保無論我當前的IP是多少,它都可以正確連上我這里。而提供這個域名解析到IP服務(wù)的是花生殼這款軟件,注冊一個賬號便得到一個免費的域名,并且為該域名提供免費的域名解析服務(wù)。
6、正式建立一條TCP鏈接。得到上步所說的返回IP之后,經(jīng)過封裝,可以形成下面這條指令A(yù)T%IPOPEN="TCP","113.14.177.44",1332;后面的1332是綁定的端口名,大于256的都可以只要跟上位機軟件的監(jiān)聽端口對應(yīng)就行了,這里我固定為1332,因為它是我手機尾號非常好記。此時監(jiān)控端的上位機服務(wù)器要開啟監(jiān)聽了,否則,它跟誰鏈接呢。
上位機軟件啟動后會自動獲取到本機的IP地址,只要填好監(jiān)聽端口,然后點擊啟動服務(wù)就可以了。這里由于我的電腦經(jīng)過了路由器,所以IP是一個子網(wǎng)IP,要是沒有經(jīng)過路由器,這里的IP應(yīng)該是我們上面看到的那個113.14.177.44的外網(wǎng)IP。過路由器就會產(chǎn)生一些麻煩事情,因為路由器接收到GTM900C發(fā)的數(shù)據(jù)的時候,它怎么知道是給自己旗下的哪臺電腦呢,并且其默認安全設(shè)置是不允許外界對它管轄的電腦發(fā)送鏈接請求的,因此,我們還得設(shè)置路由器的轉(zhuǎn)發(fā)規(guī)則,跟它打好招呼,讓其接收到1332端口數(shù)據(jù)的時候,自動轉(zhuǎn)發(fā)到旗下的192.168.1.100這臺電腦里。
因此在校園網(wǎng)下,很難做這個實驗,校園網(wǎng)是一個大局域網(wǎng),主路由器我們無法配置,永遠收不到被監(jiān)測端發(fā)來的數(shù)據(jù)。
7、接下來便進行判斷是否已經(jīng)正確建立了鏈接,如果正確鏈接了,模塊會返回CONNET參數(shù),如果鏈接不成功, 那么將會返回ERROR: N,表示錯誤的類型。為了不那么麻煩,發(fā)現(xiàn)鏈接不成功則從頭開始繼續(xù)以上操作進行 鏈接,唯獨對于ERROR: 9進行了一步額外的處理。假如模塊之前正確鏈接了我們的上位機服務(wù)器,而由于某種原因網(wǎng)絡(luò)斷線,導(dǎo)致我們跟模塊的鏈接已經(jīng)斷開,而模塊卻沒有收到移動服務(wù)臺發(fā)出的斷開鏈接信息,此 種情況之后我們讓GTM900C鏈接服務(wù)器,它會認為之前的鏈接根本沒有斷開,因此返回ERROR: 9表示鏈接已經(jīng)存在,而事實是已經(jīng)斷開了,即使返回第一步重新操作一遍也是一樣的結(jié)果,這是非常危險的一個死循環(huán),因此,我做的處理便是用指令”AT%IPCLOSE=5”把網(wǎng)絡(luò)注銷,注銷網(wǎng)絡(luò)必然斷開了所有鏈接,然后再從第1步初始化。當然每次鏈接失敗到返回重新初始化我有10s鐘的延時時間。
8、成功鏈接之后,便是設(shè)置IO模式,我用的是明文ASCII模式,即不需要什么轉(zhuǎn)換,stm32給它寫什么字符串GTM900C就會發(fā)出什么字符串,注意stm32給GTM900C的發(fā)送數(shù)據(jù)一定要封裝成以下格式:AT%IPSEND="發(fā)送字符串數(shù)據(jù)”;并且根據(jù)官方文檔推薦的,每次數(shù)據(jù)傳送不要超過512字節(jié)。
9、初始化配置完成之后,第一步便是獲取當前所用基站的信息,然后傳送給我們的監(jiān)測服務(wù)器,以便我們用來做基站定位使用。
10、接下來便是一直在等待GPS的數(shù)據(jù)接收完成,每接收完兩次GPRMC數(shù)據(jù),便向服務(wù)器發(fā)送一次用來做GPS定位監(jiān)測。并且我還開啟了一個定時服務(wù),系統(tǒng)運行每三分鐘會檢測一下是否與服務(wù)器還處在鏈接狀態(tài),避免不可抗力因素造成斷線而GTM900C卻被蒙在鼓里。如果鏈接斷開,將會向服務(wù)器重新發(fā)起鏈接命令。如果鏈接正常,那么將會再次獲取一下基站信息,發(fā)送給服務(wù)器。所以服務(wù)器監(jiān)測端會每三分鐘更新基站信息,基站定位本來就只是個大范圍,沒有必要實時更新。
實際上,我在stm32上移植了uCGUI,進行相關(guān)信息的顯示,用以觀察當前的模塊狀態(tài)與命令返回參數(shù),以便進行調(diào)試,例如此時返回的便是域名解析的IP地址信息。前段時間也移植成功了uC-OS-II但是我并不敢用于這里,因為對于這個實時操作系統(tǒng)我還非常不了解,以免給我調(diào)試增加不必要的難度。
接下來,我們來看看服務(wù)器監(jiān)測端,服務(wù)端就是我們用于監(jiān)控的那端,說它是服務(wù)器是相對于GTM900C來講的,因為GTM900C是以一個客戶端的身份跟我們進行鏈接。實際上它就是一個可以接受GTM900C的鏈接和數(shù)據(jù)的軟件而已。
此軟件是在XP系統(tǒng)VC6.0的環(huán)境下用VC++編寫的。使用的參考資料是明日科技出版的系列圖書,明日科技出版的系列編程圖書是不錯的資料,實例豐富,對于我這不專業(yè)的人來講,很多需要的代碼都可以直接從上面拿來主義,做些簡單的工控界面效果還是不錯的。四天前我做的界面其實不包括右邊部分的基站定位功能,用軟件截圖發(fā)了一條說說之后,一位朋友跟我聊天提到這個基站定位,在前天全部調(diào)試測試完成之后,我決定再用一天時間加上這么一個基站定位功能,實現(xiàn)這個功能涉及到的是一些數(shù)據(jù)庫相關(guān)的操作。
實現(xiàn)GPRS數(shù)據(jù)的接收其實還是簡單的,收到的數(shù)據(jù)里面也已經(jīng)有了經(jīng)緯度信息,這些經(jīng)緯度信息如何才能顯示在地圖上,當然是使用現(xiàn)成的地圖軟件了,但是數(shù)據(jù)怎么發(fā)給它們,我們沒有它們的源代碼,就像一個黑匣子,我們只有接口,并不知道里面是怎么實現(xiàn)的。那我就直接利用這個接口給它發(fā)數(shù)據(jù)便是最簡單的方法了。并且我使用的是硬件接口從而完全繞開軟件接口的開發(fā)難度。因此我的方案便是用兩個串口直接對接,GPRS接收服務(wù)器將接收到的GPS數(shù)據(jù)通過其中一個串口發(fā)送出去,然后另一個串口便會收到數(shù)據(jù),這個串口的數(shù)據(jù)就會自動被地圖軟件讀入,這樣就簡單的實現(xiàn)了與地圖軟件的對接。這方法親測對Google地球是可行的,對一般的只要是可以接收串口數(shù)據(jù)的導(dǎo)航軟件也必都是可以的,它沒有理由不行!
基站定位的實現(xiàn)原理也是比較簡單的,前面說了stm32會每三分鐘獲取一次基站信息然后傳回服務(wù)器監(jiān)測端,那么我們就可以通過傳回的信息檢出LAC碼和CELL碼,這兩個碼唯一對應(yīng)一個基站,就跟我們的身份證一樣,那么我們只要通過這兩個碼來搜索數(shù)據(jù)庫找出這個基站所在的經(jīng)緯度便可以了,我手上這份數(shù)據(jù)庫是比較老的,只有3萬多條記錄,不過我只是拿來做個試驗,提供一種實現(xiàn)方法而已也就夠了,沒有的記錄我可以通過網(wǎng)站的在線查詢補充進去就可以了。
從數(shù)據(jù)庫中得到經(jīng)緯度之后,為了能夠在地圖上顯示,我需要做的便是將經(jīng)緯度數(shù)據(jù)封裝成一個符合NMEA協(xié)議的GPRMC數(shù)據(jù)包。前面說了GPRMC中包括經(jīng)緯度信息但是也包含其它信息,但是那些信息對于我們定位不重要,因此其它的信息我隨便整了數(shù)據(jù)包里面的字符代替之就可以了。值得注意的是,數(shù)據(jù)庫里提取出的經(jīng)緯度是以dd.dddddd的形式的,而GPRMC中的經(jīng)緯度數(shù)據(jù)是以ddmm.mmmm格式的,還需要簡單的單位換算處理下,有換算和處理必然帶來不可避免的誤差,還有需要注意的是NMEA協(xié)議規(guī)定的數(shù)據(jù)包的最后兩位字符是校驗值,也就是$和*之間所有字符的異或結(jié)果,如果校驗結(jié)果算得不對,地圖軟件也是無法識別的,再者就是別忘記了末尾加上”rn”。在VC中實現(xiàn)代碼如下
接下來,我們便來實測一下系統(tǒng)運行效果吧
首先,附上GRPS數(shù)據(jù)發(fā)送端的整體運行實物圖:
GPS天線放在窗戶外面就可以收到衛(wèi)星信號了。然后打開監(jiān)測端的上位機軟件,開啟GPRS服務(wù)模式,等待客戶端鏈接,鏈接成功之后,首先發(fā)送過來的是基站信息,然后開始源源不斷的發(fā)送GPS數(shù)據(jù)
可以發(fā)現(xiàn)基站信息已經(jīng)顯示,但是基站的經(jīng)緯度還沒有顯示出來,原因是在GPS定位模式下,我默認是不進行對基站數(shù)據(jù)庫搜索的。
然后打開串口,開始與Google地球進行對接定位:

與Google地球?qū)拥臅r候,波特率一定是要選擇4800的,Google地球端也需要進行如下配置:在Google地區(qū)的工具/GPS菜單下,選擇實時模式,然后選擇NMEA協(xié)議,勾選自動遵循路徑復(fù)選框,點擊開始,Google地球便自動搜索符合條件的串口數(shù)據(jù)進行定位。
幾秒之后,Google地球自動華麗的飛越到GPS數(shù)據(jù)定位的地方
黃色小點的那個地方就是我家了,精度還是不錯的。我家就是在那座小山前面。
下面,來試試基站定位,在右下方定位數(shù)據(jù)輸出方式那里點擊基站定位按鈕,對比前面的圖可以發(fā)現(xiàn)經(jīng)緯度已經(jīng)顯示,這個經(jīng)緯度是從數(shù)據(jù)庫中搜索得到的,然后此時軟件給Google地球發(fā)送的數(shù)據(jù)便不是GPS傳的實時GPRMC數(shù)據(jù)了,而是軟件根據(jù)基站的經(jīng)緯度計算出來的偽GPRMC數(shù)據(jù),因為在計算的時候也是完全遵循NMEA協(xié)議的,所以Google地球同樣能夠接收。
此時在Google地球上看看此基站在什么地方可以發(fā)現(xiàn),那個基站在離我家不遠的右上方,實際上它的真實位置在靠左邊一點呵呵,前面說了計算誤差,不過沒有關(guān)系,基站定位本來就是一個大概。你再細心一點可以對比前面的畫面發(fā)現(xiàn),它顯示的時間是1月13號1:14分與今天的時間相差很遠,那是因為我在計算偽GPRMC數(shù)據(jù)包的時候除了經(jīng)緯度之外,其它的信息用的是很久之前收下來的數(shù)據(jù),不過這也沒啥影響。
下面再用靈圖導(dǎo)航軟件來看看:
同樣可以成功定位,不過它看起來沒有Google地球好看,Google地區(qū)上能看到通進我們村的小路,這軟件對于我們這種山溝地方肯定是懶得更新了。
好的整個系統(tǒng)構(gòu)成就是這樣,今天是小年夜,過幾天便是新春佳節(jié)了,在這里祝大家新春愉快,合家歡樂!
愛華網(wǎng)

