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

