正好手上有個gsm的modem.因為最近正迷php,所以不打算用其它的二次開發包之類的東西了.說幹就幹,晚上一回到家就開始找資料.這一查不要緊,你還別說,還真有用php幹這事兒的.但是網上傳來傳去的都是那一篇<<通過串列埠+GSM MODEM傳送簡訊示例>>.這個示例是windows平臺下的,還用到了一個dll檔案.我現在家裡用的系統也換成ubuntu了,再說以後報警的伺服器也是linux,總不能為了發個簡訊再專門換成windows系統吧.於是決定自己動手,用php的函式寫linux下的程式,由此埋下了禍根.
功夫不負有心人啊,很快在國外的一個網站找到了一個php操作串列埠的類:php_serial.class.於是開始動手用minicom除錯modem.因為在以前單位的時候,幫別人用超級終端除錯過modem,所以命令還記得一些,很快,用at指令通過minicom傳送簡訊成功(還好俺有兩個手機,要不做個實驗都沒法做).ok,改用php操作modem,用at指令發英文簡訊,成功!good.沒想到這麼順利.取得了點小勝利的我不滿足只傳送英文簡訊,於是開始嘗試傳送中文簡訊.但是直接通過at指令輸入中文,modem不買帳.收到的是空白簡訊.這可如何是好?於是開始細細研究網上那篇<<通過串列埠+GSM MODEM傳送簡訊示例>>,於是知道了,想傳送中文簡訊就得用pdu格式,於是又開始研究pdu格式怎麼回事兒(你說這就是多幹的話,能發英文就行了唄,還求那麼完美幹啥).一直到睡覺的點,也沒從我的modem裡發出中文來.不行,先睡覺.
一覺醒來,又細細看了一遍pdu的格式,並一遍一遍的細細學習<<通過串列埠+GSM MODEM傳送簡訊示例>>的生成方法.經過一遍一遍的嘗試終於可以收到一些亂碼的中文了,又經過嘗試終於收到了和傳送字數相同的小方塊的簡訊了.沒辦法到上班的點了,於是趕緊到單位.間隙的時候,問以前單位負責開始簡訊群發軟體的同事小楊怎麼用pdu格式傳送簡訊.小楊告訴了我,要用到的at指令,以及pdu格式的生成方法,雖然沒有幫我解決問題但是至少肯定了:1,我的操作方法是對的,因為可以成功收到簡訊;2,收到字數相等的方塊,應該是編碼的時候沒處理好.晚上回到家又一遍一遍的看之前那篇文章,很不理解,為啥用同樣的函式生成的字串,最後的差距咋就那麼大呢?邊嘗試邊找資料,不經意間在網上我發現一篇文章說傳送簡訊”晚上好123″的編碼是:”665A4E0A597D003100320033″.我把這個串直接放到我程式裡,一傳送,嘿,你猜怎麼著?還真收到了”晚上好123″.有點意思!於是,我也用我的函式生成”晚上好123″,結果我發現了一個規律!
對的:665A4E0A597D003100320033
我的:5A660A4E7D59310032003300
每四個字元裡兩兩錯位!!!發現這個規律以後,就簡單了,因為這樣就確定確實是編碼那塊的原因了.但是編碼那塊:iconv,ord,substr,sprintf好幾個系統函式,一個一個熟悉,改來改去都不行,這時候已經晚上十一點半了(我習慣是十點半睡,七八點起,標準的小學生作息時間).但是離終點這麼近了,真不甘心放棄!初中時候養成的好習慣,今日事,今日畢,因為明天還有明天的事!洗把臉,重新整理一下思路:雖然大體看明白了pdu格式,但是字串轉換這塊,我還真理解不透.怎麼辦呢?看來從根上解決這個問題,我功力是不夠了.往前不行,我們可以迂迴嘛.那我就自己寫一個函式把這個字串的順序變回來!你還真別說,這招好使!而且再發其它的中文簡訊,也可以正常收到啦!!!呵呵,看來今晚註定好夢了.
雖然湊合能用了,但是還不知道是什麼原因啊.自己搞不定不要緊,公司裡我周圍坐著的可全是php高手.午休的時候,把會軍叫過來,說明事情的來來回回,會軍看了一遍程式碼以後,斷定是字元的高低位元組問題!因為linux系統和windows系統,對unicode處理的時候,正好是高低位相反的!說實話,當時真驚呆了,我咋就沒考慮到是作業系統問題呢!知道了問題的原因以後,我和會軍開始分別想從源頭解決這個問題.但是半個小時過去了,發現要實現這個太難了,於是我提議放棄,就用我寫的這個函式湊合著用.但是還是會軍老道.雖然放棄了這步,但是最終還是通過修改轉碼那塊的順序搞定了這個問題!!!
通過這次的編碼訓練又使我學習了很多東西,關於php,關於解決問題的思路.因為用了開源的php類,也參考了網上其它的同類文章,本著開源精神,現將全部程式碼整理如下:
/*
* author: rainbird <chinakapalink@gmail.com>
* date:2010-02-26
* system:ubuntu9.10
* php cli:5.2.10
*gsm modem:WAVECOM MODEM
*/
include “php_serial.class.php”;
//載入php操作串列埠的類
$serial = new phpSerial;
//連線USB gas modem
$serial->deviceSet(“/dev/ttyUSB0”);
$serial->deviceOpen();
//要傳送的手機號:1531170xxxx
$phone_sendto = InvertNumbers(`861531170xxxx`);
//要傳送的簡訊:I am rainbird,i love 中國
$message = hex2str(`I am rainbird,i love 中國`);
$mess = “11000D91″.$phone_sendto.”000800”.sprintf(“%02X”,strlen($message)/2).$message;
$serial->sendMessage(“at+cmgf=0”.chr(13));
$serial->sendMessage(“at+cmgs=”.sprintf(“%d”,strlen($mess)/2).chr(13));
//不加簡訊中心號碼
$serial->sendMessage(`00`.$mess.chr(26));
//加簡訊中心號碼
//$phone_center = InvertNumbers(`8613800100500`);
//$mess_ll = “0891”.$phone_center.$mess;
//$serial->sendMessage($mess_ll.chr(26));
//用完了就關掉,有始有終好習慣
$serial->deviceClose();
//將utf8的簡訊轉成ucs2格式
function hex2str($str) {
$hexstring=iconv(“UTF-8”, “UCS-2”, $str);
$str = “;
for($i=0; $i<strlen($hexstring)/2; $i++){
$str .= sprintf(“%02X”,ord(substr($hexstring,$i*2+1,1)));
$str .= sprintf(“%02X”,ord(substr($hexstring,$i*2,1)));
}
return $str;
}
//手機號翻轉,pdu格式要求
function InvertNumbers($msisdn) {
$len = strlen($msisdn);
if ( 0 != fmod($len, 2) ) {
$msisdn .= “F”;
$len = $len + 1;
}
for ($i=0; $i<$len; $i+=2) {
$t = $msisdn[$i];
$msisdn[$i] = $msisdn[$i+1];
$msisdn[$i+1] = $t;
}
return $msisdn;
}
?>
php_serial.class.php檔案見附件.at指令集及pdu的相關知識請自行google.其實如果只需要傳送英文簡訊的話,只需要用php_serial.class連線modem並用at指令發簡訊就可以了.傳送中文的時候才會用到pdu格式,當然用了pdu格式以後可以發混合的中英文.本文只作測試沒有考慮簡訊最多隻能傳送74個漢字的情況,如需要請自行處理.
後記:經過這次成功的用php操作gsm modem,使我對php更加沉迷.毫不誇張的說:是php為我插上了隱形的翅膀,感謝讓我的想法可以飛翔!