Linux串口编程短信篇(三) ——— AT指令封装实现Check系列函数及SMS函数封装

文章目录

  • 一. 很重要的函数: send_at_cmd()
    • 1.1 设计思路
    • 1.2 send_at_cmd()
  • 二. Check系列函数
    • 2.1 check_comport_ready()
    • 2.2 check_if_there_is_sim()
    • 2.3 check_sim_login()
    • 2.4 check_sim_signal()
    • 2.5 check_sim_allready()
  • 三. 获取短信中心号码
  • 四. TEXT / PDU SMS SEND
    • 4.1 TEXT SMS SEND
    • 4.2 PDU SMS SEND
  • 五. 头文件 sms.h

一. 很重要的函数: send_at_cmd()

 标题并未提到这个send_at_cmd()函数,但可以这么说,该程序实现最终功能的最为核心的一个函数就是
send_at_cmd(),也是一个非常好用的函数… 那么,他到底重要在什么地方呢?又为什么说它好用呢?

1.1 设计思路

 刚开始学习串口编程时,写了这么两个函数,分别是 comport_send() 和 comport_recv(),这两个函数分别封装了 write() 和 read() 系统调用,主要用来实现向串口发送AT指令和接收串口回发的数据,后来,开始写发送中英文短信的函数,还有一些检查串口状态,检查SIM卡状态的函数,是这样实现的:

  1. 调用 comport_send 函数,向串口发送指定的AT指令:

      例如: comport_send(comport,“AT+CREG?\r”,len);

  1. 接下来,调用comport_recv() 函数读取串口发来的信息:

      comport_recv(comport,rbuf,buf_len,0); // 0表示不使用select阻塞

  1. 最后,要么打印rbuf,要么使用strstr() 函数来查找OK或者 ’ > ’ ( > 表示短信输入),来确定某些功能是否可用

      if(strstr(rbuf,“OK”)
        printf(" Received OK ! \n");

问题,因为很多地方都要用到这一系列的方法,所以,我将这三步进行了封装,将要发送的AT命令,期望收到的内容等信息通过参数传进函数中,如果AT指令发送成功且收到了我期望的字符,则函数成功,返回0,否则返回-1.

函数还有四个参数,分别是用来存储读取到的信息的buf,该参数可以为NULL,表示不关心串口返回的数据具体是什么,只需要知道有没有找到期望的字符,当然如果传了buf则保存至buf中,下一个参数是该参数的长度,最后一个参数用来指定read()时是否使用select() 进行计时阻塞,还有就是函数的第一个参数,必须是串口呀!

1.2 send_at_cmd()

/************************************************************************************ **     Function:  int send_at_cmd(ComportAttr *comport,char *atcmd,char *expected_recv,char *rmsg,int msgsize,int timeout)**    Parameter:  ComportAttr *comport   -   Serial port for communication*              *                char *arcmt            -   Commands to be sent to the serial port**                char *expected_recv    -   Expected information**                char *rmsg             -   Used to save the received information, can be NULL **                int msgsize            -   The size of rmsg  **                int timeout            -   If this option is not 0, the program will call select to block read**  Description:  Send the command in "atcmd" to the serial port, read the serial port, if you read the common-*                value with the lieutenant colonel expected_recv, the function returns 0, otherwise  returns -1** Return Value:  0                      -   AT command sent successfully and received the expected value*                *                negative number        -   AT command failed to send or did not receive the expected value*************************************************************************************/
int send_at_cmd(ComportAttr *comport,char *atcmd,char *expected_recv,char *rmsg,int msgsize,int timeout)
{char   temp_msg[512] = {0};if(!atcmd || !expected_recv){printf("Unable to send AT commond,Invalid parameter.\n");return -1;}if(comport_send(comport,atcmd,strlen(atcmd)) < 0){printf("Send AT commond failed:%s\n",strerror(errno));return -2;}usleep(10000);if(comport_recv(comport,temp_msg,sizeof(temp_msg),timeout) <= 0){printf("Recving message failed:%s\n",strerror(errno));return -3;}/* Search expectations */if(!strstr(temp_msg,expected_recv)){printf("Can't find what you expect to receive,\'expected_recv\' is not in \'rmsg\'\n");return -4;}if(rmsg){strncpy(rmsg,temp_msg,msgsize);}return 0;
}//7月25日 修改

正如上文所说,发送atcmd,如果读取的数据中找到了expected_recv ,则函数返回0.
关于comport_send() , comport_recv() 的实现请参考之前的博客.

二. Check系列函数

有了上面的send_at_cmd() 函数,Check系列函数的实现就简单了很多,下面来看,发短信之前,用来检查是否可以发短信的函数吧~

2.1 check_comport_ready()

设计原因: 发送短信前,必须先确保串口是可用的,通过发送 AT 并收到 OK 实现确认,因为不需要提取更多信息,所以第四个参数传NULL.
发送指令: AT
期望接收: OK
失败接收: ERROR
实际测试:
在这里插入图片描述
代码实现:

/* Check if the serial port can communicate */
int check_comport_ready(ComportAttr *comport)
{int    retval;retval = send_at_cmd(comport,"AT\r","OK",NULL,0,2);  //发送AT期望收到OKif(retval != 0){printf("Serial port cannot be used\n");return retval;}return retval;
}

成功返回 0 .

2.2 check_if_there_is_sim()

设计原因: 串口能够通信,并不代表模块可以检测出SIM,有可能SIM卡安装不当,或者没有SIM,都需要检查
发送指令: AT+CPIN?
期望接收: READY
失败接收: ERROR
实际测试:
在这里插入图片描述
代码实现:

/* Check if the module recognizes the SIM card */
int check_if_there_is_sim(ComportAttr *comport)
{int    retval;retval = send_at_cmd(comport,"AT+CPIN?\r","READY",NULL,0,2);if(retval != 0){printf("No SIM detected\n");return retval;}return retval;
}

成功返回 0 .

2.3 check_sim_login()

设计原因: 模块能检测出SIM卡,并不代表SIM已经注册上了,之前就是因为这个问题搞了很久
发送指令: AT+CREG?
期望接收: 0,1 or 0,3
失败接收: 不为0,1 或 0,3均不可用
实际测试:
在这里插入图片描述

代码实现:

/* Check the SIM card registration," 0,1 " , " 0,3 "  means available */
int check_sim_login(ComportAttr *comport)
{int    retval1;int    retval2;retval1 = send_at_cmd(comport,"AT+CREG?\r","0,1",NULL,0,2);retval2 = send_at_cmd(comport,"AT+CREG?\r","0,3",NULL,0,2);if(retval1 && retval2){printf("SIM Card is not registered\n");return -1;}return 0;
}

成功返回 0 .

2.4 check_sim_signal()

设计原因: SIM的信号强度也需要检测,太低会影响短信的发送,99,99表示无信号
发送指令: AT+CSQ
期望接收: +CSQ: **,## —— **在8~31之间越高越好,##通常是99
失败接收: 不为0,1 或 0,3均不可用
实际测试:
在这里插入图片描述
ps:因为信号可能是个位数,也有可能是十位数,所以需要进行判断,因为这里需要用到读取的内容,所以传入msg保存.

代码实现:

/* Check the SIM card signal strength ,The signal strength is too low or 99 is unavailable */
int check_sim_signal(ComportAttr *comport)
{int    i;int    retval;int    signal_strength;char   str_signal[10] = {0};char   msg[128] = {0};retval = send_at_cmd(comport,"AT+CSQ\r","+CSQ",msg,sizeof(msg),2);/* IF Sucess:** AT+CSQ** +CSQ: 9,99** OK** * * * * * * * AT+CSQ** +CSQ: 23,99** OK** */if(retval != 0){printf("Can not check signal\n");return -1;}for(i = 0; i < sizeof(msg); i++){if(msg[i] == ',')  //Locate ' , '{if(msg[i-2] == ' ')strncpy(str_signal,&msg[i-1],1);  //The signal strength is in single digits, for example: 8,99elsestrncpy(str_signal,&msg[i-2],2);  //The signal strength is ten digits, for example: 28,99}}signal_strength = atoi(str_signal);if(signal_strength < 7 || signal_strength == 99)  //Signal is too low or no signal{printf("Signal Strengh is too low or no signal\n");return -2;}return 0;
}

2.5 check_sim_allready()

封装了上述所有的check函数,全部满足则返回0,否则,返回 -1,-2…

/* Call 'check series' of functions, return 0 if all pass, otherwise return -1,-2.... */
int check_sim_allready(ComportAttr *comport)
{if(check_comport_ready(comport) < 0)return -1;printf("Serial port ready!\n");if(check_if_there_is_sim(comport) < 0)return -2;printf("SIM card can be detected!\n");if(check_sim_login(comport) < 0)return -3;printf("SIM card is registered!\n");if(check_sim_signal(comport) < 0)return -4;printf("Signal strength meets requirements!\n");printf("\nSIM cards are all ready!\n");return 0;
}

三. 获取短信中心号码

使用命令

AT+CSCA?

获取到保存了中心号码的buf后,操作buf获取中心号码
在这里插入图片描述

代码实现:

/* * Obtain the SMS center number of the SIM card* Different operators and different regions will result in different SMS center numbers* China Telecom can not use.* */
int get_sms_center_number(ComportAttr *comport,char *center_number)
{int      retval;char    *ptr;char     rbuf[128] = {0};retval = send_at_cmd(comport,"AT+CSCA?\r","CSCA",rbuf,sizeof(rbuf),2);/* IF Success:** AT+CSCA?** +CSCA: "+8613010788500",145** OK** */if(retval < 0){printf("Can not receive CSCA\n");return -1;}ptr = strstr(rbuf,"CSCA");ptr += 7;strncpy(center_number,ptr,CENTER_NUM_LEN);return 0;
}

四. TEXT / PDU SMS SEND

 在写好PDU编码,send_at_cmd() 等功能模块的函数后,再来写发送短信的函数就会简单很多,只要搞清楚AT发短信的流程就很轻松的写出来了. 关于AT发送短信的步骤需要搞懂,可参考:AT指令发送中英文短信详细流程

4.1 TEXT SMS SEND

因为ASCII编码足以表示所有英文字符以及英文符号,不需要用到PDU编码,所以才把TEXT格式的短信单独列出来,发送短信的步骤也简单很多

在这里插入图片描述
代码:

/* Send SMS in TEXT format */
int text_sms_send(ComportAttr *comport,char *sms_buf,char *phone_number)
{int    retval;char   sbuf[30] = {0};if(!sms_buf || !phone_number){printf("Invalid parameter\n");return -1;}retval = send_at_cmd(comport,"AT+CMGF=1\r","OK",NULL,0,2);  //"AT+CMGF=1" means TEXT SMSif(retval < 0){printf("Send \"AT+CMGF=1\" failed or can not receive \"OK\"\n");return -2;}/* Phone Number */sprintf(sbuf,"AT+CMGS=\"%s\"\r",phone_number);/* Expect to receive ' > ' */retval = send_at_cmd(comport,sbuf,">",NULL,0,2);if(retval < 0){printf("Send AT+CMGS failed or can not receive \'>\'\n");return -3;}strcat(sms_buf,"\x1a");/* Send SMS,'\x1a'is the end of SMS sending Peugeot */retval = send_at_cmd(comport,sms_buf,"OK",NULL,0,10);if(retval < 0){printf("SMS send failed\n");return -4;}printf("SMS sent successfully!\n");return 0;
}

4.2 PDU SMS SEND

 写到这里,就会发现之前的努力都是值得的,正是因为前面将所有需要用到的功能模块都进行的封装,PDU编码只需要传入几个参数便可完成,所以,再来写PDU SMS SEND 函数时,只需要清楚发送PDU格式短信的步骤,同样可以很轻松的写出函数啦!

在这里插入图片描述

代码:

/* Send PDU SMS , Will call the PDU encoded function */
int pdu_sms_send(ComportAttr *comport,char *sms_buf,char *phone_number)
{int     value_cmgs;char    pdu_buf[512] = {0};char    sbuf[32] = {0};char    center_number[128] = {0};if(!sms_buf || !phone_number){printf("Invalid parameter\n");return -1;}if(get_sms_center_number(comport,center_number) < 0)  //Obtain SMS Center Number{printf("Can not get SMS center number\n");return -2;}if(pdu_encod(sms_buf,center_number,phone_number,pdu_buf,&value_cmgs) < 0)  //PDU encoding{printf("Failed to construct PDU code\n");return -3;}if(send_at_cmd(comport,"AT+CMGF=0\r","OK",NULL,0,2) < 0)  //"AT+CMGF=0" means PDU SMS{printf("Send \"AT+CMGF=0\" failed\n");return -4;}/* Splice the processed phone number with the processed SMS into a string* value_cmgs represents one-half the length of the string, in decimal.* */sprintf(sbuf,"AT+CMGS=%d\r",value_cmgs);/*  Expect to receive ' > ' */if(send_at_cmd(comport,sbuf,">",NULL,0,2) < 0)  //Most of the failures are due to PDU encoding failure{printf("Send \"AT+CMGS=%d\"failed,or can not receive \'>\'\n",value_cmgs);return -5;}strcat(pdu_buf,"\x1a");/* If it can't receive OK within 10s,return */if(send_at_cmd(comport,pdu_buf,"OK",NULL,0,10) < 0){printf("PDU SMS send failed\n");return -6;}printf("SMS sent successfully!\n");return 0;
}

五. 头文件 sms.h

/*********************************************************************************      Copyright:  (C) 2020 LuXiaoyang<920916829@qq.com>*                  All rights reserved.**       Filename:  comport.h*    Description:  This head file of sms.c**        Version:  1.0.0(09/07/20)*         Author:  LuXiaoyang <920916829@qq.com>*      ChangeLog:  1, Release initial version on "09/07/20 08:58:33"*                 ********************************************************************************/
#ifndef  _SMS_H_
#define  _SMS_H_#include "comport.h"
#include "PDU.h"#define CENTER_NUM_LEN 14/* Send AT Commond(comport_send plus) */
int send_at_cmd(ComportAttr *comport,char *atcmd,char *expected_recv,char *rmsg,int msgsize,int timeout);/* Check if the serial port can communicate */
int check_comport_ready(ComportAttr *comport);/* Check if the module can recognize the SIM card */
int check_if_there_is_sim(ComportAttr *comport);/* Check the SIM card registration */
int check_sim_login(ComportAttr *comport);/* Check the SIM card signal strength */
int check_sim_signal(ComportAttr *comport);/* Use the above function to check whether the SIM card is ready to be used */
int check_sim_allready(ComportAttr *comport);/* Get SMS Center Number of the SIM Card */
int get_sms_center_number(ComportAttr *comport,char *center_number);/* Send TEXT Message */
int text_sms_send(ComportAttr *comport,char *sms_buf,char *phone_number);/* Send PDU message */
int pdu_sms_send(ComportAttr *comport,char *sms_buf,char *phone_number);#endif   /*   ----- #ifndef _COMPORT_H_  ----- */


本系列函数属于较上层的功能实现函数,封装了send_at_cmd() 函数,从而轻松的实现其他功能函数,通过该函数,设计了检查串口和SIM卡相关状态的一系列函数,在通过check_sim_allready() 将check系列函数进行封装,从而一个函数就能实现发送短信前的检测工作;
在发送短信时,也只是停留在传入参数即可实现所有功能,短信中心号,电话号码,UTF-8等数据的处理,统统交由PDU.c中的pdu_encod()函数实现,函数要做的仅仅是与发送指令而已,像这样使用一层一层的封装,调用,使得函数的移植性得到了很大的提升,这样,在真正的main程序中,只需要简单的调用几个函数即可实现全部功能…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/256177.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Compareable接口

重写 compareTo方法如下&#xff1a; public int compareTo(Object o) {Name n (Name) o;int lastCmp secondName.compareTo(n.secondName);return(lastCmp!0 ? lastCmp:firstName.compareTo(n.firstName));}完整代码如下 import java.util.List; import java.util.LinkedLi…

@ServeletComponentScan和@ComponentScan的区别

一、SpringBoot中使用Servlet 在SpringBootApplication上使用ServletComponentScan注解后&#xff0c;Servlet、Filter、Listener可以直接通过WebServlet、WebFilter、WebListener注解自动注册&#xff0c;无需其他代码。 1.在入口Application类上加入注解ServletComponentSca…

Linux系统imx6ull开发板内核安装STM Virtual ComPort虚拟串口驱动

目录 前言 一、问题描述 二、解决步骤 1.更改option.c文件 2.修改内核编译选项 3.编译内核并加载到开发板 总结 前言 本文硬件使用正点原子imx6ull-min底板emmc ddr512核心板&#xff0c;软件使用正点原子针对imx6ull开发板提供的出场Linux内核源码、设备树与根文件系统&#…

MDK5:正点原子stm32遇Target not created的解决方法

几乎在直接打开这里的test.uvprojx文件进行编译运行时都会出现以上的错误。这时候只需关闭改工程&#xff0c;然后右击keil5的应用程序&#xff0c;点击“以管理员身份运行”&#xff0c;打开之后即可编译成功。

MDK5 添加Device

现象如下图&#xff1a; 出现这个问题的原因有两种&#xff0c;因此有两种解决方法&#xff1a; 第一种&#xff1a;大部分为网上介绍的&#xff0c;没有下载Pack包&#xff0c;进入Packs Installer中选择自己需要的芯片系列选择包进行下载即可。 第二种&#xff1a;不常见的…

Keil MDK 软件包(MDK5 Software Packs)离线下载方法

最近 Keil 官网的 MDK 软件包下载页面不展示软件包的下载列表了&#xff0c;无法下载软件包&#xff0c;如图1所示。 图1 Keil 官网 MDK5 Software Packs 下载页面截图 其实是有办法下载软件包的&#xff0c;方法如下&#xff1a; 1、打开 Microsoft Edge 浏览器&#xff08;其…

Keil MDK5 打开MDK4项目

安装完最新版本keil(5.38a)后&#xff0c;需要打开几个MDK4的项目&#xff0c;结果一打开keil就提示报错了。 这里我选择的是第二种方式&#xff0c;首先安装legacy support&#xff0c;以下是下载链接 MDK v4 Legacy Support (keil.com) 安装完成以后&#xff0c;可能会遇到另…

keil MDK5 无法 Go To Definition Of

目录 1.勾选 Browse Information 2.重新编辑即可 弹出如下窗口&#xff1a; 编译没有通过&#xff0c;或者函数有编译条件限制&#xff08;在某个条件成立时&#xff0c;函数才会被keil编译&#xff09;&#xff0c;没有被编译的函数是不能使用“Go To Definition Of”跳转到…

MDK5__配色方案

这个MDK5的配色方案是仿照Notepad里面的Plastic Code Wrap主题&#xff0c;字体是Courier New&#xff0c;个人比较喜欢这个主题配色方案&#xff0c;同时经常看代码不会觉得太刺眼 配色过程 修改颜色有两种方案&#xff0c;最简单的是在MDK软件中&#xff0c;通过Edit->C…

MDK5的安装

一、安装MDK5 下载&#xff1a; 官方下载地址&#xff1a;http://www2.keil.com/mdk5 1.打开官方网站&#xff0c;并点击下载MDK5 2.按照要求填写信息并点击提交 3.点击下载 安装 1.双击图标进行安装 2.进入安装界面——点击Next&#xff08;下一步&#xff09; 3.选中…

ARM KEIL: MDK5 Software Packs

ARM&#xff1a;Advanced RISC Machines 先进的精简指令集器件。 ARM是最当下最流行的处理器&#xff0c;尤其在嵌入式领域。 KEIL被ARM收购&#xff0c;MDK5是ARM官方的开发工具。 作为一款面向微控制器和微处理器的软件开发工具&#xff0c;需要支持各种各样的芯片。 所以…

STM32——新建 Keil MDK 5 工程(寄存器版本)

注&#xff1a;本文仅依照加粗字体和图片可快速新建 Keil MDK 5 工程 由于文章没有细分步骤&#xff0c;标记点用于方便阅读。 &#x1f53a; 标记点 ① 打开 Keil MDK 5&#xff0c;点击Project > New μVision Project&#xff0c;如图&#xff1a; &#x1f53a; 标记…

MDK5之.map文件解析

MDK5之.map文件解析 前言一、.map文件总览二、详细介绍1、Section Cross References&#xff1a;模块、段(入口)交叉引用2、Removing Unused input sections from the image&#xff1a;移除未使用的模块3、Image Symbol Table&#xff1a;映射符号表4、Memory Map of the imag…

MDK5 与 GIT 配合使用

参考 Abstract: (keil.com) 1&#xff0c;增加忽略文件 (.gitignore) &#xff08;我一般喜欢把输出文件放到Objects/目录下&#xff09; Listings/ Objects/ *.TMP 2 最好是mdk5.15以上版本&#xff0c;不然需要拷贝文件到此目录 Vision 5.15 is providing a SVCS templ…

Keil MDK5硬件仿真之基本介绍

Keil MDK5这个软件是提供硬件仿真功能的&#xff0c;所谓硬件仿真就是指将当前的程序实时的运行在你的设备中&#xff0c;并且可以检测程序中的变量的值&#xff0c;或者控制程序运行的流程&#xff0c;下面就让我们来看一下如何进行硬件仿真吧。 第一步&#xff0c;单击Optio…

keil MDK5软件包介绍、下载、安装与分享

前言 本文介绍了Keil MDK5软件包的分类、作用、下载、安装与更新。软件包下载可通过Keil自带的Pack Installer、进入Keil Pack下载网站手动下载、去芯片厂家官网下载三种方式。同时分享了一个小技巧&#xff0c;可以直接分享已安装好的软件包给别人。 一. Keil MDK软件包介绍 K…

Ubuntu安装MDK5

1 环境部署 Ubuntu 18.04 Wine 3.0.4 1.1 查看CPU信息 lscpu序号属性描述1架构x86_642CPU 运行模式32-bit, 64-bit3字节序Little Endian4CPU45在线 CPU 列表0-36每个核的线程数17每个座的核数48座19NUMA 节点110厂商 IDGenuineIntel11CPU 系列613型号15814型号名称Intel Core…

下载MDK5 Software Packs导入Keil5

⭐最近在用Keil5编译STM32L系列的项目文件&#xff0c;在选择芯片型号时并没有自己的STM32L系列的芯片&#xff0c;找了很久&#xff0c;才找到下载的网址&#xff0c;现在来分享一下自己下载和安装的过程。&#x1f308; 1.下载网址 MDK Software Packs下载地址 以上就是MDK5的…

MDK5注释乱码以及解决方案

在MDK5中会出现注释乱码的情况 //GPIO_SetBits(GPIOB,GPIO_Pin_7);//0GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; //GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; /…

MDK5新建工程

文章目录 一、MDK简介二、建立STM32工程模板 一、MDK简介 MDK 源自德国的 KEIL 公司&#xff0c;是 RealView MDK 的简称。在全球 MDK 被超过 10 万的嵌入式开发工程师使用。目前最新版本为&#xff1a;MDK5.14&#xff0c;该版本使用 uVision5 IDE 集成开发环境&#xff0c;是…