C#開發(fā)終端式短信的原理和方法

2010-08-28 10:49:27來源:西部e網(wǎng)作者:

AT指令

  說到AT指令可多了,有厚厚的一本書,不屬于我們今天討論的范圍,在這里我僅討論在發(fā)送短信中必須要用的幾個(gè)AT指令。

  與SMS有關(guān)的GSM AT指令(from GSM07.05)如表1所示:

AT 指令 功 能
AT+CMGC Send an SMS command(發(fā)出一條短消息命令)
AT+CMGD Delete SMS message(刪除SIM卡內(nèi)存的短消息)
AT+CMGF Select SMS message formate(選擇短消息信息格式:0-PDU;1-文本)
AT+CMGL List SMS message from preferred store(列出SIM卡中的短消息PDU/text: 0/"REC UNREAD"-未讀,1/"REC READ"-已讀,2/"STO UNSENT"-待發(fā),3/"STO SENT"-已發(fā),4/"ALL"-全部的)
AT+CMGR Read SMS message(讀短消息)
AT+CMGS Send SMS message(發(fā)送短消息)
AT+CMGW Write SMS message to memory(向SIM內(nèi)存中寫入待發(fā)的短消息)
AT+CMSS Send SMS message from storage(從SIN|M內(nèi)存中發(fā)送短消息)
AT+CNMI New SMS message indications(顯示新收到的短消息)
AT+CPMS Preferred SMS message storage(選擇短消息內(nèi)存)
AT+CSCA SMS service center address(短消息中心地址)
AT+CSCB Select cell broadcast messages(選擇蜂窩廣播消息)
AT+CSMP Set SMS text mode parameters(設(shè)置短消息文本模式參數(shù))
AT+CSMS Select Message Service(選擇短消息服務(wù))

表一:相關(guān)的GSM AT指令

  我現(xiàn)在以實(shí)例來說明這些指令的使用方法:

  先用手機(jī)數(shù)據(jù)線將手機(jī)連接到電腦串口,并將串口的波特率設(shè)置為19200,可以開始了。

  1、首先測試你的連接及手機(jī)是否支持AT指令,請?jiān)谀愕拇谡{(diào)試程序中輸入:

  AT<回車>

  屏幕上返回"OK"表明計(jì)算機(jī)與手機(jī)連接正常,那樣我們就可以進(jìn)行其它的AT指令測試了

  2、設(shè)置短信發(fā)送格式

  AT+CMGF=1<回車>

  屏幕上返回"OK"表明現(xiàn)在短信的發(fā)送方式為PDU方式,如果是設(shè)置為TEXT方式,則,AT+CMGF=0<回車>

  3、 發(fā)送短信

  發(fā)送內(nèi)容及手要號仍舊同上面在編碼中的一樣,編碼后,得到要發(fā)送的數(shù)據(jù)如下

0891683108705505F011000D91683117352446F2000800124F60597D002C00480065006C006C006F0021

  我們用如下指令來發(fā)送

  AT+CMGS=33<回車>

  如果返回">",就把上面編碼數(shù)據(jù)輸入,并以CTRL+Z結(jié)尾,稍等一下,你就可以看到返回OK啦。

  說明一下,為什么AT+CMGS=33呢,是這樣得來的:

11000D91683117352446F2000800124F60597D002C00480065006C006C006F0021

  這一段字符串的長度除以2得到的結(jié)果,上面的字符串,短信中心號加上短信內(nèi)容得到的,怎么得到的,請回顧一下解碼部份

  在我們前面的討論中,一條完整的短信發(fā)送,只要執(zhí)行三條AT指令,AT、AT+CMGS=?、AT+CMGS=?就可以了。由于篇幅,我只能在這里提到這么多,大家要是想了解更多,可以向各手機(jī)廠商索取AT指令白皮書,里面很詳細(xì)的。

  上面講到的,只能為我們實(shí)際中作準(zhǔn)備,我們還必須要一個(gè)發(fā)送途徑,根據(jù)我們的需要,我們選擇投資最少,實(shí)現(xiàn)比較方便的串口通信。注意,串口通過數(shù)據(jù)線跟手機(jī)相連,用AT指令來實(shí)現(xiàn)發(fā)送短信,在我們選擇數(shù)據(jù)線時(shí),建議購買原廠所配,非原廠所配,在使用過程中,經(jīng)常出現(xiàn)一些莫明其妙的問題,比如,手機(jī)屏幕黑了,手機(jī)老是提示電池電量不足之類的。

串口通信

  在C#中要實(shí)現(xiàn)串口通信,很多人都不知所措,在論壇上經(jīng)常可以看到"怎么用MSCOMM實(shí)現(xiàn)串口通信"、"怎樣能過串口與設(shè)備相連"諸如此類的問題。其實(shí)國外的網(wǎng)友早就把這些列入FAQ中了。

  通常,在C#中實(shí)現(xiàn)串口通信,我們有四種方法:

  第一:通過MSCOMM控件這是最簡單的,最方便的方法。可功能上很難做到控制自如,同時(shí)這個(gè)控件并不是系統(tǒng)本身所帶,所以還得注冊,不在本文討論范圍。可以訪問http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=320 ,一個(gè)國外網(wǎng)友的寫的教程,作者很熱心,我曾有發(fā)郵件給他,很快就回復(fù)了。

  第二:微軟在.NET新推出了一個(gè)串口控件,基于.NET的P/Invoke調(diào)用方法實(shí)現(xiàn),詳細(xì)的大家可以訪問微軟網(wǎng)站http://msdn.microsoft.com/msdnmag/issues/02/10/NETSerialComm/default.aspx,方便得到更多資料。

  第三:就是用第三方控件啦,可一般都要付費(fèi)的,不太合實(shí)際,不作考慮

  第四:自己用API寫串口通信,這樣難度高點(diǎn),但對于我們來說,可以方便實(shí)現(xiàn)自己想要的各種功能

  在本文,我們采用第四種方法來實(shí)現(xiàn)串口通信,不過不是自己寫,用一個(gè)國外網(wǎng)友現(xiàn)成的已經(jīng)封裝好的類庫,不過功能簡單點(diǎn),相對我們來說已經(jīng)夠用了。

  在整個(gè)終端短信的操作過程中,與串口的通信,只用到了四個(gè)功能,打開、寫、讀、關(guān)閉串口。下面是類庫對這四個(gè)功能的定義:

  打開串口:

  函數(shù)原型:public void Open()

  說明:打開事先設(shè)置好的端口

  示例:

using JustinIO;

static JustinIO.CommPort ss_port = new JustinIO.CommPort();
ss_port.PortNum = COM1; //端口號
ss_port.BaudRate = 19200; //串口通信波特率
ss_port.ByteSize = 8; //數(shù)據(jù)位
ss_port.Parity = 0; //奇偶校驗(yàn)
ss_port.StopBits = 1;//停止位
ss_port.ReadTimeout = 1000; //讀超時(shí)
try
{
 if (ss_port.Opened)
 {
  ss_port.Close();
  ss_port.Open(); //打開串口
 }
 else
 {
  ss_port.Open();//打開串口
 }
 return true;
}
catch(Exception e)
{
 MessageBox.Show("錯誤:" + e.Message);
 return false;
}


  寫串口:

  函數(shù)原型:public void Write(byte[] WriteBytes)

  WriteBytes 就是你的寫入的字節(jié),注意,字符串要轉(zhuǎn)換成字節(jié)數(shù)組才能進(jìn)行通信

  示例:

ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI\r")); //獲取手機(jī)品牌

  讀串口:

  函數(shù)原型:public byte[] Read(int NumBytes)

  NumBytes 讀入緩存數(shù),注意讀取來的是字節(jié)數(shù)組,要實(shí)際應(yīng)用中要進(jìn)行字符轉(zhuǎn)換

  示例:

string response = Encoding.ASCII.GetString(ss_port.Read(128)); //讀取128個(gè)字節(jié)緩存

  關(guān)閉串口:

  函數(shù)原型:ss_port.Close()

  示例:

ss_port.Close();

  由于篇幅,以及串口通信涉及內(nèi)容廣泛,我在這里只講這些。

  在上面我們已經(jīng)把終端短信所需的各種原始技術(shù)有所了解,是可以小試牛刀的時(shí)候了

實(shí)踐篇

  在整個(gè)開始的時(shí)候,你要準(zhǔn)備以下軟硬件:

   硬件:西門子3508或C35系列手機(jī)一個(gè)
   西門子手機(jī)通信數(shù)據(jù)線一條
   軟件:VS.NET(C#)
   短信編碼類庫(PDUdecoding.cs)
   串口通信類庫(JustinIO.cs)

  當(dāng)所要求的軟硬件都準(zhǔn)備好后,我們就可以正式開始了。下面以我自己的測試用例為大家詳細(xì)介紹。

   做什么事情都應(yīng)該有計(jì)劃,雖然我們的測試用例很簡單,但還是畫個(gè)簡單的流程圖:



  有了流程圖,還只是明白了程序怎么運(yùn)行,再看看界面,會讓你更心動的了。


圖二、短信終端C#版界面圖

  再不開始,就有人罵我了。下在我講的開發(fā)環(huán)境是在VS.NET(C#)中。COME GO,GO…

  步驟一、打開VS.NET,新建項(xiàng)目->Visual C#項(xiàng)目->W(wǎng)indows應(yīng)用程序,名稱中輸入你的工程名就行啦,我的是smsForCsharp

  步驟二、參照上面的界面圖,設(shè)計(jì)你的程序界面,下面是我程序中各控件的主要屬性

控件名稱 控件Name屬性 說明
TextBox targetNumber 接收手機(jī)號碼
TextBox CenterNumber 短信中心號
TextBox smsState 發(fā)送短信后,返回的信息。注意設(shè)置控件為多行
TextBox smsContent 短信內(nèi)容,同樣,注意設(shè)置為多行
ComboBox ConnectPort 連接手機(jī)的端口,例:COM1\COM2
ComboBox ConnectBaudRate 串口連接的波特率,在串口通信中很重要的
Button btnSend 發(fā)送按鈕
Button btnConnect 連接按鈕,主要用于程序的初始化
Button btnExit 退出按鈕


  步驟三、將PDUdecoding.cs與JustinIO.cs拷入剛剛新建工程目錄,并打開解決方案資源管理器,右鍵添加現(xiàn)有項(xiàng),選中兩個(gè)文件就行了,這里再打開類視圖,里面是不是多了兩個(gè)類,JustinIO與SMS類啊,如圖三,要是沒有,那你再試。


圖三,添加類后的類視圖

  步驟四、引用命名空間,用代碼查看方式打開Form1.cs(這里以我電腦為準(zhǔn),如果你自己更改過,請以你電腦為準(zhǔn)),在代碼前面加上

using JustinIO;
using SMS;
using System.IO;
using System.Text;


  步驟五、在smsFormCsharp類中,添加兩個(gè)字段ss_port、sms,分別為JustinIO及SMS的對象,如下



  步驟六、添加串口初始化代碼,如下:

/// <summary>
/// 初始化串口
/// </summary>
public bool InitCom(string m_port, int m_baudrate)
{
 ss_port.PortNum = m_port;//串口號
 ss_port.BaudRate = m_baudrate;//波特率
 ss_port.ByteSize = 8;//數(shù)據(jù)位
 ss_port.Parity = 0;//
 ss_port.StopBits = 1;//停止位
 ss_port.ReadTimeout = 1000;//讀超時(shí)
 try
 {
  if (ss_port.Opened)
  {
   ss_port.Close();
   ss_port.Open();
  }
  else
  {
   ss_port.Open();//打開串口
  }
  return true;
 }
 catch(Exception e)
 {
  MessageBox.Show("錯誤:" + e.Message);
  return false;
 }
}


  將上述代碼直接拷入你的程序中,并確保添加在Main主函數(shù)的后面,按F5,調(diào)試應(yīng)該沒什么問題,不過上面還沒有實(shí)際任何看得見的功能,僅僅是打開了串口而以。

  步驟七、打開串口后,我們就應(yīng)該初始化程序,取得手機(jī)的名牌,型號,以及短信中心號,雙擊連接按鈕,并把下面代碼拷入程序中:

/// <summary>
/// 初始化代碼,并獲取手機(jī)相關(guān)信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnConnect_Click(object sender, System.EventArgs e)
{
 bool opened = InitCom(ConnectPort.SelectedItem.ToString(),Convert.ToInt32(ConnectBaudRate.SelectedItem.ToString()));//打開并初始化串口
 bool Connected = false;
 if (opened)
 {
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI\r")); //獲取手機(jī)品牌
  string response = Encoding.ASCII.GetString(ss_port.Read(128));
  if (response.Length > 0)
  {
   ConnectState.Text = response.Substring(10,7);
   Connected = true;
  }
  else
  {
   ConnectState.Text = "與手機(jī)連接不成功";
   Connected = false;
  }
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMM\r"));//獲取手機(jī)型號
  response = Encoding.ASCII.GetString(ss_port.Read(128));
  if(response.Length > 0)
  {
   ConnectState.Text =ConnectState.Text+ " " + response.Substring(10,5) + " 連接中......";
   Connected = true;
  }
  else
  {
   ConnectState.Text = "與手機(jī)連接不成功";
   Connected = false;
  }
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CSCA?\r"));//獲取手機(jī)短信中心號
  response = Encoding.ASCII.GetString(ss_port.Read(128));
  if(response.Length > 0)
  {
   CenterNumber.Text = response.Substring(20,13);
   Connected = true;
  }
  else
  {
   Connected = false;
  }
  if (Connected == true)
  {
   btnConnect.Enabled = false;
   btnSend.Enabled = true;
  }
  else
  {
   btnConnect.Enabled = true;
   btnSend.Enabled = false;
  }
 }
}


  到這里,你可以按F5,編譯調(diào)試,通過,在確保你的手機(jī)與電腦連接正常下,點(diǎn)擊連接按鈕看看,是不是像我的一樣,手機(jī)型號及短信中心號者正常顯示出來了。


圖四、連接后程序界面

  步驟八、看到上在的結(jié)果,是不是感覺到離成功發(fā)送短信很近啦,看這么長的文章,費(fèi)了大家不少時(shí)間,再不亮出發(fā)短信部份,對不起大家了。

  雙擊發(fā)送按鈕,將下面代碼拷入程序中。

/// <summary>
/// 發(fā)送短信
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, System.EventArgs e)
{
 string decodedSMS = sms.smsDecodedsms(CenterNumber.Text,targetNumber.Text,smsContent.Text);
 byte[] buf =Encoding.ASCII.GetBytes(String.Format("AT+CMGS={0}\r",sms.nLength));
 ss_port.Write(buf);
 string response = Encoding.ASCII.GetString(ss_port.Read(128));
 string SendState = "";
 if( response.Length > 0 && response.EndsWith("> "))
 {
  ss_port.Write(Encoding.ASCII.GetBytes(String.Format("{0}\x01a",decodedSMS)));
  SendState = "發(fā)送成功!";
 }
 else
 {
  SendState = "發(fā)送失敗";
 }

 string Result = String.Format("{0},{1},{2},\n\r",targetNumber.Text,smsContent.Text,SendState);
 smsState.Text += Result;
}


  快按F5吧!神啊,快通過吧!不用求神了,已經(jīng)通過了,現(xiàn)在你就可以發(fā)短信了,請確保手機(jī)可以正常連接電腦。按連接,然后填入你要的發(fā)送的目標(biāo)手機(jī)號,并在內(nèi)容中添入你要發(fā)送的內(nèi)容,發(fā)送吧!成功了!成功了是這樣子的!看你的跟我的一樣嗎?


圖五、發(fā)送成功

  還有一些事 不要忘了,記得添加退出代碼。雙擊退出,添加下面代碼:

/// <summary>
/// 關(guān)閉串口,退出程序
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnExit_Click(object sender, System.EventArgs e)
{
 ss_port.Close();
 Application.Exit();
}


  到這里都告一個(gè)段落了,所有的功能都完成了!不過由于這僅僅是一個(gè)演示用例,還有很多沒有考慮,像串口通信中的,在實(shí)際操作不可這樣操作的,應(yīng)該用多線程來處理,一個(gè)專門用來讀串口,一個(gè)專門用來寫串口。還有程序中很多防出錯代碼沒有添加進(jìn)去,希望有心有朋友添加,并公布出來,這也是我寫這篇文章希望看到的結(jié)果。請勿將本程序直接用于實(shí)際中,真誠提醒你!

  終于寫完了,我也放松了許多,本來很早就應(yīng)該完成了,因?yàn)橐恍﹤(gè)人原因,沒有及時(shí)寫完,向那些曾經(jīng)問過我相關(guān)問題,沒有及時(shí)回復(fù)的朋友,抱歉一聲,希望你們繼續(xù)支持我!

  調(diào)試環(huán)境

  Windows 2000 Professional、Visual Studio.NET、西門子3508手機(jī)、西門子專用數(shù)據(jù)線

常見問題

  第一, 手機(jī)品牌,因?yàn)椴煌a(chǎn)商的手機(jī),對AT指令的支持不同,所以請選擇適合你手機(jī)AT指令,像NOKIA的就只能用TEXT模式的AT指令。

  第二, 數(shù)據(jù)線,問題出得最多的地方也就是數(shù)據(jù),如果接上數(shù)據(jù)線后,你的手機(jī)顯示為黑屏,建議你換數(shù)據(jù)線。

  第三, 手機(jī)SIM卡上的短信中心號設(shè)置,請確保在你的手機(jī)上可以發(fā)送短信。

  第四, 請你先用串口調(diào)試工具調(diào)試手機(jī)與電腦的連接,這樣對你整個(gè)工作都是一個(gè)保證。

關(guān)鍵詞:C#短信

贊助商鏈接: