赞
踩
上一遍博客已经讲述了PDU编码的实现,接下来就要说如何实现发送信息了,其实发送短信的过程就是在串口下发送AT指令的过程,为了提高代码的可移植性以及看起来更简洁,所以我们先来学习一下AT指令的封装吧!
atmcmd.c 的头文件,声明了接下来的一些函数定义以及宏、枚举等信息。
代码:
#ifndef _ATCMD_H_ #define _ATCMD_H_ #include <stdlib.h> #include "myserial.h" #define SEND_BUF 128 typedef struct atcmd_ctx_s { char send_at_buf[516]; // send AT command char succe_at_buf[128]; // expect receive respond char use_buf[512]; // need store the contents of the response in the use_buf int use_len; // length of use_buf int resp_timeout; // respond time timeout }atcmd_ctx_t; typedef struct gsm_ctx_s { int status; int sim_signal; }gsm_ctx_t; enum { GSM_STATUS_INIT = 0, //SIM card and serial port initial status. GSM_STATUS_SERIAL_READY, //Serial port ready. GSM_STATUS_SIM_EXIST, //SIM card exist. GSM_STATUS_SIM_REG, //SIM card already register. GSM_STATUS_SIM_SIG, //SIM card have signal/ GSM_STATUS_READY = GSM_STATUS_SIM_SIG, //SIM card and serial port all ready. }; /*----------------------------------------------------------------------- | funtion:AT command send and receive respond. | | argument: serial: serial port property struct. | 2、atcmd: send at command relevant parameters struct. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int send_recv_atcmd(serial_ctx_t *serial,atcmd_ctx_t *atcmd); /*----------------------------------------------------------------------- | funtion:Check sim card and serial port whether already ready. | | argument: serial: serial port property struct. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_sim_serial(serial_ctx_t *serial,gsm_ctx_t *gsm_ctx); /*----------------------------------------------------------------------- | funtion:Check serial port whether already ready. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_serial_ready(serial_ctx_t *serial); /*----------------------------------------------------------------------- | funtion:Check sim cart whether already exist. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_sim_exist(serial_ctx_t *serial); /*----------------------------------------------------------------------- | funtion:Check sim cart whether already register. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_sim_register(serial_ctx_t *serial); /*-------------------------------------------------------------------------- | funtion:Check sim cart whether already signal. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |---------------------------------------------------------------------------*/ int check_sim_signal(serial_ctx_t *serial,gsm_ctx_t *gsm_ctx); /*-------------------------------------------------------------------------- | funtion:Check sms amount of sim cart. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |---------------------------------------------------------------------------*/ int check_sms_amount(serial_ctx_t *serial,int *sms_sum); #endif /* ----- #ifndef _ATCMD_H_ ----- */
该函数把前面说到的实现AT指令的收发的文章中的serial_send() 和 serial_receive() 函数封装了起来,后面只需调用这个函数就能实现AT指令的发送与接收。
代码;
/*----------------------------------------------------------------------- | funtion:AT command send and receive respond. | | argument: 1、serial_fd: serial port fd. | 2、atcmd: send at command relevant parameters struct. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int send_recv_atcmd(serial_ctx_t *serial,atcmd_ctx_t *atcmd) { int rv = 0; char recv_tranfer[512] = {0}; if(!atcmd || !serial) { printf("%s,Invalid input arguments\n",__func__); return -1; } rv = serial_send(serial,atcmd->send_at_buf,strlen(atcmd->send_at_buf)); if(rv < 0) { printf("serial port send AT cmd failure and rv: %d\n",rv); return -2; } usleep(10000); rv = serial_receive(serial,recv_tranfer,sizeof(recv_tranfer),atcmd->resp_timeout); if(rv < 0) { printf("serial port receive AT response failure and rv: %d\n",rv); return -3; } if(!strstr(recv_tranfer,atcmd->succe_at_buf)) { printf("recv_tranfer: %s",recv_tranfer); printf("recive AT response have not expect data and [%s] not in recive AT response\n",atcmd->succe_at_buf); return -4; } if(atcmd->use_len) { strncpy(atcmd->use_buf,recv_tranfer,atcmd->use_len); } return 0; }
在我们使用串口发送AT指令前必须检查串口是否已经准备好了,以及SIM卡是否存在4G模块的卡槽里,是否已经注册、是否有信号等,接下来逐一进行封装。
函数功能:检查串口是否已经就绪。
原理:在串口下我们发送AT指令后,如果串口就绪,就会返回OK,否则就返回ERROR。我们通过返回的字符串(use_buf)是否含有OK来判断是否已经就绪。
代码:
/*----------------------------------------------------------------------- | funtion:Check serial port whether already ready. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_serial_ready(serial_ctx_t *serial) { int rv = 0; atcmd_ctx_t atcmd; if(!serial) { printf("%s,Invalid input arguments\n",__func__); return -1; } memset(&atcmd,0,sizeof(atcmd_ctx_t)); strncpy(atcmd.send_at_buf,"AT\r",sizeof(atcmd.send_at_buf)); strncpy(atcmd.succe_at_buf,"OK",sizeof(atcmd.succe_at_buf)); atcmd.resp_timeout = 2; rv = send_recv_atcmd(serial,&atcmd); if(rv < 0) { printf("send_resp_serial() of %s failure and rv: %d\n",__func__,rv); return -2; } return 0; }
函数功能:检查SIM卡就是否存在
原理:发送”AT+CPIN?”,如果存在则返回“READY”,否则则返回“ERROR”,同样通过判断返回的字符串(use_buf)是否含有“READY”来判断SIM卡是否存在。
代码:
/*----------------------------------------------------------------------- | funtion:Check sim cart whether already exist. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_sim_exist(serial_ctx_t *serial) { int rv = 0; atcmd_ctx_t atcmd; if(!serial) { printf("%s,Invalid input arguments\n",__func__); return -1; } memset(&atcmd,0,sizeof(atcmd_ctx_t)); strncpy(atcmd.send_at_buf,"AT+CPIN?\r",sizeof(atcmd.send_at_buf)); strncpy(atcmd.succe_at_buf,"READY",sizeof(atcmd.succe_at_buf)); atcmd.resp_timeout = 2; rv = send_recv_atcmd(serial,&atcmd); if(rv < 0) { printf("send_resp_serial() of %s failure and rv: %d\n",__func__,rv); return -2; } return 0; }
函数功能:判断SIM卡是否已经注册上。
原理:发送”AT+CREG?”,如果已经注册上则返回“0,1”,否则则返回“ERROR”,同样通过判断返回的字符串(use_buf)是否含有“0,1”来判断SIM卡是否已经存在。
代码:
/*----------------------------------------------------------------------- | funtion:Check sim cart whether already register. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_sim_register(serial_ctx_t *serial) { int rv = 0; atcmd_ctx_t atcmd; if(!serial) { printf("%s,Invalid input arguments\n",__func__); return -1; } memset(&atcmd,0,sizeof(atcmd_ctx_t)); strncpy(atcmd.send_at_buf,"AT+CREG?\r",sizeof(atcmd.send_at_buf)); strncpy(atcmd.succe_at_buf,"0,1",sizeof(atcmd.succe_at_buf)); atcmd.resp_timeout = 2; rv = send_recv_atcmd(serial,&atcmd); if(rv < 0) { printf("send_resp_serial() of %s failure and rv: %d\n",__func__,rv); return -2; } return 0; }
函数功能:检查SIM卡是否有信号或者说信号是否符号发送要求。
原理: SIM的信号强度也需要检测,太低会影响短信的发送,99,99表示无信号,发送”AT+CSQ”,通过判断返回的字符串(use_buf)中的值来判断来判断SIM卡的信号强度是否符合。第一个值表示强度:在7~31表示正常,越高越好,不在这个范围表示信号太强或者太弱;第二个值通常为99
代码:
/*-------------------------------------------------------------------------- | funtion:Check sim cart whether already signal. | | argument: serial_fd: serial port fd. | | return: rv: success: == 0 ; error: < 0 |---------------------------------------------------------------------------*/ int check_sim_signal(serial_ctx_t *serial,gsm_ctx_t *gsm_ctx) { int i = 0; int rv = 0; atcmd_ctx_t atcmd; char judge_sig[8] = {0}; int judge_sig_int = 0; if(!serial || !gsm_ctx) { printf("%s,Invalid input arguments\n",__func__); return -1; } memset(&atcmd,0,sizeof(atcmd_ctx_t)); strncpy(atcmd.send_at_buf,"AT+CSQ\r",sizeof(atcmd.send_at_buf)); strncpy(atcmd.succe_at_buf,"+CSQ",sizeof(atcmd.succe_at_buf)); atcmd.use_len = sizeof(atcmd.use_buf); atcmd.resp_timeout = 2; rv = send_recv_atcmd(serial,&atcmd); if(rv < 0) { printf("send_resp_serial() of %s failure and rv: %d\n",__func__,rv); return -2; } if(!atcmd.use_buf) { printf("%s not copy AT response to use_buf\n",__func__); return -3; } for(i=0; i < atcmd.use_len; i++) { if(',' == atcmd.use_buf[i]) { if(' ' == atcmd.use_buf[i-2]) strncpy(judge_sig,&atcmd.use_buf[i-1],1); // the signal is single-signal, eg: 6,99 else strncpy(judge_sig,&atcmd.use_buf[i-2],2); // the signal is double-signal, eg: 21,99 break; } } judge_sig_int = atoi(judge_sig); if(judge_sig_int < 7 || judge_sig_int > 31) { printf("sim card signal strength is too low or no signal and signal strength: %d\n",judge_sig_int); return -4; } gsm_ctx->sim_signal = judge_sig_int; return 0; }
函数功能:把上面的几个检查函数,封装为一个函数,在之后直接调用这个函数即可。
代码:
/*----------------------------------------------------------------------- | funtion:Check sim card and serial port whether already ready. | | argument: serial: serial port property struct. | | return: rv: success: == 0 ; error: < 0 |-----------------------------------------------------------------------*/ int check_sim_serial(serial_ctx_t *serial,gsm_ctx_t *gsm_ctx) { int rv = 0; if(!serial || !gsm_ctx) { printf("%s,Invalid input arguments\n",__func__); return -1; } switch(gsm_ctx->status) { case GSM_STATUS_INIT: if((rv = check_serial_ready(serial)) < 0) { printf("check serial port failure or [not ready] and rv: %d\n",rv); break; } //printf("serial port alread ready\n"); gsm_ctx->status ++; case GSM_STATUS_SERIAL_READY: if((rv = check_sim_exist(serial)) < 0) { printf("check sim card failure or [not exist] and rv: %d\n",rv); break; } //printf("sim card alread exist\n"); gsm_ctx->status ++; case GSM_STATUS_SIM_EXIST: if((rv = check_sim_register(serial)) < 0) { printf("check sim card failure or [not register] and rv: %d\n",rv); break; } //printf("sim card alread register\n"); gsm_ctx->status ++; case GSM_STATUS_SIM_REG: if((rv = check_sim_signal(serial,gsm_ctx)) < 0) { printf("check sim card failure or [not signal] and rv: %d\n",rv); break; } //printf("sim card have signal\n"); gsm_ctx->status ++; default: break; } return gsm_ctx->status; }
状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。详情柯参考:状态机
先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个LED等,就有 亮 和 灭两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如LED灯的状态就是两个 亮和 灭。
上述的2.5 的check_sim_serial()函数中就是使用了状态机来这个机制,利用Switch语句,来逐一判断串口就绪、SIM卡存在、注册、有信号这四个状态。因为这些状态的判断并不是在初始化的时候,只进行一次检查就可以了的,可能程序运行过程中SIM卡移动了,但是还继续进行下去就会出现错误,所以要把这些检查放到while循环里面,但是每次都要全部判断太麻烦了,而且会降低效率,这时候就可以时候状态机来判断记录状态了。
switch(gsm_ctx->status) { case GSM_STATUS_INIT: if((rv = check_serial_ready(serial)) < 0) { printf("check serial port failure or [not ready] and rv: %d\n",rv); break; } //printf("serial port alread ready\n"); gsm_ctx->status ++; case GSM_STATUS_SERIAL_READY: if((rv = check_sim_exist(serial)) < 0) { printf("check sim card failure or [not exist] and rv: %d\n",rv); break; } //printf("sim card alread exist\n"); gsm_ctx->status ++; case GSM_STATUS_SIM_EXIST: if((rv = check_sim_register(serial)) < 0) { printf("check sim card failure or [not register] and rv: %d\n",rv); break; } //printf("sim card alread register\n"); gsm_ctx->status ++; case GSM_STATUS_SIM_REG: if((rv = check_sim_signal(serial,gsm_ctx)) < 0) { printf("check sim card failure or [not signal] and rv: %d\n",rv); break; } //printf("sim card have signal\n"); gsm_ctx->status ++; default: break; } enum { GSM_STATUS_INIT = 0, //SIM card and serial port initial status. GSM_STATUS_SERIAL_READY, //Serial port ready. GSM_STATUS_SIM_EXIST, //SIM card exist. GSM_STATUS_SIM_REG, //SIM card already register. GSM_STATUS_SIM_SIG, //SIM card have signal/ GSM_STATUS_READY = GSM_STATUS_SIM_SIG, //SIM card and serial port all ready. };
本篇博客实现了AT指令的封装、check系列函数的实现以及状态机的利用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。