Linux串口编程 —— 4G模块短信获取与删除


前面在座的项目一直在短信的发送上下功夫,尤其是在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;
}


四. 测试

这是我用手机给模块发送的短信
在这里插入图片描述
下面的程序获取并删除短信:
在这里插入图片描述
如果需要,可以列出收件时间!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值