短信開發系列(一):GSM手機短信開發初探


短信開發系列目錄:

短信開發系列(一):GSM手機短信開發初探
短信開發系列(二):GSM手機短信開發之短信解碼
短信開發系列(三):短信接收引擎

這兩天需要實現一個短信的報警平台,便看了一下相關的資料。

其實短信的收發還是比較簡單的。

 

首先,我們需要有個短信模塊,也就是短信貓。

其次,需要熟悉一下AT命令,這個到google了解一下即可。

接着,需要了解串口編程。串口的編程,其實也是比較簡單的一塊。

最后,往串口中寫入相關的命令即可。

過程是簡單的,但是在實踐中還是遇到一些麻煩,主要是調試硬件和調試AT命令,如果沒有一定的經驗,可能會不知所措。本文着重於介紹其功能的實現和協議的解析

 

下面是部分代碼的解析

首先是打開串口

 

1  var serialPort =  new SerialPort( " COM6 "115200, Parity.None,  8, StopBits.One)
2             {
3                 WriteTimeout =  500,
4                 ReadTimeout =  500,
5                 RtsEnable =  true,
6                 DtrEnable =  true
7             };
8 serialPort.Open();

 

其中波特率為115200,RtsEnable設置為true是為了能夠在發送完命令后能夠及時接收到返回值。每個AT命令發送后都應該會接收到相應的返回值。

剛開始的時候,沒有設置這個屬性,結果在讀取接收緩沖區時,沒能讀取到任何信息。

 

然后就是發送一個設置發送格式的命令,確定發送的格式

 

1  ///   <summary>
2           ///  生成設置信息發送格式的發送命令串
3           ///   </summary>
4           ///   <param name="format"> The format. </param>
5           ///   <returns></returns>
6          public  static  string GenSendFormatCmd(SendFormat format = SendFormat.Text)
7         {
8              return  string.Format( " AT+CMGF={0}\r ", ( int)format);
9         }

發送完之后,返回也會存在OK字樣,通過該返回串可以判斷是否發送成功。

 

最后,也是最主要的部分,就是構建發送內容了。

根據平常我們使用的情況,手機的信息發送,包括兩部分,目的地址和信息內容。

實際的發送時,手機信息會被編碼為PDU格式(當然還有其他格式,暫時沒有研究),整個報文發出。

PDU格式的報文可以簡單解析如下:

報文頭+編碼的手機號碼+信息內容編碼方案+編碼的信息內容

具體的格式可以上網查找,下面我用代碼來說明如果對手機號碼和信息內容進行編碼

 

 1  ///   <summary>
 2           ///  生成短信息發送串
 3           ///   </summary>
 4           ///   <param name="phoneNo"> The phone no. </param>
 5           ///   <param name="message"> The message. </param>
 6           ///   <returns></returns>
 7          public  static  string GenSendCmd( string phoneNo,  string message)
 8         {
 9              if (phoneNo.StartsWith( " 86 ")) phoneNo =  " + " + phoneNo;
10              else  if (!phoneNo.StartsWith( " +86 ")) phoneNo =  " +86 " + phoneNo;
11 
12             phoneNo = EncodePhone(phoneNo);
13             message = EncodeMessage(message);
14 
15              return  string.Format( " AT+CMGS={0}\r{1}{2}{3} ", (phoneNo.Length + message.Length) /  2 -  1, phoneNo, message, ( char) 26);
16         }

 

 1  ///   <summary>
 2           ///  對電話號碼進行編碼
 3           ///   </summary>
 4           ///   <param name="phone"> The phone. </param>
 5           ///   <returns></returns>
 6          private  static  string EncodePhone( string phone)
 7         {
 8              var result =  new StringBuilder();
 9 
10              /* *構建協議頭部* */
11             result.Append( " 00 "); // Length of SMSC information. Here the length is 0, which means that the SMSC stored in the phone should be used. Note: This octet is optional. On some phones this octet should be omitted! (Using the SMSC stored in phone is thus implicit)
12             result.Append( " 11 "); // PDU type (forst octet)文件頭字節,一般為11或者01,10為亂碼
13             result.Append( " 00 "); // 信息類型(TP-Message-Reference),一般為00
14 
15              /* *構建被叫號碼地址(目的地址(TP-Destination-Address)* */
16              var isInternational = phone.StartsWith( " + ");
17              if (isInternational) phone = phone.Remove( 01); // 去除前面的+
18              var header = (phone.Length <<  8) +  0x81 | (isInternational ?  0x10 :  0x20);
19             result.Append(Convert.ToString(header,  16).PadLeft( 4' 0 ')); // 被叫號碼長度+被叫號碼類型
20 
21              if (phone.Length %  2 ==  1) phone = phone +  " F "; // 個數為奇數,則在后面補F湊成偶數
22             phone = SwapOddEven(phone);
23             result.Append(phone); // 互換了奇偶位的電話號碼
24 
25              /* *構建協議尾部* */
26             result.Append( " 00 "); // 協議標識TP-PID,這里一般為00 
27             result.Append( " 08 "); // 數據編碼方案TP-DCS(TP-Data-Coding-Scheme),采用前面說的USC2(16bit)數據編碼 
28             result.Append( " 00 "); // 有效期TP-VP(TP-Valid-Period)
29               // if (_validityPeriodFormat != ValidityPeriodFormat.FieldNotPresent)
30               //     result.Append("00"); // 有效期TP-VP(TP-Valid-Period)
31               // result.Append("A7"); // ?
32 
33              return result.ToString();
34         }

 

 1  ///   <summary>
 2           ///  對信息內容進行編碼
 3           ///   </summary>
 4           ///   <param name="message"> The message. </param>
 5           ///   <returns></returns>
 6          private  static  string EncodeMessage( string message)
 7         {
 8              var len = Encoding.BigEndianUnicode.GetByteCount(message);
 9 
10              var result =  new StringBuilder();
11              // 信息內容長度,一個字節兩個16進制表示
12               // result.Append(Convert.ToString(len, 16).PadLeft(2, '0'));
13             result.AppendFormat( " {0:X2} ", len);
14              // Unicode 兩個字節,4個16進制數表示
15               // foreach (byte b in messageBytes)
16               //     result.AppendFormat(Convert.ToString(b, 16).PadLeft(2, '0'));
17              foreach ( var m  in message)
18             {
19                 result.AppendFormat( " {0:X4} ", ( int)m);
20             }
21 
22              return result.ToString();
23         }

 

 1  ///   <summary>
 2           ///  互換奇偶位
 3           ///   </summary>
 4           ///   <param name="source"> 原字符串,如1234567890 </param>
 5           ///   <returns> 返回互換了奇偶位的字符串,如:2143658709 </returns>
 6          private  static  string SwapOddEven( string source)
 7         {
 8              var result =  string.Empty;
 9 
10              for ( var i =  0; i < source.Length; i++)
11                 result = result.Insert(i %  2 ==  0 ? i : i -  1, source[i].ToString());
12 
13              return result;
14         }

構建好發送串之后,就可以通過串口將內容發送出去。

這里有一點要注意的,發送完每個AT命令之后,一定要睡眠一段時間,否則是不能發送出去的。這個可能是連續多條命令會被當成一條命令使用吧,求大神解答。

 

 1 buffer = Encoding.ASCII.GetBytes(SMSUtil.GenSendCmd(phone, message));
 2             serialPort.Write(buffer,  0, buffer.Length);
 3             Thread.Sleep( 100);
 4             len = serialPort.BytesToRead;
 5              if (len >  0)
 6             {
 7                 buffer =  new  byte[len];
 8                 serialPort.Read(buffer,  0, len);
 9                 Console.WriteLine(Encoding.ASCII.GetString(buffer));
10             }

發送后,返回串會是如下的內容(返回報文頭+經過編碼的發送內容)

比如我發送的內容為:

string.Format("hello,marvin,now time is {0}", DateTime.Now)

 

AT+CMGS=99
00110
> 00d91683115509050F00008005400680065006C006C006F002C006D0061007200760069006E002C006E006F0077002000740069006D006500200069007300200032003000310032002F0037002F0034002000310031003A00320033003A00340035

因此根據返回內容是否包含\r\n>來判斷是否已成功發送出去

 

完整的發送函數如下:

 

 1  private  void Send( string phone,  string message)
 2         {
 3              var serialPort =  new SerialPort( " COM6 "115200, Parity.None,  8, StopBits.One)
 4             {
 5                 WriteTimeout =  500,
 6                 ReadTimeout =  500,
 7                 RtsEnable =  true,
 8                 DtrEnable =  true
 9             };
10             serialPort.Open();
11                 
12              var buffer = Encoding.ASCII.GetBytes(SMSUtil.GenSendFormatCmd()); // 設置發送格式
13             serialPort.Write(buffer,  0, buffer.Length);
14             Thread.Sleep( 100);
15              var len = serialPort.BytesToRead;
16              if (len >  0)
17             {
18                 buffer =  new  byte[len];
19                 serialPort.Read(buffer,  0, len);
20                 Console.WriteLine(Encoding.ASCII.GetString(buffer));
21             }
22 
23             buffer = Encoding.ASCII.GetBytes(SMSUtil.GenSendCmd(phone, message));
24             serialPort.Write(buffer,  0, buffer.Length);
25             Thread.Sleep( 100);
26             len = serialPort.BytesToRead;
27              if (len >  0)
28             {
29                 buffer =  new  byte[len];
30                 serialPort.Read(buffer,  0, len);
31                 Console.WriteLine(Encoding.ASCII.GetString(buffer));
32             }
33 
34             serialPort.Close();
35         }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM