文章目录
前面在座的项目一直在短信的发送上下功夫,尤其是在PDU编码上花了很多时间,但同时也完成了对AT指令发送函数的封装与优化,后来添加了短信的获取与收件箱短信的删除的功能,现在总结一下这个模块的几个函数…
一. 回显短信的两种方式
1.1 PDU编码显示
这里我们先观察两种回显短信的方式有什么不同,先不关心如何设置这两种模式以及短信是如何获取到的:
- AT+CMGL=4查看所有短信(为啥是4后面会说)
如图中所示,+CMGL: 1,1,39
其中,第一个1表示这是第一条短信,相当于数组的下标,第二个1位该条短信的状态,1表示已读短信,39位编码长度,而后的一串字符串即PDU编码,其中包括了短信中心号,电话号码,短信内容以及一些号码格式,长度与发件时间等信息的处理,最后组成了这样一个PDU编码,我们可以通过对该编码的解码,即可获取到短信内容与发件人号码…
关于获取到的短信PDU编码详解可参考:接收短信PDU解码
1.2 Unicode显示
- AT+CMGL=“ALL” 查看所有短信
可以看到,在这种模式下,串口已经自动帮我们把收件人号码解析了出来,同样还包括日期,都做了处理,自动将短信内容的Unicode编码列了出来,“REC READ”表示已读,最特别的是,如果是TEXT格式的短信,会直接显示短信内容,例如第2条短信为“ hello world! ”,而不是他的Unicode编码,所以在后面编写串口编程的代码时,可以再第二种模式下更为方便.
二. 相关指令的学习
2.1 AT+CMGF 设置短信回显模式
前面介绍了两个显示短信的模式,且在两种模式下查看短信的命令是有一定区别的,下面来看第一种模式:
AT+CMGF=0
该命令将短信的显示模式设置为PDU编码显示的模式,短信相关的一切信息均在编码中,需要我们进行解码.
AT+CMGD=1
该命令将短信回显模式设置为1.2中的Unicode显示,系统会帮我们处理一部分信息.
AT+CMGF=?
AT+CMGF?
以上两个命令分别用来查看支持的模式以及当前处于哪个模式
2.2 AT+CMGL 查看短信
在使用这个命令之前,我们需要用到上面的命令来查看当前的短信回显模式
2.2.1 AT+CMGF=0 模式
在这个模式下,我们使用AT+CMGL查看短信时,需要使用数字,使用下面命令查看支持的数字:
支持0,1,2,3,4五个数字,其中:
0 —— REC UNREAD
1 —— REC READ
2 —— STO UNSENT
3 —— STO SENT
4 —— ALL
AT+CMGL=4
显示所有短信,使用相应的数字显示相应属性的短信
2.2.2 AT+CMGF=1 模式
该模式在使用AT+CMGL时,需要使用英文,如图:
可以看到,在这个模式下,AT+CMGL=?获取到的不是数字,而是数字所对应的意义。
需要注意的是,在第一种模式下只能用数字,第二种模式下只能用文字:
2.3 AT+CMGD 删除短信
通过index用来删除地址处的短信
AT+CMGD=0
作用:删除index为0处的短信
除了这样一条条删除之外,可以通过delflag标识来删除
AT+CMGD=<index>[,<delflag>]
<index> 0~255 整数型;关联存储器支持的地址编号范围内的取值
<delflag>
- 删除指定的短信
0 删除指定的短信
1 全部删除存储器中的已读短信
2 全部删除存储器中的已读和已发送短信
3 全部删除存储器中的已读、已发送和未发送短信
4 全部删除存储器中的已读、未读、已发送和未发送短信
AT+CMGD=1,4
作用:删除所有短信
三. 编程实现短信的获取与解析
基于上文所述短信回显的第二种模式进行编程
3.1 字符流转字节流
AT+CMGL="ALL"
使用上述命令获取短信,显示如下,
图中的5927540959275229FF0C4ECA665A54039E21FF01,,,或者54C854C854C8就是短信内容的Unicode编码,需要转化为UTF-8编码我能才能看得懂短信内容,然而在转换之前,千万不能忘记将字符流的数据转为字节流,这是为什么呢?
因为,我们在获取到这一个字符串后,实际上,计算机是保存的这些字符的数值,例如5为0x35,9为0x39,F为0x46(计算机底层保存了他们的二进制),所指用这个去转码是不正确到的,需要将其转化为字节流的数据才可使用,下面是代码:
/*
* 函数名:void Str2Hex(const char *source,int source_len,char *dest)
* 功能 :将字符流的数据转为字节流形式
* 参数 :source - 字符流数据 source_len - 长度 dest - 用来保存转换后的字节流数据
* 返回值:无
*
* */
void Str2Hex(const char *source,int source_len,char *dest)
{
int i;
unsigned char HighByte; //保存高位
unsigned char LowByte; //保存低位
for(i=0; i<source_len; i++)
{
HighByte = toupper(source[i*2]); //如果遇到小写,则转为大写处理
LowByte = toupper(source[i*2+1]);
if(HighByte <= 0x39) //0x39对应字符'9',这里表示是数字
HighByte -= 0x30;
else //否则为字母,需要跳过7个符号
HighByte -= 0x37;
if(LowByte <= 0x39)
LowByte -= 0x30;
else
LowByte -= 0x37;
/*
* 假设字符串"3c"
* 则 HighByte = 0x03,二进制为 0000 0011
* LowByte = 0x0c,二进制为 0000 1100
*
* HighByte << 4 = 0011 0000
* HighByte | LowByte :
*
* 0011 0000
* 0000 1100
* -------------
* 0011 1100
*
* 即 0x3c
*
**/
dest[i] = (HighByte << 4) | LowByte;
}
}
我前面的博客解释了转换的原理:字符流与字节流的相互转换原理及代码(Linux C)
3.2 利用iconv系列函数完成转码
/*
* 函数名:int unicode_to_utf8(char *unic_buf,size_t unic_len,char *utf8_buf,size_t utf8_len)
* 功能 :将Unicode编码数据转为UTF-8编码的数据
* 参数 :unic_buf - Unicode字符流数据 utf8_buf - 长度
* 返回值:成功返回0,失败返回-1
*
* */
int unicode_to_utf8(char *unic_buf,size_t unic_len,char *utf8_buf,size_t utf8_len)
{
int i = 0;
int retval;
iconv_t cd;
char temp;
char hex_unic[128] = {0};
Str2Hex(unic_buf,hex_unic,strlen(unic_buf)); //字符流转字节流
/* MSB to LSB */
for(i;i<unic_len;i+=2)
{
temp = hex_unic[i];
hex_unic[i] = hex_unic[i+1];
hex_unic[i+1] = temp;
}
/*
* The iconv_open() function allocates a conversion descriptor suitable for converting byte sequences
* from character encoding fromcode to character encoding tocode.
* */
cd = iconv_open("UTF-8","UNICODE");
if(cd < 0)
{
printf("iconv_open failed.\n");
return -1;
}
char *punic = hex_unic; //The parameters required by the function
retval = iconv(cd,&punic,&unic_len,&utf8_buf,&utf8_len);
if(retval < 0)
{
printf("iconv failed\n");
return -2;
}
iconv_close(cd);
}
ps:参数unic_buf传的是字符流的数据,函数中包含了Str2Hex()函数。
3.3 上层调用,获取指定号码的首条短信
/************************************************************************************
*
* Function: int get_module_sms(ComportAttr *comport,char *sms_buf_utf8,int buf_len,char *sender_number)
*
* Parameter: ComportAttr *comport - Serial port for communication
*
* char *sms_buf - Save the received SMS
*
* char *buf_len - The length of sms_buf_utf8
*
* char *sender_number - Used to save the sender phone number
*
*
* Description: Get SMS of target_number from 4g module,Both PDU and TEXT are supported
*
* Return Value: 0 - There is no SMS in the module or no SMS for the target_number
*
* 1 - Success
*
* negative number - Failed
*
************************************************************************************/
int get_module_sms(ComportAttr *comport,char *sms_buf_utf8,int buf_len,char *target_number)
{
int i;
int retval;
int PDU_flag = 1;
int compare_flag = 0;
char rbuf[1024] = {0};
char sms_buf[128] = {0};
char compare_number[16] = {0};
char *ptr = NULL;
char *data_start = NULL;
char *data_end = NULL;
if(!sms_buf_utf8 || !target_number)
{
printf("Invalid argument.\n");
return -1;
}
/* View SMS */
retval = send_at_cmd(comport,"AT+CMGL=\"ALL\"\r","OK",rbuf,sizeof(rbuf),2);
if(retval < 0)
{
printf("Get SMS failed.\n");
return -2;
}
/* Check for SMS */
if(strstr(rbuf,"+CMGL:") == NULL)
{
printf("There is no new sms\n");
return 0;
}
ptr = rbuf;
while((ptr = strstr(ptr,"\",\"")) != NULL)
{
ptr+=3;
strncpy(compare_number,ptr,11);
if(strcmp(compare_number,target_number) == 0)
{
compare_flag = 1;
break;
}
else
{
continue;
}
}
if(!compare_flag)
{
printf("There is no message for %s\n",target_number);
return 0;
}
/* Locate SMS data */
data_start = strstr(ptr,"\r\n");
data_start+=2;
data_end = strstr(data_start,"\r\n");
strncpy(sms_buf,data_start,(int)(data_end - data_start));
/* Determine the SMS format */
for(i = 0; i < strlen(sms_buf); i++)
{
if(sms_buf[i] >= 0x47)
PDU_flag = 0;
}
if(PDU_flag)
unicode_to_utf8(sms_buf,sizeof(sms_buf),sms_buf_utf8,buf_len);
else
strncpy(sms_buf_utf8,sms_buf,buf_len);
if(1)
{
printf("%s\n",sms_buf_utf8);
}
return 1;
}
中文解释一下,该函数通过参数中的target_number,获取到与之相同号码的短信,成功则保存到sms_buf_utf8中.
3.4 删除指定号码的首条短信
* Delete the first SMS of the specified target number */
int delete_sms(ComportAttr *comport,char *target_number)
{
int retval;
int compare_flag = 0;
char *ptr = NULL;
char compare_number[16] = {0};
char sbuf[128] = {0};
char rbuf[1024] = {0};
if(!comport || !target_number)
{
printf("Invalid argument.\n");
return -1;
}
retval = send_at_cmd(comport,"AT+CMGL=\"ALL\"\r","OK",rbuf,sizeof(rbuf),2);
if(retval < 0)
{
printf("Failed to view SMS.\n");
return -2;
}
ptr = strstr(rbuf,"+CMGL");
if(!ptr)
{
printf("There is no SMS in the module.\n");
return 0;
}
while((ptr = strstr(ptr,"\",\"")) != NULL)
{
ptr+=3;
strncpy(compare_number,ptr,11);
if(strcmp(compare_number,target_number) == 0)
{
compare_flag = 1;
break;
}
else
{
continue;
}
}
if(!compare_flag)
{
printf("Can not find target_number\n");
return -1;
}
ptr-=14;
sprintf(sbuf,"AT+CMGD=%d\r",atoi(ptr));
retval = send_at_cmd(comport,sbuf,"OK",NULL,0,2);
if(retval < 0)
{
printf("Delete SMS failed.\n");
return -3;
}
return 1;
}
四. 测试
这是我用手机给模块发送的短信
下面的程序获取并删除短信:
如果需要,可以列出收件时间!