技术热线: 4007-888-234

单片机通过短信猫收发短信的方法

更新时间: 2019-03-26
阅读量:3029

单片机通过短信猫收发短信的方法

       短信在现今的生活中起着非常重要的作用,我们每天都会使用它来进行信息的接收与发送,为我们的沟通提供新的手段。从本质上说,其实是一种数据传输的机制,通过GSM(全球移动通信系统)作为其传输的道路,从而实现了数据的远距离传输。如果我们把它运用于单片机上,就可以实现单片机上的数据远距离传输,这在实际的应用中是十分有用的。如数据的远程采集与传输、环境的监测与报警等。
       然而要实现单片机发送短信,就要依赖于专用的硬件――短信猫(GSM Modem),,它用来负责与GSM网络进行通信,而单片机则负责短信的编解码。下面介绍短信的编码,其实这些编码我们每天都在使用,只是它藏匿于手机或终端设备中,不被我们熟知。

短信编码

以一个发送实例来进行讲解:
       笔者位于哈尔滨市,其短信中心号码为13800451500,短信接收方号码采用我本人的号码15846003114,短信内容(最多为140个字节,如果是中文则最多为70个字)为“大家好!!!”。
先列出最终的编码,然后再来慢慢分析:
0891683108401505F011000B815148063011F40008A70C59275BB6597DFF01FF01FF01
将以上编码通过串口写入短信猫,再辅以相应的AT指令与附加信息就可以成功发送短信。
1.08:短信中心地址长度(可以固定不变)
2.91:短信中心号码类型(可以固定不变)
3.68:中国地区代码(在中国范围内固定不变)
4.3108401505F0:短信服务中心号码 13800451500
 (根据地域的不同进行变动,实际情况下,可以固定不变,不论在中国任何地域都通过哈尔滨短信中心,但使用当地的服务中心收发会更快。)
       可以看到,短信服务中心号码采用了一种比较特殊的表示方法。其实也很简单就是在短信服务中心号码后加一个F,号码长度就变成了12位,然后对它每两位中的字符进行对调。在后面的接收方号码也是采用此种方法进行表示的,对这一点的理解的至关重要的,它将直接关系到短信发送的正确与否。
5.1100:发送短信的编码方式(可以固定不变)
6.0B:目的地址长度(可以固定不变)
7.81:目的地址类型(可以固定不变)
8. 5148063011F4:目的地址,即接收方号码 15846003114
如前面所说,接收方号码亦采用此种方法。
9.0008:发送中文字符方式
10.A7:(可以固定不变)
11.0C:短信内容长度

12. 59275BB6597DFF01FF01FF01:发送中文字符的UNICODE码
(根据发送信息内容进行变动)
中文的发送采用UNICODE编码。由于手机上对编程的解码也是采用UNICODE的,因此只有只用UNICODE编码才能使短信内容成功显示在手机上。其次,在向手机发送短信时,短信内容需要使用内存方式的显示表示,如01FF为一个字符的UNICODE码,在编码中它为4个ASCII码,而它表示是0x01ff,为两个字节,这一点至关重要,不采用这种表示方法或表示有错,则会导致在接收方手机上无显示或显示不正常。
      此编码段是单片机进行数据传输的核心部分,它是真正的数据。在日常的中文短信编码中均采用UNICODE,但其本身并不局限于UNICODE,如果接收终端是支持GB2312的,而不支持UNICODE,那么就可以在这里放相应的GB码。脱离开字符编码,其实可以放入任何数据,只是要在上一个编码段中设置相应的数据长度即可。
      在笔者的短信收发系统中,采用西门子的TC35i作为短信猫,由于使用了一种支持GB2312硬件字库的液晶显示器,因此在数据编码中直接使用GB码,成功实现短信发送与接收,并在显示器上对解码后的内容进行显示,达到了较好的效果。

14.:发送结束标志(十六进制为0x1A),表示短信码结束。
结束标志在实际的操作中很容易遗忘,请读者加以注意。

 UNICODE与GB2312码

 与短信收发相关的AT指令

短信编码还要辅以相应的AT指令,才能实现短信的发送。AT指令如下:
AT:用以与Modem的握手,返回OK则说明握手成功。
AT+CMGR=X:用以读取第X条短信。

AT+CMGD=X:用以删除第X条短信。
AT+CMGS=X:设置发送短信的字节数为X。
实际的AT指令集功能非常丰富,在应用中也是非常重要,尤其在手机开发中更如此。在这里只是实现简单的短信收发,只涉及到以上几条AT指令。读者如果想进行深入的了解,可以翻阅相应的手册。

短信的发送过程(以下过程中>代表发送,<代表接收)

通讯握手

>AT(回车 十六进制的0x0d 0x0a)
         <ok< />
           收到OK,说明握手成功。
       2.  设置短信字节数
          >AT+CMGS=26(回车)
           设置短信字节数,由笔者的使用经验,后面的数值为短信内容的字节数+14。
           如上面的编码中内容为12个字节,则后面的数值为12+14=26
       3.  发送短信编码
          <>
           收到>符号,说明猫正在等待接收短信编码
>0891683108401505F011000B815148063011F40008A70C59275BB6597DFF01FF01FF01

          发送短信编码,送送完毕后,如果短信猫已经接收,则启动短信发送。
       4.短信发送结果检测
          <+CMGS:45
         <ok< />
          在短信发送成功后,会返回&ldquo;+CMGS&rdquo;,后面的45,是表示第45条短信。如果没有返回此字符串则说明发送失败。

          以上过程可以通过串口调试工具手工进行,而这里需要实现的是单片机的自动发送。握手、设置短信字节数、发送短信编码、发送结果检测等都需要单片机来实现。
   (5)单片机实现短信自动发送
            进行短信发送的前提是短信内容的正确编码。经过以上对短信发送过程的分析,可以通过单片机对其进行实现。下面是实现程序例程:
/*-------------------------------------------------------------------------
 函数名:PDU_SMS()
 功能  :发送短信
 参数说明:SMS_Center为短信中心号码 11位

           SMS_Telenum为短信接收方的号码 11位

           SMS_Context为短信的内容

--------------------------------------------------------------------------------*/

int PDU_SMS(char *SMS_Center, char *SMS_Telenum,

char *SMS_Context,char is_GB)

{

  int i,j;

  unsigned char len,time;

  char lens[3];

  time=0;

  for(i=0;i<300;i++) PDU_Code[i]=PDU_t[i];

  /*----------设置短信中心号码--------------*/

  for (i = 0, j = 0; i < strlen(SMS_Center) / 2; i++)

  {

    PDU_Code[6+(j++)] = SMS_Center[2 *i + 1];

    PDU_Code[6+(j++)] = SMS_Center[2 *i];

  }

  PDU_Code[6+j++] = 'F';    //在最后补上的F

  PDU_Code[6+j] = SMS_Center[strlen(SMS_Center) - 1];

  /*---------------------------------------------*/

  /*----------设置接收号码--------------*/

  for (i = 0, j = 0; i < strlen(SMS_Telenum) / 2; i++)

  {

    PDU_Code[26+(j++)] = SMS_Telenum[2 *i + 1];

    PDU_Code[26+(j++)] = SMS_Telenum[2 *i];

  }

  PDU_Code[26+j++] = 'F';

  PDU_Code[26+j] = SMS_Telenum[strlen(SMS_Telenum) - 1];

  /*---------------------------------------------*/

  /*----------设置短信内容长度--------------*/

  if(is_GB==0)

  len = strlen(SMS_Context) *2;

  else

  len = strlen(SMS_Context);



  PDU_Code[44] = (len >> 4) > 9 ? (len >> 4) + 55: (len >> 4) + 48;

  PDU_Code[45] = (len &0x0f) > 9 ? (len &0x0f) + 55: (len &0x0f) + 48;

  /*---------------------------------------------*/



  /*----------编码短信内容--------------*/

  if(is_GB==0)//如果不是GB码,短信内容为ascii码字符串

  {

  for (i = 0,j=0; i<strlen(SMS_Context);i++)

  {

     szzh16(SMS_Context[j++],lens);

     PDU_Code[46+i*4] = '0';

     PDU_Code[46+i*4+1] = '0';

     PDU_Code[46+i*4+2] = lens[0];

     PDU_Code[46+i*4+3] = lens[1];

  }

  PDU_Code[46+i*4] = 0x1a;

  PDU_Code[46+i*4+1] = 0xff;

  }

  else              

//短信内容为GB码,如果要使手机能够显示,改到UNICODE编码

  {

  for (i = 0,j=0; i<strlen(SMS_Context);i++)

  {

     szzh16((int)SMS_Context[j++],lens);

     PDU_Code[46+i*2] = lens[0];

     PDU_Code[46+i*2+1] = lens[1];

  }

  PDU_Code[46+i*2] = 0x1a;

  PDU_Code[46+i*2+1] = 0xff;

  }

  /*---------------------------------------------*/

  if(PDU_HandShake())

  {

  do

  {

  //LCD_PutChn(5,96,"SS...");

  //if(is_GB)

  PDU_EnablePDU();

  if(is_GB==0)

  PDU_SetLength(Strlen(SMS_Context)*2);

  else

  PDU_SetLength(Strlen(SMS_Context));

  PDU_Send(PDU_Code);

  //LCD_PutEng(5,96,"SS");

  for(i=0;i<25;i++)

  delay(20000);  

  sbuf[counter]=0;

  //LCD_PutNum16(5,96,time);

  counter=0;

  time++;

  }

 //判断是否发送成功,如果不成功继续发送,最多4次,如仍不成功,返回0

  while(strpos(sbuf+strlen(sbuf)-20,'G')==-1&&time<4);



  if(strpos(sbuf+strlen(sbuf)-20,'G')==-1)

  return 0;

  else

  return 1;  //成功的话返回0

  }

  else

  {

  //LCD_PutEng(5,96,"LL");

  sbuf[counter]=0;

  //LCD_PutEng(0,0,sbuf);

  counter=0;

  return 0;

  }

}

       以上程序成功实现短信的发送,其中的一些函数限于篇幅可自行实现。
   (6)单片机对短信的读取与解码
            单片机可以通过AT指令对短信猫中的短信进行读取,并对读入的短信数据进行分析与解码。
            读出的短信格式与发送时的短信编码大致是相同的。下面给出相应的程序例程,读者可以在自行实验中对照验证。

读取某一条短信,并将其进行显示

/*-------------------------------------------------------

函数名:LAD_SMS()(short for "Load And Display the Short MessageS")

功能:用户函数,读取第n条短信,并在LCD的(x,y)位置显示出来

作者:于振南

----------------------------------------------------------*/

unsigned char LAD_SMS(unsigned char n,unsigned char x,unsigned char y)

{

 unsigned char i,len,t;

 char temp[5];

 char temp1[3];

 //IN_Draw_BlankorBlackRect(0,20,30,72,0);

 szzh10(n,temp1);  

//将n转为相应的字符串,如n=21,则字符串为"21",用以与AT指令拼接。

 t=85;

 clear_sbuf();

 counter=0;

 send_s("AT+CMGR=");//AT+CMGR为读取短信的AT指令

 send_s(temp1);//上面所得的字符串

 send(0x0d);

 send(0x0a);

 for(i=0;i<10;i++) delay(10000);  //等待读取完毕

 sbuf[counter]=0;   //在收到的数据末尾附加'\0'

 temp[0]=sbuf[23];

<, SPAN style="mso-spacerun: yes"> temp[1]=sbuf[24];

 temp[2]=sbuf[25];

 temp[3]=0;

 if(sbuf[25]!=0x0d) t++;

 for(i=0;i<strlen(temp);i++) if(temp[i]==0x0d) temp[i]=0;

 len=atoi(temp);  //获取收到的短信内容长度

   //LCD_PutEng(23,76,"(SM:");

   //LCD_PutNum16(27,76,n);

   //LCD_PutEng(29,76,")");

 if(len==0)

 {

  //LCD_PutEng(5,96,"EMP");

  //LCD_PutChn(x,y,">短信空");

  delay(50000);

  return 0;

 }

 len-=20;

 if(len>90)

 {

  //LCD_PutEng(5,96,"MTL");

  //LCD_PutChn(x,y,">短信太长");

  delay(50000);

  return 0;

 }



 for(i=0;i<len;i++)

 {

  temp[0]=sbuf[t+2*i];

  temp[1]=sbuf[t+2*i+1];

  temp[2]=0;

  sbuf[i]=_hex_(temp);   

  //收到的短信内容是内存方式的显示表示,转为十六进制数

 }

 sbuf[i]=0;

 Analysis_Pro();//解码后的内容在sbuf中,此函数对其进行显示输出

 //LCD_PutEng(x,y,inf_bw.Date);

 //LCD_PutEng(x,y,sbuf+29);

 delay(50000);

 clear_sbuf();

 counter=0;

 return 1;

}


检测新短信

/*-------------------------------------------------------

函数名:Check_New()

功能:用户函数,检测有无新的短信,如果有返回1,否则返回0

作者:于振南

----------------------------------------------------------*/

unsigned char Check_New()

{int i;

 send_s("AT+CMGL=0"); //AT+CMGL=0为读取新短信的AT指令

 send(0x0d);

 send(0x0a);  //发送回车

 delay(10000);   //等待接收完毕

 if(sbuf[12]=='O') return 0xff;

 if(sbuf[12]=='+')

 {

  for(i=18;i<23;i++)

  if(sbuf[i]==',') sbuf[i]=0;

  return atoi(sbuf+19);    //返回新短信的位置

 }

}


删除某条短信

/*-------------------------------------------------------

函数名:Delete()

功能:用户函数,删除第n条短信

作者:于振南

----------------------------------------------------------*/

unsigned char Delete(unsigned char n)

{

 char t[10];

 char t1[5];

 strcpy(t,"AT+CMGD=");  //AT+CMGD为删除短信的AT指令

 szzh10(n,t1);

 strcpy(t+8,t1);

 while(Send_AT_CMD(t)!=1);

 //LCD_PutEng(5,96,"SM");

 //LCD_PutNum16(7,96,n);

 //LCD_PutEng(10,96,"De");

 delay(60000);

 return 1;

}