• (五)Linux 4G模块封装发送指令函数以及检测串口和SIM卡是否就绪


    一、前言

    在前一篇:(四)Linux 4G模块实现短信PDU格式编码,实现了一条短信的PDU格式编码,这样在后面我们就可以发送中英文短信了。但是把短信打包成PDU包后,我们怎么发送出去呢?思路也不难,就是调用前面写的tty_send()函数实现数据发送,再调用tty_recv()去接收串口返回的数据,通过返回的数据就可以判断我们是否发送成功了。这样我们就可以写一个send_at_cmd()函数,来实现数据发送、接收以及判断的功能了。然后再调用send_at_cmd()函数封装Check系列函数,Check系列函数系列函数的作用是在程序启动的时候检测串口和SIM卡是否就绪,如果就绪说明一切正常,可发通信发数据。

    二、发送AT指令函数:send_at_cmd()

    2.1 设计思路

    上面也说到了,send_at_cmd()函数里面调用tty_send()和tty_recv()两个函数,而这两个函数其实是我们前面要发送AT指令而封装的,它们底层是调用了write()和read()这两个系统调用实现的。后来我们需要发送中英文短信,但是在发送短信之前,我们需要检测串口和SIM的状态。每发一条短信就需要调用tty_send()和tty_recv()两个函数,然后还要判断它们的返回值,如果我们不对这一重复性的操作进行封装,那每次发短信的时候就显得繁琐而复杂了。所以send_at_cmd()函数应运而生,实现过程如下。
    函数原型:

    int send_at_cmd(ttyusb_ctx_t *ttyusb_ctx, char *at_buf, const char *expect_buf, char *return_buf, int return_buf_size)
    • 1

    参数:

    • ttyusb_ctx_t *ttyusb_ctx 指串口属性结构体的指针,用来传fd和超时时间timeout;
    • char *at_buf 需要发送的命令;
    • const char *expect_buf 期望的在返回值中所包含的字符串;
    • char *return_buf 存放返回值的buf,如果我们需要对返回的数据进一步处理,可以存放到里面,若不需要的话,可以设置为NULL;
    • int return_buf_size 前一个参数return_buf 的大小。

    调用send_at_cmd()函数向串口发送AT指令"AT\r"的过程:
    (1) 先向串口发送数据
    tty_send(ttyusb_ctx , “AT\r”, strlen(“AT\r”))
    判断tty_send()的返回值,<0 说明发送失败。
    (2)进行延时
    因为4G模块收到指令后,需要消耗时间进行操作,所以需要给它一定的反应时间,不然发完数据立马去读串口的话就可能读不到数据。不过我们前面对tty_recv()函数进行了一个巧妙地设计就是,调用了selcet()。我们可以传参给tty_recv()进行阻塞一段时间。

    (3)接收数据
    if (tty_recv(ttyusb_ctx, temp_buf, sizeof(temp_buf)) <= 0)
    {
    printf(“Recving message failed:%s\n”,strerror(errno));
    return -3;
    }
    判断tty_recv()的返回值,<0 说明发送失败。

    (4)判断返回值temp_buf,进一步判断数据是否发送成功
    if(!strstr(temp_buf, expect_buf))
    {
    printf(“Can’t find what you expect to receive[%s]\n”, expect_buf);
    return -4;
    }

    2.2 代码实现

    int send_at_cmd(ttyusb_ctx_t *ttyusb_ctx, char *at_buf, const char *expect_buf, char *return_buf, int return_buf_size)
    {
        char   temp_buf[512] = {0};
    
        if (!at_buf || !expect_buf)
        {
            printf("Unable to send AT commond,Invalid parameter.\n");
            return -1;
        }
    
        if(tty_send(ttyusb_ctx, at_buf, strlen(at_buf)) < 0)
        {
            printf("Send AT commond failed:%s\n",strerror(errno));
            return -2;
        }
        
        usleep(10000);
        if(return_buf && return_buf_size)//需要接收返回值
        {
            if (tty_recv(ttyusb_ctx, return_buf, return_buf_size) <= 0)
            {
                printf("Recving message failed:%s\n",strerror(errno));
                return -3;
            }
    
            if (!strstr(return_buf, expect_buf))
            {
                printf("Can't find what you expect to receive[%s]\n", expect_buf);
                return -4;
            }
        }
        else//不需要接收返回值
        {
            if (tty_recv(ttyusb_ctx, temp_buf, sizeof(temp_buf)) <= 0)
            {
                printf("Recving message failed:%s\n",strerror(errno));
                return -3;
            }
    
            if (!strstr(temp_buf, expect_buf))
            {
                printf("Can't find what you expect to receive[%s]\n", expect_buf);
                return -4;
            }
            
            memset(return_buf, 0, return_buf_size);
            strncpy(return_buf,temp_buf,return_buf_size);
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    做一个判断,因为我们不知道返回值占用字节的大小,所以如果不需要返回值的话,那直接存在一个中间buf里面,只需判断是否有返回对应的关键字段即可,如果需要的话就返回到return_buf里面。

    三、Check系列函数——检测串口和SIM卡是否就绪

    3.1check_tyy_ready()

    确保串口可用
    在这里插入图片描述

    若返回OK则说明串口可用。

    int check_tyy_ready(ttyusb_ctx_t *ttyusb_ctx)
    {
        int send_rv = -1;
    
        if (!ttyusb_ctx)
        {
            printf("[%s]Invalid argument!\n", __func__);
            return -1;
        }
    
        send_rv = send_at_cmd(ttyusb_ctx, "AT\r", "OK", NULL, 0);
        if (0 != send_rv)
        {
            printf("[%s]The serial port is not ready!\n", __func__);
            return -2;
        }
    
        printf("[%s]The serial port is ok!\n", __func__);
        
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.2 check_sim_exist()

    串口可通信不代表能检测出SIM卡
    在这里插入图片描述

    若返回READY,则说明能检测出SIM卡

    int check_sim_exist(ttyusb_ctx_t *ttyusb_ctx)
    {
        int send_rv = -1;
    
        if (!ttyusb_ctx)
        {
            printf("[%s]Invalid argument!\n", __func__);
            return -1;
        }
    
        send_rv = send_at_cmd(ttyusb_ctx, "AT+CPIN?\r", "READY", NULL, 0);
        if (send_rv < 0)
        {
            printf("[%s]The SIM card is not exist\n", __func__);
            return -2;
        }
    
        printf("[%s]The SIM card is exist!\n", __func__);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.3 check_sim_login()

    检测SIM卡是否已经注册
    在这里插入图片描述

    若返回0,1 或者返回0,3 则说明SIM卡已注册

    int check_sim_login(ttyusb_ctx_t *ttyusb_ctx)
    {
        int send_rv1 = -1;
        int send_rv2 = -1;
    
        if (!ttyusb_ctx)
        {
            printf("[%s]Invalid argument!\n", __func__);
            return -1;
        }
    
        send_rv1 = send_at_cmd(ttyusb_ctx, "AT+CREG?\r", "0,1", NULL, 0);
        send_rv2 = send_at_cmd(ttyusb_ctx, "AT+CREG?\r", "0,3", NULL, 0);
        if (send_rv1 && send_rv2)
        {
            printf("[%s]The SIM card does not exist!\n", __func__);
            return -2;
        }
    
        printf("[%s]SIM card is exist!\n", __func__);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.4 check_sim_signal()

    检测SIM卡信号
    在这里插入图片描述

    命令解释:检查网络信号强度
    命令格式:AT+CSQ
    命令返回:+CSQ: ** ,##
    其中**应在 10 到 31 之间,数值越大表明信号质量越好,##为误码率,值在 0 到 99 之间。

    int check_sim_signal(ttyusb_ctx_t *ttyusb_ctx)
    {
        int     send_rv = -1;
        char    return_buf[256] = {0};
        char    separator[] = " ,";
        int     signal = -1;
        char    *token = NULL;
        int     i = 1;
    
        if (!ttyusb_ctx)
        {
            printf("[%s]Invalid argument!\n", __func__);
            return -1;
        }
    
        send_rv = send_at_cmd(ttyusb_ctx, "AT+CSQ\r", "+CSQ", return_buf, sizeof(return_buf));
        if (send_rv < 0)
        {
            printf("[%s]Not found SIM signal!\n", __func__);
            return -2;
        }
    
        printf("[%s]return_buf:%s\n", __func__, return_buf);
    
        #if 1
       
        token = strtok(return_buf, separator);
        while (token != NULL)
        {
            ++i;
            token = strtok(NULL, separator);
            printf("i: %d \ntoken:%s\n", i , token);
    
            if(2 == i)
            {
                signal = atoi(token);
                if ((signal < 8) || (signal > 31))
                {
                    printf("[%s]The signal1 value is: %d, is not normal!\n", __func__, signal);
                    return -3;
                }
                else
                {
                    printf("[%s]The signal1 value is: %d, normal!\n", __func__, signal);
                    break;
                }
            }
    
        }
        #endif
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    3.5 check_all_ready()

    对以上函数进行封装

    int check_all_ready(ttyusb_ctx_t *ttyusb_ctx)
    {
        //确保串口可用
        if (check_tyy_ready(ttyusb_ctx) < 0)
            return -1;
        log_info("[%s]tyy is ok\n", __func__);
    
        //串口可通信不代表能检测出SIM卡
        if (check_sim_exist(ttyusb_ctx) < 0)
            return -2;
        log_info("[%s]SIM is exist!\n", __func__);
    
        //检测SIM卡是否已经注册
        if (check_sim_login(ttyusb_ctx) < 0)
            return -3;
        log_info("[%s]SIM is login!\n", __func__);
    
        //检测SIM卡信号
        if (check_sim_signal(ttyusb_ctx) < 0)
            return -4;
        log_info("[%s]SIM signal is ok!\n", __func__);
    
        printf("[%s]SIM and tty are ready!\n", __func__);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    【HTML5入门指北】第二篇 网页相关的标签
    docker容器基础
    MATLAB算法实战应用案例精讲-【大模型】LLM算法(最终篇)
    windows电脑连接Android和iPhone真机调试
    labelimg标注的VOC格式标签xml文件和yolo格式标签txt文件相互转换
    什么是hive的高级分组聚合,它的用法和注意事项以及性能分析
    《Effective C++》《构造/析构/赋值运算——9、绝不在构造和析构过程中调用virtual函数》
    Spring 中容器启动分析之refresh方法执行之前
    我要给你讲的简单明了,Java就是值传递,不服来辩
    shell 监听指定日志变化进行相关业务处理
  • 原文地址:https://blog.csdn.net/weixin_45880057/article/details/126018216