短信开发系列(一):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         }

 

转载于:https://www.cnblogs.com/marvin/archive/2012/07/04/SMSPrograming.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值