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

前言

上一遍博客已经讲述了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系列函数的实现以及状态机的利用。

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 单片机与4G模块 EC200S 的串口通信程序,一般分为初始化模块、发送数据和接收数据三个步骤。 首先,要初始化单片机的串口口和4G模块串口口。单片机和EC200S的串口口需要配置波特率、数据位、停止位和奇偶校验位等参数,以确保它们之间的通信能够正常进行。 其次,要通过单片机的串口口发送数据到EC200S模块。可以使用单片机的发送函数,在发送缓冲区中放入待发送的数据,然后通过串口口发送出去。在发送之前,还需要检查发送缓冲区是否为空,以确保没有数据丢失。 最后,要在单片机上接收来自EC200S模块的数据。单片机也需要设置接收缓冲区,并通过中断或轮询方式来读取接收缓冲区中的数据。读取完成后,可以对接收到的数据进行处理,比如打印到显示屏上或进行其他操作。 需要注意的是,在进行单片机和EC200S模块的串口通信时,还需要处理数据的传输格式。比如,可以使用ASCII码或二进制格式进行数据的传输。在发送和接收数据时,需要进行格式的转换和拆装,以确保数据能够正确传输和解析。 以上就是单片机与4G模块EC200S串口通信程序的一般步骤,具体实施时还需根据实际情况进行调整和优化。 ### 回答2: 单片机与4G模块EC200S之间的串口通信程序主要包含以下几个步骤: 1. 硬件连接:将单片机的串口TX(发送)引脚连接到EC200S的串口RX(接收)引脚,同时将单片机的串口RX(接收)引脚连接到EC200S的串口TX(发送)引脚。 2. 初始化串口:在单片机代码中,首先需要设置串口的波特率、数据位、停止位和校验位等参数,并使能串口的接收和发送功能。 3. 发送AT指令:通过串口向EC200S发送AT指令,以进行4G网络的连接、断开、发送短信等操作。可以使用串口发送函数将指令发送给EC200S。 4. 接收和解析响应:通过串口接收函数,单片机可以接收到EC200S返回的响应信息。需要对响应信息进行解析,判断操作是否成功,并根据具体情况作出相应处理。 5. 串口中断处理:可以使用串口中断,当EC200S返回数据时,单片机可以通过中断响应快速处理接收到的数据,提高响应速度。 6. 错误处理:在通信过程中可能出现各种错误,如串口通信错误、AT指令错误等。需要对可能出现的错误进行处理,例如重新发送指令检查串口连接等。 7. 其他功能扩展:根据具体需求,还可以实现其他功能,例如接收EC200S主动推送的数据、实现双向通信等。 综上所述,单片机与4G模块EC200S之间的串口通信程序主要包括串口初始化、AT指令发送与接收、响应解析、错误处理等步骤。通过这些步骤,可以实现单片机与4G模块之间的双向通信,并实现各种操作和功能。 ### 回答3: 单片机和4G模块EC200S的串口通信可以通过以下步骤实现: 1. 首先,将单片机的串口4G模块串口进行连接。确保连接正确,包括连接正确的引脚和设置正确的电平转换电路。 2. 在单片机的程序中,使用相应的串口通信库来配置和初始化单片机的串口通信功能。 3. 在单片机的程序中,设置好与4G模块通信的波特率、数据位、停止位等参数,确保与4G模块串口通信参数一致。 4. 编写单片机程序,通过串口4G模块发送指令或数据。可以使用串口发送函数将指令或数据发送到4G模块。 5. 在单片机程序中,通过串口接收函数接收4G模块返回的数据或响应。可以使用中断或轮询的方式进行接收。 6. 解析和处理4G模块返回的数据。根据4G模块的通信协议,对接收到的数据进行解析和处理,以获取所需的信息或进行相应的操作。 7. 根据需要,可以设置超时机制或错误处理,以确保通信的稳定性和可靠性。 8. 最后,测试和调试单片机与4G模块串口通信程序,确保正常通信和数据的正确传输。 总之,单片机与4G模块EC200S之间的串口通信需要进行连接、初始化、设置参数、发送指令和数据、接收返回的数据等步骤,并对返回的数据进行解析和处理,以实现双方之间的通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值