既然需要通过串口来与短信modem进行通讯,就需要一个串口通讯控件,这里我使用了微软的mscomm32.ocx串口通讯控件。具体实现方法是通过对 mscomm32.ocx的几个属性设置(串口端口设置、发送数据类型设置、读写缓冲区设置等)来达到串口与短信modem进行通讯的目的,然后对输入的 短信息进行编码,把编码后的短信息发送至短信modem,再通过AT指令去控制modem的发送。整个软件由三个程序组成,一是发送主程序,二是短信编码 程序,三是串口通讯控件,下面作一一介绍。
一、串口通讯控件
微软的串行通信控件MSCOMM简化了编程,可以快速的建立通信应用程序。MSComm 控件通过串行端口传输和接收数据,为应用程序提供串行通讯功能。MSComm控件在串口编程时非常方便,程序员不必去花时间去了解较为复杂的API函数。
MSCOMM的属性和事件
MSComm 控件有很多重要的属性,这里只讲本程序应用的几个属性。
1)CommPort:设置并返回通讯端口号,在设计时,可以设置成从 1 到 16 的任何数,必须在打开端口之前设置 CommPort 属性;
2)Settings: 以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位,设置的格式为“BBBB,P,D,S”,BBBB 为波特率,P 为奇偶校验,D 为数据位数,S 为停止位数;
3)PortOpen: 设置并返回通讯端口的状态。也可以打开和关闭端口,true表示串口打开;
4)Input: 从接收缓冲区返回和删除字符;
5)Output: 向传输缓冲区写一个字符串;
6)InputMode:设置发送和接收数据的类型,InputModeConstants.comInputModeText表示文本的方式,InputModeConstants.comInputModeBinary表示二进制的方式;
7)InputLen:读取接收缓冲区的字节数,如果为0则表示读取整个缓冲区的数据
8)OutBufferCount:返回发送缓冲区内等待发送的字节数,可通过设置该属性为0来清空缓冲区。
9)InBufferCount:返回接收缓冲区内等待发送的字节数,可通过设置该属性为0来清空缓冲区。
二、短信的编码
短信息是文本信息,短信modem只支持ASCII格式码的文本信息,ASCII码格式包括英文字母A-Z和数字0-9及一些标点符号等字符,为此对于中 文短信息必须通过编码使中文变成ASCII码,短信编码有三种模式:BLOCK MODE、基于AT指令的TEXT MODE和基于AT指令的PDU MODE的编码,西门子6688手机支持PDU MODE的编码,这种编码方式是把短信正文经过16进制编码后发送。PDU的编码又分为7bit编码、8bit编码和16bit编码,7bit编码最多可 发送160个字符,8bit编码最多可发送140个字符,而16bit编码最多可发送70个字符,这种编码被用显示Unicode(UCS2)文体信息, 可被大多数手机显示,所以这里我采用的是UCS2编码,其最多可发送70个字符,不管是英文还是中文。
一条短信由三部分组成,一是接收方的手机号码,二是短信中心号码,三是短信内容。在实际的编码过程中,这三部分是分开来进行的,最后再把它们合三为一。
⑴、短信中心号码编码
首先把短信中心号码的奇数位与偶数位互换,然后看其长度是否为偶数,如果不是,在最后添加F,并对最后一位与F互换位置。接着在号码前加上91,相当于加 上一个"+"字符。最后计算编码后的总长度并除以2,并把这个数格式化为二位的16进制数。例如:广州的短信中心号码为8613800200500,编码 后为0891683108200005F0,08是91683108200005F0的长度除2,即16/2=8,二位16进制表示就是08。
㈡、接收方手机号码编码
首先检查当前接收方手机号码的前两位是否有86字样,如果没有就补上86,然后奇偶位互换,最后检查整个长度是否为偶数,如果不是就加上F,并对最后一位与F互换位置,如手机号码为13918765434,编码后为683119785634F4。
㈢、短信内容编码
首先采用Big-Endian字节顺序的Unicode格式编码,即高低位互换,接着把它存入一个字节数组,然后去掉进行Unicode编码过程中的“-”字符,最后把整条编码好后的短信内容长度除2,并格式化成二位16进制数添加到编码后的短信内容前面。
例如,短信内容为:你好,HELLO!,编码后为:4F60597DFFOC00480065006C006C006F0021,最后根据国内PDU编码 的原则在接收手机号编码前加上11000D91,在短信内容前加上000800,并把以上三部分编码串起来即可。
三、AT指令
AT指令类似于DOS命令,由命令与一些参数组成,来完成对modem实现某一功能的控制。AT指令有很多个,它的基本格式是AT+操作功能符+回车,我 编写的这款短信发送软件实际只使用了两个AT指令,分别是:AT+CSCA?和AT+CMGS,前一个是获取存于手机SIM卡里面的短信中心号码的命令, 后面一个是发送一条短信命令。通常向modem发送一个AT指令,modem如果执行完成都会有一个执行结果回送。
四、短信发送软件主程序
下面是主程序的代码,图1是主程序的运行界面
PDUdecoding sms = new PDUdecoding();
MSComm com=new MSComm();
Thread t ,w;
private
{
xiancheng1();
}
void xiancheng1()//连接串口
{
t= new Thread(new ThreadStart(InitCom));//定义连接线程
t.Start();//启动线程
}
private bool lianjie(string duankou,string botelv)
{
try
{
string dk=duankou.Trim().Substring(3);
if (com.PortOpen==true)com.PortOpen=false;
com.CommPort = Convert.ToInt16(dk);//串口号
com.InputMode =MSCommLib.InputModeConstants.comInputModeText;//传输的数据形式是文本
com.InputLen=0;//读取整个接收缓冲区内容
com.Settings = botelv+",N,8,1"; //串口基本参数设置
com.PortOpen=true;//打开串口
com.OutBufferCount=0;//清空发送缓冲区
com.InBufferCount=0;//清空接收缓冲区
return true;
}
catch
{
MessageBox.Show("错误:连接参数有错,请重新选择!");
return false;
}
}
void InitCom()
{
int i;
if(lianjie(duankou.SelectedItem.ToString(),bote.SelectedItem.ToString()))
string Buffer="";
string bufy="AT+CSCA?\r";
for(i=0;i<5;i++)
com.Output = bufy; //发送数据到串口发送缓冲区
Buffer+=com.Input;//接收串口接收缓冲区的数据
break;
}
}
if(i>4)
{
label5.Text="连接失败,重新连接!!";
}
else
{
int Num1=Buffer.LastIndexOf("86");
zhongxinhao.Text =Buffer.Substring(Num1,13);
label5.Text="连接成功!!";
}
}
else
{
label5.Text="连接失败,重新连接!!";
}
com.PortOpen=false;
t.Abort();
}
private void button2_Click(object sender, System.EventArgs e)
{
if (com.PortOpen==true)com.PortOpen=false;
Application.Exit();
}
void fsduanxin()
{
w= new Thread(new ThreadStart(fs));//定义发送线程
w.Start();//启动线程
}
private void button3_Click(object sender, System.EventArgs e)
{
fsduanxin();
}
void fs()
{
if(lianjie(duankou.SelectedItem.ToString(),bote.SelectedItem.ToString()))
{
string dhnum=shoujihaoma.Text;
fsdx(dhnum);//发送短信
com.PortOpen=false;
w.Abort();
}
}
void fsdx(string danhuahaoma)//发送短信程序段
{
int i=0;
duanxinshoufa.Text="正在发送,稍候";
string Buffer="";
string decodedSMS = sms.smsDecodedsms(zhongxinhao.Text,danhuahaoma,neirong.Text);
string bufy="AT+CMGS="+sms.nLength+"\r";
for(i=0;i<10;i++)
{
com.Output = bufy;
Thread.Sleep(300);
Buffer+=com.Input;
Thread.Sleep(500);
if( Buffer.Length > 0 && Buffer.EndsWith("> "))//判断AT指令执行的回送结果
{
string buf=decodedSMS+"\x01a";
com.Output = buf;
Thread.Sleep(1000);
string chenggong = "发送成功!";
string Result = String.Format("{0},{1},{2}。\n",danhuahaoma,neirong.Text,chenggong);
duanxinshoufa.Text += "\r\n"+Result;
break;
}
duanxinshoufa.Text +=".";
}
if(i>9)
{
duanxinshoufa.Text ="发送失败,请重新发送!";
w.Abort();
}
}
在程序中使用了线程控制,目的是协调程序运行与串口通讯之间的同步问题。另外在主程序中使用AT指令时都使用了循环,通过对AT指令执行结果回送的检测,判断AT指令是否被正确地执行,如果未被正确执行,则通过循环控制重发AT指令直至AT指令正确执行为止。
此程序界面简单易用,我把他放上来给有兴趣的朋友使用(点击这里下载)。使用此程序的注意事项:
1、此程序我只在西门子6688,SL45i手机上成功应用。
2、此程序是在.NET平台上开发的,所以要在你的电脑上安装ms.net.framework1.1,这是一个免费软件,网上有下载。
3、程序中所使用的mscomm32.ocx控件需要注册,注册步骤如下:
①、将Mscomm32.ocx,Mscomm32.dep两个文件复制到系统SYSTEM32文件夹中。
②、用Windows下的注册工具regsvr32注册该OCX控件,点击“开始”->"运行",再在其中填入 Regsvr32 C:\windows\system32\Mscomm32.ocx。
③、在注册表中手工新建一个主键项:先在点击“开始”->"运行",再在其中填入regedit命令打开注册表,找到 HKEY_CLASSES_ROOT\Licenses,在其中添加主键:4250E830-6AC2-11cf-8ADB-00AA00C00905 并将内容设置为:kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun。
{ if(Buffer.IndexOf("86")!=-1)//判断AT指令执行的回送结果 Thread.Sleep(1000); {{ void button1_Click(object sender, System.EventArgs e)