【回顾】Linux下基于4G模块(EC200U-CN)的串口编程——AT指令的封装以及AT指令检查SIM

本文介绍了AT指令的封装,重点讲解了check系列函数,包括串口准备、SIM卡存在、注册和信号检查,以及状态机在检查流程中的应用,以简化代码并提高程序的健壮性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上一遍博客已经讲述了PDU编码的实现,接下来就要说如何实现发送信息了,其实发送短信的过程就是在串口下发送AT指令的过程,为了提高代码的可移植性以及看起来更简洁,所以我们先来学习一下AT指令的封装吧!

1. AT指令的封装

1.1 atcmd.h

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_  ----- */

1.2 send_recv_atcmd()

该函数把前面说到的实现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;
}

2. check系列函数

在我们使用串口发送AT指令前必须检查串口是否已经准备好了,以及SIM卡是否存在4G模块的卡槽里,是否已经注册、是否有信号等,接下来逐一进行封装。

2.1 check_serial_ready()

函数功能:检查串口是否已经就绪。
原理:在串口下我们发送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;
}

2.2 check_sim_exist()

函数功能:检查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;
}

2.3 check_sim_register()

函数功能:判断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;
}

2.4 check_sim_signal()

函数功能:检查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;
}

2.5 check_sim_serial()

函数功能:把上面的几个检查函数,封装为一个函数,在之后直接调用这个函数即可。
代码:

/*-----------------------------------------------------------------------
|  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;
}

2.5.1 状态机的使用

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。详情柯参考:状态机

先来解释什么是“状态”( 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系列函数的实现以及状态机的利用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值