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,****
:表示fd
为1
的连接,接收到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模组。