就"通過串口收發(fā)短消息"的問題,本人將同網(wǎng)友交流、探討的部分技術(shù)問題整理成如下文字。希望這篇文章能對更多對SMS感興趣的朋友有所幫助。由于本人是業(yè)余愛好,時間和金錢都有限,沒有力量將很多型號的手機(jī)和模塊一一試驗(yàn),可能存在這樣那樣的差錯,希望行內(nèi)高人批評指正。
Q 我寫了個短信發(fā)送程序,使用PDU格式發(fā)送,程序在廣州使用一點(diǎn)問題也沒有,在河南卻怎么也發(fā)不出去。不知道為什么,短信"你好嗎"格式如下:
河南: 0891683108200005F011000D91683170031618F20008A9064F60597D5417
廣州: 0891683108301705F011000D91683170031618F20008A9064F60597D5417
A 發(fā)送短信時要用SIM卡屬地的SMSC號碼。如果是在廣州辦的卡,即使在外地還是要用廣州的SMSC號碼。你的兩個短信內(nèi)SMSC號碼不同,但用的是同一張SIM卡,不知是否是此原因。
Q 短信中心的號碼可否直接使用SIM卡中的號碼,而不要用戶輸入?我用過的短信軟件好像都是不用輸SMSC號碼的。
A 有一條"AT+CSCA"指令,可用于設(shè)置或查詢服務(wù)中心號碼。若手機(jī)中已存在此號碼,有兩種解決辦法:
用"AT+CSCA?"指令查詢出來,然后自動將此號碼寫到PDU的SCA中。
PDU的SCA字段只寫一個"00": "08 91 68 31 ..." -> "00"
可用"AT+CSCA=xxxxxxxx"指令設(shè)置服務(wù)中心號碼。
Q 我在超級終端上,用at+cmgs發(fā)送短消息,格式好像沒有錯誤,但總返回"ERROR"。我輸入的就象這樣:
at+cmgs=30
> 0891683108100005F011000D91683118405057F000000006C8329BFD0E01
請問是什么原因?
A "at+cmgs"指令很特殊,回車后還需要輸入數(shù)據(jù)。此處是"CR",不是"CRLF",注意在超級終端里直接回車是不是生成了兩個字符(查看設(shè)置)。象"at+cmgl"指令,即使最后輸入"CRLF"也是不要緊的。
你的問題出在長度上。長度不是隨便寫的,你的例子中,長度應(yīng)為21。除去SMSC段(0891683108100005F0),從"11"開始算(即"11000D91683118405057F000000006C8329BFD0E01"),除以2即得。
正確的寫法應(yīng)該是
at+cmgs=21
> 0891683108100005F011000D91683118405057F000000006C8329BFD0E01
(">"是手機(jī)提示,不是輸入的)
Q 我最近在編一個關(guān)于短消息的程序,在你的"通過串口收發(fā)短消息"中提到"Text Mode是純文本方式,可使用不同的字符集,從技術(shù)上說也可用于發(fā)送中文短消息,但國內(nèi)手機(jī)基本上不支持,主要用于歐美地區(qū)。"是不是說我用AT指令"AT+CMGF=1"或"AT+CMGF=0"對我后來的收發(fā)短消息沒什么影響啊?
A Text mode寫起來簡單,直接發(fā)原文就行,發(fā)送非ASCII碼內(nèi)容也能發(fā),但需要手機(jī)支持才能正確顯示。如法語、德語的很多字符,編碼大于0x80,他們都是用text mode。Text mode靠什么區(qū)分字符編碼方式呢?有專門的字符集設(shè)定指令"AT+CSCS"。可以設(shè)定為擴(kuò)充字符集"UCS2"。Siemens TC35/TC37資料上說,它的"AT+CSCS"支持"UCS2"字符集,但我目前沒有機(jī)會去親自試驗(yàn)。正在使用TC35/TC37模塊的朋友不妨試一下。
據(jù)我了解,中文短消息方面,在國內(nèi)賣的各種手機(jī)只支持PDU mode,這成了事實(shí)上的標(biāo)準(zhǔn)。其實(shí)PDU mode真的挺好用,估計以后text mode會萎縮。我們寫的程序,我建議只采用PDU mode,即使是發(fā)純英文信息也這樣,編碼倒是可以靈活采取7bit或UCS2,因?yàn)?bit能發(fā)的長度是UCS2的2倍(僅對純英文而言)。如果發(fā)送純數(shù)據(jù),不需要手機(jī)顯示,可用8bit。
Q 你的smstraffic類中的發(fā)送接收大循環(huán)中,是不是把所有收到的消息都放入消息隊(duì)列后,然后執(zhí)行刪除程序。咳绻沂遣l(fā)量很大的話,就是網(wǎng)關(guān)有很多短消息等著進(jìn)入手機(jī),讀完所有短消息后,進(jìn)行刪除的過程中,因?yàn)槎滔⒌呐帕许樞,而?dǎo)致誤刪除呢(比如說我現(xiàn)在手機(jī)里有1-15條短消息,然后在我刪除第一二條后,第三條自動填補(bǔ)為第一條,而新進(jìn)來的短消息,16條排在了第三條,而被cancel掉呢?)我試過好象短消息的排列不是每次都一樣?(在接收的時候,同一條短消息有時是14條,有時是第15條)這個怎么解決。
A 手機(jī)里消息都有一個物理序號,讀出的時候帶序號,刪除也要根據(jù)序號刪。"物理"二字很關(guān)鍵。這個序號相當(dāng)于ID,無論它前面有沒有刪除、刪除了多少消息,都不會變的。假如原來有1-15,刪除了1和2,又來了一條消息,手機(jī)內(nèi)部的軟件有兩種處理方式:有的放在第1條,有的則放在第16條,我都見過。其實(shí),它愿意放到哪個空閑的地方都行。但無論怎樣,不會引起混亂的,因?yàn)樽x出是什么序號,就刪除什么序號的。在執(zhí)行刪除命令前,消息還是在原來那個地方,不會被后來的覆蓋。
如果說網(wǎng)關(guān)有很多短消息等著進(jìn)入手機(jī),量很大,這種處理方式效率不高,因?yàn)锳T+CMGL占用很長時間,這段時間手機(jī)無法從SMSC接收新消息。采用我說的"實(shí)時"接收方法比較好,消息來了直接傳出來,不經(jīng)過寫入手機(jī)的過程。
Q 我用Nokia 8210串口數(shù)據(jù)線,連上電腦的com1口,用SmsTest運(yùn)行提示"沒有發(fā)現(xiàn)MODEM",跟蹤發(fā)現(xiàn)gsmInit()檢測中串口發(fā)AT指令沒有回應(yīng)"OK"。按您的提示我安裝了Nokia modem驅(qū)動程序,(WIN2000 server系統(tǒng))虛擬出com3口的一個8210 MODEM設(shè)備,再次調(diào)用smsTest還是提示"沒有發(fā)現(xiàn)MODEM"。但用串口線,手機(jī)能通過LogoManager手機(jī)管理軟件進(jìn)行相應(yīng)的圖片LOGO,短信發(fā)送操作。
A Nokia手機(jī)本身沒有帶modem功能,用專業(yè)術(shù)語講就是不具備TA(Terminal Adapter)接口,需要驅(qū)動轉(zhuǎn)換,不管是真的串口,USB還是紅外接口,反正它能虛擬出"標(biāo)準(zhǔn)MODEM"串口來。AT命令只能用標(biāo)準(zhǔn)異步通信。
在我的印象中,Nokia 8210需用紅外線接口同PC通信。估計你裝的那個驅(qū)動是IR->COM轉(zhuǎn)換的,而不是驅(qū)動串口數(shù)據(jù)線的,可能你的電腦沒有紅外接口,所以com3也連不上?
要試(虛擬)串口是否連接正確,很簡單,用windows自帶的"超級終端"在特定虛擬端口連上,敲個"AT"回車,看有沒有反應(yīng),正確回答應(yīng)該是"OK"。
Nokia數(shù)據(jù)線上跑的是"Nokia語"- Nokia專有協(xié)議的數(shù)據(jù),不是通用/擴(kuò)展的AT命令集。LogoManager能聽、能說"Nokia語",所以不需要安裝驅(qū)動就能工作。Nokia有一個免費(fèi)的"Nokia PC Connectivity SDK",可供開發(fā)Nokia手機(jī)使用。至于LogoManager是不是用的這個開發(fā)包,那就不得而知了。
Q 在SmsTest中,發(fā)出AT命令,然后接收應(yīng)答,比如
WriteComm("AT+CMGF=0\r", 10);
ReadComm(ans, 128);
在WriteComm函數(shù)后接著就調(diào)用ReadComm,是不是太急,這里的ReadComm函數(shù)是讀返回的這個字符串還是其中的單個字符或不完全的字符串?請問超時控制設(shè)多少最合適。
A 關(guān)于讀串口,程序中是這樣設(shè)定超時控制的:
COMMTIMEOUTS timeouts = { // 串口超時控制參數(shù)
100, // 讀字符間隔超時時間: 100 ms
1, // 讀操作時每字符的時間: 1 ms (n個字符總共為n ms)
500, // 基本的(額外的)讀超時時間: 500 ms
1, // 寫操作時每字符的時間: 1 ms (n個字符總共為n ms)
100}; // 基本的(額外的)寫超時時間: 100 ms
ReadComm什么時候返回呢?按此timeout設(shè)定,若n=128(ReadComm的第二個參數(shù)),則
若無任何數(shù)據(jù),等待500+1*128=628毫秒返回。也就是說,若沒有連上手機(jī),根本不存在應(yīng)答,ReadComm會持續(xù)阻塞628毫秒,而后返回。
若數(shù)據(jù)連續(xù)傳輸,且字符間隔也未超過了100毫秒,但時間已經(jīng)到了628毫秒,返回已讀取的字符(串)。接收到的可能是不完全的字符串。
若在628毫秒內(nèi),字符間隔超過了100毫秒(第一個字符之前等待的時間不算),返回已讀取的字符(串)。接收到的應(yīng)該是完整的字符串。
在手機(jī)正確連接的情況下,主要是最后一條起作用。一段數(shù)據(jù)是連續(xù)傳輸?shù)模舨ㄌ芈适?600bps,可以算出字符間隔是0.1毫秒左右,遠(yuǎn)小于100毫秒,不會讀一個字節(jié)或部分?jǐn)?shù)據(jù)就返回;通常是數(shù)據(jù)完畢后才可能出現(xiàn)等待100毫秒而返回的情況。舉個例子,若在執(zhí)行ReadComm(ans, 128)后150毫秒收到"OK\r\n",則還需要額外等100毫秒,也就是說函數(shù)將在250毫秒后返回。這里傳輸4個字節(jié)數(shù)據(jù)的時間被忽略不計了。如果覺得讀得太急,可將基本的(額外的)讀超時時間調(diào)大一些。不過500毫秒內(nèi)還沒有應(yīng)答,可能是連接故障造成的。
需要特別注意的是"AT+CMGL"指令及其應(yīng)答?赡苁怯捎谛枰獟呙杷写鎯^(qū)域的緣故,手機(jī)在逐條送出短消息后,還需要延遲好幾秒的時間才能送出最后的"OK"。當(dāng)然可以通過設(shè)定上面的基本讀超時時間很長(比如20秒),并且一次讀很長的數(shù)據(jù)(比如2000),來達(dá)到目的。但這樣一來,函數(shù)阻塞時間太長,若恰好這時要程序退出,你會赫然發(fā)現(xiàn)"該程序無響應(yīng)"。SmsTest中解決辦法是:循環(huán)讀取串口數(shù)據(jù),將每次讀取的數(shù)據(jù)拼接起來,最后得到完整的應(yīng)答。gsmGetResponse()每次可能讀取部分?jǐn)?shù)據(jù),將新數(shù)據(jù)追加到已讀數(shù)據(jù)后,且檢測是否見到"OK"或"ERROR",以判斷是否已經(jīng)讀到完整的數(shù)據(jù)。