AT驱动框架完成硬件链路和操作系统的适配后,还需适配模组特性。本文介绍如何适配模组特性。

前提条件

已适配硬件链路和操作系统,具体信息,请参见适配硬件链路和操作系统

背景信息

模组特性主要体现在模组的AT指令上,不同模组供应商定义的指令有所不同。因此,您需根据不同模组,找出差异点,完成适配。

模组通信的AT指令可分为:

通过这三种AT指令,定义一个模组设备,具体操作,请参见 AT模组结构化定义

控制指令

控制指令以一发一收的形式,由处理器发送至模组。您需根据不同的回复报文,处理对应的逻辑。

  • 控制命令结构体 core_at_cmd_item_t

    AT驱动框架已为AT控制指令,提供处理框架。您只需根据标准结构体,定义指令的处理逻辑,即可完成指定控制指令的处理。

    typedef struct {
        /* 要发送的AT命令格式,当AT命令需要输入参数时使用 */
        char *fmt;
        /* 要发送的AT命令,当设置的AT命令不需要输入参数时使用 */
        char *cmd;
        /* AT命令的数据长度,无需赋值 */
        uint32_t cmd_len;
        /* 期望的应答数据,默认处理匹配到该字符串认为命令执行成功 */
        char *rsp;
        /* 得到应答的超时时间,达到超时时间为执行失败, 默认10000,单位为毫秒,即10秒*/
        uint32_t timeout_ms;
        /* 接受到应答数据后的用户自定义处理回调函数 */
        at_rsp_handler_t handler;
    } core_at_cmd_item_t;
  • 自定义处理函数原型at_rsp_handler_t
    说明
    • 返回.rsp定义的期望返回值,即为执行成功。
    • 返回错误信息,即为执行失败。
    • 达到超时时间,即为执行失败。
    typedef enum rsp_result_t {
        AT_RSP_SUCCESS,              /* AT命令执行成功 */
        AT_RSP_WAITING,              /* 继续等待命令回执*/
        AT_RSP_FAILED,               /* AT命令执行失败*/
    } at_rsp_result_t;
    
    /**
     * @brief 接受到应答数据后的用户处理回调函数原型定义
     *
     * @param[in] rsp       AT命令的应答数据
     *
     * @return rsp_result_t
     */
    typedef at_rsp_result_t (*at_rsp_handler_t)(char *rsp);
  • 示例:
    • 从处理器向模组发送如下指令,检查设备的信号强度:
      AT+CSQ
    • 模组接收指令后,向处理器返回如下的强度信息:
      +CSQ: 10,99 
      OK
    • 对应的控制命令结构体示例如下:
          {   /* 检查信号强度 */
              .cmd = "AT+CSQ\r\n",         /*不需要参数,定义命令*/
              .rsp = "OK",                 /*期望返回的字符串,返回OK即为成功*/
              .handler = at_csq_handler,   /*添加自定义返回报文处理,可选参数*/
              .timeout_ms= 1000,           /*自定义该命令1秒超时,可选参数*/
          },
    • 自定义处理函数示例如下:
      static at_rsp_result_t at_csq_handler(char *rsp)
      {
          at_rsp_result_t res = AT_RSP_WAITING;        /*默认返回等待*/
          int rssi = 0, ber = 0;
          char *line = NULL;
          line = strstr(rsp, "+CSQ");
          /*获取信号强度,强度小于5返回失败,否则返回成功*/
          if(line != NULL && sscanf(line, "+CSQ: %d,%d\r\n", &rssi, &ber)) {
              if(rssi < 5) {
                  res =  AT_RSP_FAILED;
              }else {
                  res =  AT_RSP_SUCCESS;
              }
          }
      
          return res;
      }

上报数据

  • 模组上报数据头的结构:
     /**
     * @brief AT主动上报接收的数据,可识别头
     */
    typedef struct {
        /* 匹配头标识符,匹配到即表示后面有数据上报 */
        char *prefix;
        /* 匹配包体,应该包含socket_id和data_len, 后面接数据 */
        char *fmt;
    } core_at_recv_data_prefix;
  • 示例:
    • 模组主动上报如下数据指令至处理器:
      +MIPRTCP: 1,4,****
      说明
      • +MIPRTCP:表示命令为数据上报命令。
      • +MIPRTCP: 1,4,****:表示fd1的连接,接收到4个字节的数据。其中,****表示上报的TCP/IP数据。
    • 示例代码:
      static core_at_recv_data_prefix at_recv = {
          .prefix = "+MIPRTCP",
          .fmt = "+MIPRTCP: %d,%d,",
      };

上报状态

模组主动上报错误信息、网络状态变化等信息至处理器。AT驱动框架提供接口,以接收状态变化等消息。根据业务需要,您可以订阅对应指令,编写回调函数的处理逻辑,以自定义处理这些信息。

  • 模组上报状态的数据头结构:
    /**
     * @brief AT主动上报命令
     */
    typedef struct {
        /* 主动上报数据匹配头 */
        char *prefix;
        /* 接受到应答数据后的用户处理回调函数 */
        at_urc_handler_t handle;
    } core_at_urc_item_t;
  • 示例:

    以下示例为模组向处理器上报TCP/IP连接断开。

    • 模组主动上报如下数据指令至处理器:
      +MIPCLOSE: 1,0
      说明
      • +MIPCLOSE:表示上报关键字。
      • 1,0:表示Socket号及状态信息。
    • 上报状态的数据头结构的示例代码:
          {
              .prefix = "+MIPCLOSED:",               /*状态上报匹配头*/
              .handle = socket_status_urc_handle,    /*匹配后的回调处理函数*/
          },
    • 定义回调函数的示例代码:
      static void socket_status_urc_handle(char *line)
      {
          int socket_id = 0, status = 0;
              if(sscanf(line, "+MIPCLOSED: %d,%d\r\n", &socket_id, &status)) {
              printf("URC id %d, status %d\r\n", socket_id, status);
              /* 通知AT驱动socket的状态变化*/
              core_at_socket_status(socket_id, CORE_AT_LINK_DISCONN);
          }
      }

AT模组结构化定义

  • 定义AT模组设备的数据结构时,需注意:

    • 单个功能可能需多个命令实现,因此,以命令表的方式,定义控制命令。
    • 控制命令可能出现错误,统一定义错误码 error_prefix
    • AT模组设备兼容SSL功能,需要定义 ssl_cmd
    /**
     * @brief AT设备结构化数据
     */
    typedef struct {
        /* 模组初始化命令列表, 有默认值 */
        core_at_cmd_item_t *module_init_cmd;
        uint32_t           module_init_cmd_size;
    
        /* 网络初始化命令列表 */
        core_at_cmd_item_t *ip_init_cmd;
        uint32_t           ip_init_cmd_size;
    
        /* 打开socket网络列表 */
        core_at_cmd_item_t *open_cmd;
        uint32_t           open_cmd_size;
    
        /* 发送数据命令列表 */
        core_at_cmd_item_t *send_cmd;
        uint32_t           send_cmd_size;
    
        /* 模组主动上报数据标识符,识别到数据上报,会进入数据接收流程 */
        core_at_recv_data_prefix *recv;
    
        /* 关闭socket命令列表 */
        core_at_cmd_item_t *close_cmd;
        uint32_t           close_cmd_size;
    
        /* 错误标识符,控制命令返回数据中,识别到错误标识符会返回执行失败 */
        char               *error_prefix;
    
        /* 订阅模组主动上报处理,可选 */
        core_at_urc_item_t *urc_register;
        uint32_t            urc_register_size;
    
        /* 开启ssl命令列表 ,可选*/
        core_at_cmd_item_t *ssl_cmd;
        uint32_t           ssl_cmd_size;
    } at_device_t;
  • 以下为AT模组驱动支持的功能及其示例代码:
    • 模组网络初始化
      /* 模块初始化命令表 */
      static core_at_cmd_item_t at_ip_init_cmd_table[] = {
          {   /* 设置身份认证参数 */
              .cmd = "AT+QICSGP=1,1,\"UNINET\",\"\",\"\",1\r\n",
              .rsp = "OK",
          },
          {   /* 去场景激活 */
              .cmd = "AT+QIDEACT=1\r\n",
              .rsp = "OK",
          },
          {   /* 场景激活 */
              .cmd = "AT+QIACT=1\r\n",
              .rsp = "OK",
          },
          {   /* 查询场景激活 */
              .cmd = "AT+QIACT?\r\n",
              .rsp = "OK",
          },
      };
    • 打开Socket连接
      /* TCP建立连接AT命令表 */
      static core_at_cmd_item_t at_connect_cmd_table[] = {
          {   /* 建立TCP连接, TODO: aiot_at_nwk_connect接口会组织此AT命令 */
              .fmt = "AT+QIOPEN=1,%d,\"TCP\",\"%s\",%d,0,1\r\n",
              .rsp = "+QIOPEN",
          },
      };
    • 发送数据
      /* 发送数据AT命令表 */
      static core_at_cmd_item_t at_send_cmd_table[] = {
          {
              .fmt = "AT+QISEND=%d,%d\r\n",
              .rsp = ">",
          },
          {
              /* 纯数据,没有格式*/
              .rsp = "SEND OK",
          },
      };
    • 定义数据接收格式
      static core_at_recv_data_prefix at_recv = {
          .head = "+QIURC: \"recv\"",
          .body = ",%d,%d",
          .tail = "\r\n",
      };
    • 关闭Socket连接
      /* TCP关闭连接AT命令表 */
      static core_at_cmd_item_t at_disconn_cmd_table[] = {
          {   /* 建立TCP连接 */
              .fmt = "AT+QICLOSE=%d\r\n",
              .rsp = "OK",
          }
      };
    • 定义模组设备
      at_device_t ec200_at_cmd = {
          .ip_init_cmd = at_ip_init_cmd_table,
          .ip_init_cmd_size = sizeof(at_ip_init_cmd_table) / sizeof(core_at_cmd_item_t),
      
          .open_cmd = at_connect_cmd_table,
          .open_cmd_size = sizeof(at_connect_cmd_table) / sizeof(core_at_cmd_item_t),
      
          .send_cmd = at_send_cmd_table,
          .send_cmd_size = sizeof(at_send_cmd_table) / sizeof(core_at_cmd_item_t),
      
          .close_cmd = at_disconn_cmd_table,
          .close_cmd_size = sizeof(at_disconn_cmd_table) / sizeof(core_at_cmd_item_t),
      
          .recv = &at_recv,
          .error_prefix = "ERROR",
      }; 

测试示例

完成AT驱动框架适配后,您可以进行测试。测试示例,请参见示例一:STM32+EC200S模组