AT命令集应用完全指南¶

AT命令集应用完全指南¶

概述¶

AT命令集(AT Commands)是一种用于控制调制解调器和通信模块的文本命令协议。AT是"Attention"(注意)的缩写,最初由Hayes公司为其调制解调器开发,后来成为事实上的工业标准。在嵌入式系统中,AT命令广泛应用于GSM/GPRS模块、WiFi模块、蓝牙模块等各类通信设备的控制。

什么是AT命令¶

AT命令是一种基于ASCII文本的命令协议,具有以下特点:

简单易用:命令和响应都是可读的ASCII文本

标准化:遵循Hayes命令集标准,具有良好的兼容性

串口通信:通常通过UART串口进行通信

同步/异步:支持同步命令响应和异步事件通知

可扩展:厂商可以添加自定义命令

AT命令的基本格式¶

AT命令格式:

基本命令:

AT[]

示例:

AT - 测试命令

ATI - 查询信息

ATD13800138000 - 拨号命令

AT+CGMI - 查询厂商信息

响应格式:

示例:

OK

ERROR

+CME ERROR: 3

其中:

= 回车符 (0x0D)

= 换行符 (0x0A)

AT命令 vs 其他通信协议¶

通信协议对比:

┌──────────────┬─────────┬─────────┬─────────┬─────────┐

│ 特性 │ AT命令 │ Modbus │ MQTT │ HTTP │

├──────────────┼─────────┼─────────┼─────────┼─────────┤

│ 复杂度 │ 简单 │ 中等 │ 中等 │ 复杂 │

│ 可读性 │ 高 │ 低 │ 中 │ 高 │

│ 传输效率 │ 低 │ 高 │ 高 │ 中 │

│ 实时性 │ 好 │ 很好 │ 好 │ 一般 │

│ 标准化 │ 高 │ 高 │ 高 │ 高 │

│ 学习曲线 │ 平缓 │ 中等 │ 陡峭 │ 陡峭 │

│ 应用场景 │ 模块控制│工业控制│物联网 │Web应用 │

└──────────────┴─────────┴─────────┴─────────┴─────────┘

AT命令优势:

- 人类可读,便于调试

- 实现简单,资源占用少

- 广泛支持,兼容性好

- 适合交互式控制

AT命令劣势:

- 传输效率低

- 不适合大数据传输

- 解析相对复杂

应用场景¶

AT命令在嵌入式系统中的典型应用:

GSM/GPRS模块:SIM800、SIM900、EC20等,用于短信、语音、数据通信

WiFi模块:ESP8266、ESP32、WizFi360等,用于WiFi连接和网络通信

蓝牙模块:HC-05、HC-06、BLE模块等,用于蓝牙配对和数据传输

GPS模块:NEO-6M、NEO-M8N等,用于定位数据获取

LoRa模块:RYLR896、E32等,用于远距离通信

NB-IoT模块:BC95、BC26等,用于物联网通信

4G/5G模块:EC25、RG500等,用于高速数据通信

本文内容概览¶

本教程将涵盖以下内容:

AT命令基础:命令格式、响应类型、错误处理

UART通信实现:串口配置、数据发送接收、缓冲管理

命令解析器:命令发送、响应解析、超时处理

基本AT命令:测试命令、信息查询、配置命令

GSM模块应用:SIM卡管理、短信收发、语音通话

WiFi模块应用:网络连接、TCP/UDP通信、HTTP请求

蓝牙模块应用:配对连接、数据传输、参数配置

高级特性:URC处理、多线程安全、命令队列

错误处理:超时重试、错误恢复、日志记录

实战项目:GSM短信收发、WiFi数据上传、蓝牙透传

第一部分:AT命令基础知识¶

1.1 AT命令分类¶

按功能分类:

AT命令功能分类:

1. 基本命令(Basic Commands)

- AT : 测试命令

- ATE0/1 : 回显控制

- ATZ : 复位

- AT&F : 恢复出厂设置

- AT&W : 保存配置

2. 扩展命令(Extended Commands)

- AT+ : 扩展命令格式

- AT+? : 查询命令

- AT+=? : 测试命令

- AT+= : 设置命令

3. 专有命令(Proprietary Commands)

- 厂商自定义命令

- 模块特定功能

命令示例:

AT+CGMI - 查询厂商信息

AT+CGMM - 查询模块型号

AT+CGMR - 查询固件版本

AT+CGSN - 查询IMEI号

AT+CIMI - 查询SIM卡IMSI

按操作类型分类:

/**

* @brief AT命令操作类型

*/

typedef enum {

AT_CMD_TEST, // 测试命令:AT+CMD=?

AT_CMD_QUERY, // 查询命令:AT+CMD?

AT_CMD_SET, // 设置命令:AT+CMD=

AT_CMD_EXECUTE // 执行命令:AT+CMD

} at_cmd_type_t;

/**

* @brief AT命令示例

*/

// 测试命令 - 查询命令支持的参数范围

// AT+CGDCONT=?

// 响应:+CGDCONT: (1-16),"IP",,,(0-2),(0-4)

// 查询命令 - 查询当前设置

// AT+CGDCONT?

// 响应:+CGDCONT: 1,"IP","cmnet","0.0.0.0",0,0

// 设置命令 - 设置参数

// AT+CGDCONT=1,"IP","cmnet"

// 响应:OK

// 执行命令 - 执行操作

// AT+CGACT=1,1

// 响应:OK

1.2 AT命令响应类型¶

标准响应:

AT命令响应类型:

1. 成功响应

OK

2. 错误响应

ERROR

3. CME错误(移动设备错误)

+CME ERROR:

常见CME错误码:

0 : 手机故障

1 : 不支持

2 : 不允许

3 : 不允许的操作

4 : 不支持的操作

10 : SIM卡未插入

11 : SIM卡PIN码需要

12 : SIM卡PUK码需要

13 : SIM卡故障

14 : SIM卡忙

15 : SIM卡错误

16 : 密码错误

17 : SIM卡PIN2码需要

18 : SIM卡PUK2码需要

4. CMS错误(短信错误)

+CMS ERROR:

常见CMS错误码:

300 : ME故障

301 : SMS服务保留

302 : 不允许的操作

303 : 不支持的操作

304 : 无效的PDU模式参数

305 : 无效的文本模式参数

310 : SIM卡未插入

311 : SIM卡PIN码需要

312 : PH-SIM PIN码需要

313 : SIM卡故障

314 : SIM卡忙

315 : SIM卡错误

5. 信息响应

+:

示例:

+CGMI: SIMCOM_Ltd

+CSQ: 25,0

+CREG: 0,1

6. 数据响应

CONNECT

NO CARRIER

响应代码定义:

/**

* @brief AT命令响应结果

*/

typedef enum {

AT_RESP_OK = 0, // 成功

AT_RESP_ERROR, // 一般错误

AT_RESP_CME_ERROR, // CME错误

AT_RESP_CMS_ERROR, // CMS错误

AT_RESP_TIMEOUT, // 超时

AT_RESP_BUSY, // 忙

AT_RESP_NO_CARRIER, // 无载波

AT_RESP_NO_ANSWER, // 无应答

AT_RESP_NO_DIALTONE // 无拨号音

} at_response_t;

/**

* @brief AT响应结构

*/

typedef struct {

at_response_t result; // 响应结果

int error_code; // 错误码

char *data; // 响应数据

uint16_t data_len; // 数据长度

} at_response_info_t;

1.3 URC(非请求结果码)¶

URC(Unsolicited Result Code)是模块主动上报的信息,不是对AT命令的响应。

常见URC:

URC示例:

1. 网络注册状态

+CREG:

+CGREG:

示例:

+CREG: 1 // 已注册本地网络

2. 信号质量变化

+CSQ: ,

示例:

+CSQ: 25,0 // 信号强度25

3. 来电提示

RING

+CLIP: "",

示例:

RING

+CLIP: "13800138000",129

4. 短信接收

+CMTI: "",

示例:

+CMTI: "SM",1 // SIM卡收到短信,索引1

5. TCP/IP连接状态

,CLOSED

,CONNECT OK

示例:

0,CONNECT OK // 连接0建立成功

6. 数据接收

+RECEIVE,,

示例:

+RECEIVE,0,100 // 连接0收到100字节数据

URC处理机制:

/**

* @brief URC回调函数类型

*/

typedef void (*urc_callback_t)(const char *urc, uint16_t len);

/**

* @brief URC处理器结构

*/

typedef struct {

const char *prefix; // URC前缀

urc_callback_t callback; // 回调函数

} urc_handler_t;

/**

* @brief URC处理器表

*/

static urc_handler_t urc_handlers[] = {

{"+CREG:", handle_creg_urc},

{"+CGREG:", handle_cgreg_urc},

{"RING", handle_ring_urc},

{"+CLIP:", handle_clip_urc},

{"+CMTI:", handle_cmti_urc},

{"+RECEIVE,", handle_receive_urc},

{"CLOSED", handle_closed_urc},

{NULL, NULL} // 结束标记

};

/**

* @brief 检查并处理URC

* @param line: 接收到的行

* @return true: 是URC, false: 不是URC

*/

bool at_check_urc(const char *line)

{

for (int i = 0; urc_handlers[i].prefix != NULL; i++) {

if (strncmp(line, urc_handlers[i].prefix,

strlen(urc_handlers[i].prefix)) == 0) {

// 找到匹配的URC

if (urc_handlers[i].callback != NULL) {

urc_handlers[i].callback(line, strlen(line));

}

return true;

}

}

return false;

}

第二部分:UART通信实现¶

2.1 UART配置¶

AT命令通常通过UART串口通信,需要正确配置串口参数。

串口参数配置:

/**

* @file at_uart.c

* @brief AT命令UART通信实现

* @author 嵌入式知识平台

*/

#include

#include

#include

#include "stm32f1xx_hal.h"

/* UART句柄 */

UART_HandleTypeDef huart2;

/* 接收缓冲区 */

#define RX_BUFFER_SIZE 1024

static uint8_t rx_buffer[RX_BUFFER_SIZE];

static uint16_t rx_head = 0;

static uint16_t rx_tail = 0;

/* 单字节接收缓冲 */

static uint8_t rx_byte;

/**

* @brief UART初始化

* @param baudrate: 波特率

* @return HAL状态

*/

HAL_StatusTypeDef at_uart_init(uint32_t baudrate)

{

HAL_StatusTypeDef status;

// 配置UART参数

huart2.Instance = USART2;

huart2.Init.BaudRate = baudrate;

huart2.Init.WordLength = UART_WORDLENGTH_8B;

huart2.Init.StopBits = UART_STOPBITS_1;

huart2.Init.Parity = UART_PARITY_NONE;

huart2.Init.Mode = UART_MODE_TX_RX;

huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart2.Init.OverSampling = UART_OVERSAMPLING_16;

// 初始化UART

status = HAL_UART_Init(&huart2);

if (status != HAL_OK) {

return status;

}

// 启动接收中断

status = HAL_UART_Receive_IT(&huart2, &rx_byte, 1);

return status;

}

/**

* @brief UART接收中断回调

*/

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

if (huart->Instance == USART2) {

// 将接收到的字节放入环形缓冲区

uint16_t next_head = (rx_head + 1) % RX_BUFFER_SIZE;

if (next_head != rx_tail) {

rx_buffer[rx_head] = rx_byte;

rx_head = next_head;

}

// 继续接收下一个字节

HAL_UART_Receive_IT(&huart2, &rx_byte, 1);

}

}

/**

* @brief 发送数据

* @param data: 数据指针

* @param len: 数据长度

* @return 发送的字节数

*/

int at_uart_send(const uint8_t *data, uint16_t len)

{

HAL_StatusTypeDef status;

status = HAL_UART_Transmit(&huart2, (uint8_t*)data, len, 1000);

return (status == HAL_OK) ? len : -1;

}

/**

* @brief 接收数据

* @param data: 数据缓冲区

* @param max_len: 最大长度

* @return 接收的字节数

*/

int at_uart_receive(uint8_t *data, uint16_t max_len)

{

uint16_t count = 0;

while (rx_tail != rx_head && count < max_len) {

data[count++] = rx_buffer[rx_tail];

rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;

}

return count;

}

/**

* @brief 获取可用数据数量

* @return 可用字节数

*/

uint16_t at_uart_available(void)

{

if (rx_head >= rx_tail) {

return rx_head - rx_tail;

} else {

return RX_BUFFER_SIZE - rx_tail + rx_head;

}

}

/**

* @brief 清空接收缓冲区

*/

void at_uart_flush(void)

{

rx_head = 0;

rx_tail = 0;

}

2.2 行缓冲实现¶

AT命令是基于行的协议,需要实现行缓冲功能。

/**

* @file at_line_buffer.c

* @brief AT命令行缓冲实现

*/

#include

#include

#include

/* 行缓冲区 */

#define LINE_BUFFER_SIZE 256

static char line_buffer[LINE_BUFFER_SIZE];

static uint16_t line_pos = 0;

/**

* @brief 读取一行数据

* @param line: 行缓冲区

* @param max_len: 最大长度

* @param timeout_ms: 超时时间(毫秒)

* @return 行长度,-1表示超时

*/

int at_read_line(char *line, uint16_t max_len, uint32_t timeout_ms)

{

uint32_t start_time = HAL_GetTick();

uint8_t ch;

while ((HAL_GetTick() - start_time) < timeout_ms) {

// 检查是否有数据

if (at_uart_available() > 0) {

at_uart_receive(&ch, 1);

// 处理回车换行

if (ch == '\r' || ch == '\n') {

if (line_pos > 0) {

// 行结束

line_buffer[line_pos] = '\0';

// 复制到输出缓冲区

strncpy(line, line_buffer, max_len - 1);

line[max_len - 1] = '\0';

uint16_t len = line_pos;

line_pos = 0;

return len;

}

// 忽略空行

} else {

// 添加字符到行缓冲

if (line_pos < LINE_BUFFER_SIZE - 1) {

line_buffer[line_pos++] = ch;

}

}

}

}

return -1; // 超时

}

/**

* @brief 清空行缓冲

*/

void at_clear_line_buffer(void)

{

line_pos = 0;

memset(line_buffer, 0, LINE_BUFFER_SIZE);

}

第三部分:AT命令解析器实现¶

3.1 命令发送器¶

/**

* @file at_parser.c

* @brief AT命令解析器实现

*/

#include

#include

#include

#include

#include

/* AT命令超时时间 */

#define AT_DEFAULT_TIMEOUT 1000 // 默认超时1秒

#define AT_LONG_TIMEOUT 30000 // 长超时30秒

/**

* @brief 发送AT命令

* @param cmd: 命令字符串

* @return 发送的字节数

*/

int at_send_command(const char *cmd)

{

int len = strlen(cmd);

// 发送命令

at_uart_send((const uint8_t*)cmd, len);

// 发送回车换行

at_uart_send((const uint8_t*)"\r\n", 2);

return len + 2;

}

/**

* @brief 发送格式化AT命令

* @param fmt: 格式字符串

* @param ...: 可变参数

* @return 发送的字节数

*/

int at_send_command_fmt(const char *fmt, ...)

{

char cmd_buffer[256];

va_list args;

va_start(args, fmt);

vsnprintf(cmd_buffer, sizeof(cmd_buffer), fmt, args);

va_end(args);

return at_send_command(cmd_buffer);

}

/**

* @brief 等待响应

* @param expected: 期望的响应字符串(可以为NULL)

* @param timeout_ms: 超时时间

* @return AT响应结果

*/

at_response_t at_wait_response(const char *expected, uint32_t timeout_ms)

{

char line[256];

uint32_t start_time = HAL_GetTick();

while ((HAL_GetTick() - start_time) < timeout_ms) {

// 读取一行

int len = at_read_line(line, sizeof(line), 100);

if (len > 0) {

// 检查是否是URC

if (at_check_urc(line)) {

continue; // 跳过URC,继续等待响应

}

// 检查响应

if (strcmp(line, "OK") == 0) {

return AT_RESP_OK;

}

else if (strcmp(line, "ERROR") == 0) {

return AT_RESP_ERROR;

}

else if (strncmp(line, "+CME ERROR:", 11) == 0) {

return AT_RESP_CME_ERROR;

}

else if (strncmp(line, "+CMS ERROR:", 11) == 0) {

return AT_RESP_CMS_ERROR;

}

else if (strcmp(line, "BUSY") == 0) {

return AT_RESP_BUSY;

}

else if (strcmp(line, "NO CARRIER") == 0) {

return AT_RESP_NO_CARRIER;

}

else if (expected != NULL && strstr(line, expected) != NULL) {

// 找到期望的响应

return AT_RESP_OK;

}

}

}

return AT_RESP_TIMEOUT;

}

/**

* @brief 执行AT命令并等待响应

* @param cmd: 命令字符串

* @param expected: 期望的响应(可以为NULL)

* @param timeout_ms: 超时时间

* @return AT响应结果

*/

at_response_t at_execute_command(const char *cmd, const char *expected, uint32_t timeout_ms)

{

// 清空接收缓冲

at_uart_flush();

at_clear_line_buffer();

// 发送命令

at_send_command(cmd);

// 等待响应

return at_wait_response(expected, timeout_ms);

}

3.2 响应解析器¶

/**

* @brief 解析响应数据

* @param response: 响应字符串

* @param prefix: 响应前缀

* @param data: 输出数据缓冲区

* @param max_len: 最大长度

* @return 解析的数据长度,-1表示失败

*/

int at_parse_response(const char *response, const char *prefix, char *data, uint16_t max_len)

{

// 查找前缀

const char *start = strstr(response, prefix);

if (start == NULL) {

return -1;

}

// 跳过前缀

start += strlen(prefix);

// 跳过空格

while (*start == ' ') {

start++;

}

// 复制数据

uint16_t len = 0;

while (*start != '\r' && *start != '\n' && *start != '\0' && len < max_len - 1) {

data[len++] = *start++;

}

data[len] = '\0';

return len;

}

/**

* @brief 解析整数参数

* @param response: 响应字符串

* @param prefix: 响应前缀

* @param value: 输出整数值

* @return 0: 成功, -1: 失败

*/

int at_parse_int(const char *response, const char *prefix, int *value)

{

char data[32];

if (at_parse_response(response, prefix, data, sizeof(data)) < 0) {

return -1;

}

*value = atoi(data);

return 0;

}

/**

* @brief 解析多个参数

* @param response: 响应字符串

* @param prefix: 响应前缀

* @param params: 参数数组

* @param max_params: 最大参数数量

* @return 解析的参数数量

*/

int at_parse_params(const char *response, const char *prefix, char params[][64], int max_params)

{

char data[256];

if (at_parse_response(response, prefix, data, sizeof(data)) < 0) {

return -1;

}

int count = 0;

char *token = strtok(data, ",");

while (token != NULL && count < max_params) {

// 去除引号

if (token[0] == '"') {

token++;

char *end = strchr(token, '"');

if (end != NULL) {

*end = '\0';

}

}

strncpy(params[count], token, 63);

params[count][63] = '\0';

count++;

token = strtok(NULL, ",");

}

return count;

}

/**

* @brief 读取响应数据

* @param prefix: 响应前缀

* @param data: 数据缓冲区

* @param max_len: 最大长度

* @param timeout_ms: 超时时间

* @return 读取的数据长度,-1表示失败

*/

int at_read_response_data(const char *prefix, char *data, uint16_t max_len, uint32_t timeout_ms)

{

char line[256];

uint32_t start_time = HAL_GetTick();

while ((HAL_GetTick() - start_time) < timeout_ms) {

int len = at_read_line(line, sizeof(line), 100);

if (len > 0) {

// 检查是否包含前缀

if (strncmp(line, prefix, strlen(prefix)) == 0) {

// 解析数据

return at_parse_response(line, prefix, data, max_len);

}

}

}

return -1; // 超时

}

3.3 命令重试机制¶

/**

* @brief 带重试的命令执行

* @param cmd: 命令字符串

* @param expected: 期望的响应

* @param timeout_ms: 超时时间

* @param retry_count: 重试次数

* @return AT响应结果

*/

at_response_t at_execute_with_retry(const char *cmd, const char *expected,

uint32_t timeout_ms, int retry_count)

{

at_response_t result;

for (int i = 0; i <= retry_count; i++) {

result = at_execute_command(cmd, expected, timeout_ms);

if (result == AT_RESP_OK) {

return result; // 成功

}

// 失败,延迟后重试

if (i < retry_count) {

HAL_Delay(500);

}

}

return result; // 所有重试都失败

}

/**

* @brief 测试AT命令

* @return true: 模块响应正常, false: 模块无响应

*/

bool at_test(void)

{

at_response_t result = at_execute_with_retry("AT", NULL, 1000, 3);

return (result == AT_RESP_OK);

}

第四部分:基本AT命令应用¶

4.1 模块信息查询¶

/**

* @file at_basic.c

* @brief 基本AT命令应用

*/

/**

* @brief 查询厂商信息

* @param manufacturer: 厂商名称缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_get_manufacturer(char *manufacturer, uint16_t max_len)

{

at_response_t result;

// 发送命令

at_send_command("AT+CGMI");

// 读取响应

if (at_read_response_data("", manufacturer, max_len, 1000) < 0) {

return -1;

}

// 等待OK

result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询模块型号

* @param model: 型号缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_get_model(char *model, uint16_t max_len)

{

at_send_command("AT+CGMM");

if (at_read_response_data("", model, max_len, 1000) < 0) {

return -1;

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询固件版本

* @param version: 版本缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_get_firmware_version(char *version, uint16_t max_len)

{

at_send_command("AT+CGMR");

if (at_read_response_data("", version, max_len, 1000) < 0) {

return -1;

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询IMEI号

* @param imei: IMEI缓冲区(至少16字节)

* @return 0: 成功, -1: 失败

*/

int at_get_imei(char *imei)

{

at_send_command("AT+CGSN");

if (at_read_response_data("", imei, 16, 1000) < 0) {

return -1;

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

4.2 模块配置命令¶

/**

* @brief 设置回显模式

* @param enable: true启用回显,false禁用回显

* @return 0: 成功, -1: 失败

*/

int at_set_echo(bool enable)

{

const char *cmd = enable ? "ATE1" : "ATE0";

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 复位模块

* @return 0: 成功, -1: 失败

*/

int at_reset(void)

{

at_response_t result = at_execute_command("ATZ", NULL, 3000);

HAL_Delay(1000); // 等待模块重启

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 恢复出厂设置

* @return 0: 成功, -1: 失败

*/

int at_factory_reset(void)

{

at_response_t result = at_execute_command("AT&F", NULL, 3000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 保存配置

* @return 0: 成功, -1: 失败

*/

int at_save_config(void)

{

at_response_t result = at_execute_command("AT&W", NULL, 3000);

return (result == AT_RESP_OK) ? 0 : -1;

}

第五部分:GSM模块应用¶

5.1 SIM卡管理¶

/**

* @file at_gsm.c

* @brief GSM模块AT命令应用

*/

/**

* @brief 检查SIM卡状态

* @return 0: SIM卡就绪, -1: SIM卡未就绪

*/

int at_check_sim_status(void)

{

at_response_t result;

char response[64];

at_send_command("AT+CPIN?");

// 读取响应

if (at_read_response_data("+CPIN:", response, sizeof(response), 1000) < 0) {

return -1;

}

// 等待OK

result = at_wait_response(NULL, 1000);

if (result != AT_RESP_OK) {

return -1;

}

// 检查状态

if (strstr(response, "READY") != NULL) {

return 0; // SIM卡就绪

}

return -1; // SIM卡未就绪或需要PIN码

}

/**

* @brief 输入PIN码

* @param pin: PIN码字符串

* @return 0: 成功, -1: 失败

*/

int at_enter_pin(const char *pin)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+CPIN=\"%s\"", pin);

at_response_t result = at_execute_command(cmd, NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询IMSI号

* @param imsi: IMSI缓冲区(至少16字节)

* @return 0: 成功, -1: 失败

*/

int at_get_imsi(char *imsi)

{

at_send_command("AT+CIMI");

if (at_read_response_data("", imsi, 16, 1000) < 0) {

return -1;

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询SIM卡运营商

* @param operator: 运营商名称缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_get_operator(char *operator, uint16_t max_len)

{

at_send_command("AT+COPS?");

char response[128];

if (at_read_response_data("+COPS:", response, sizeof(response), 1000) < 0) {

return -1;

}

// 解析运营商名称

char params[5][64];

int count = at_parse_params(response, "+COPS:", params, 5);

if (count >= 3) {

strncpy(operator, params[2], max_len - 1);

operator[max_len - 1] = '\0';

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

5.2 网络注册¶

/**

* @brief 网络注册状态

*/

typedef enum {

NET_NOT_REGISTERED = 0, // 未注册

NET_REGISTERED_HOME, // 已注册本地网络

NET_SEARCHING, // 正在搜索

NET_REGISTRATION_DENIED, // 注册被拒绝

NET_UNKNOWN, // 未知

NET_REGISTERED_ROAMING // 已注册漫游网络

} network_status_t;

/**

* @brief 查询网络注册状态

* @param status: 网络状态

* @return 0: 成功, -1: 失败

*/

int at_get_network_status(network_status_t *status)

{

at_send_command("AT+CREG?");

char response[64];

if (at_read_response_data("+CREG:", response, sizeof(response), 1000) < 0) {

return -1;

}

// 解析状态

char params[3][64];

int count = at_parse_params(response, "+CREG:", params, 3);

if (count >= 2) {

*status = (network_status_t)atoi(params[1]);

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 等待网络注册

* @param timeout_ms: 超时时间

* @return 0: 成功, -1: 超时

*/

int at_wait_network_registration(uint32_t timeout_ms)

{

uint32_t start_time = HAL_GetTick();

network_status_t status;

while ((HAL_GetTick() - start_time) < timeout_ms) {

if (at_get_network_status(&status) == 0) {

if (status == NET_REGISTERED_HOME || status == NET_REGISTERED_ROAMING) {

return 0; // 已注册

}

}

HAL_Delay(1000); // 每秒检查一次

}

return -1; // 超时

}

/**

* @brief 查询信号强度

* @param rssi: 信号强度(0-31,99表示未知)

* @param ber: 误码率(0-7,99表示未知)

* @return 0: 成功, -1: 失败

*/

int at_get_signal_quality(int *rssi, int *ber)

{

at_send_command("AT+CSQ");

char response[64];

if (at_read_response_data("+CSQ:", response, sizeof(response), 1000) < 0) {

return -1;

}

// 解析参数

char params[2][64];

int count = at_parse_params(response, "+CSQ:", params, 2);

if (count >= 2) {

*rssi = atoi(params[0]);

*ber = atoi(params[1]);

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 将RSSI转换为dBm

* @param rssi: RSSI值(0-31)

* @return dBm值

*/

int at_rssi_to_dbm(int rssi)

{

if (rssi == 0) {

return -113; // 或更低

} else if (rssi == 1) {

return -111;

} else if (rssi >= 2 && rssi <= 30) {

return -109 + (rssi - 2) * 2;

} else if (rssi == 31) {

return -51; // 或更高

} else {

return -999; // 未知

}

}

5.3 短信收发¶

/**

* @brief 设置短信格式

* @param text_mode: true为文本模式,false为PDU模式

* @return 0: 成功, -1: 失败

*/

int at_set_sms_format(bool text_mode)

{

const char *cmd = text_mode ? "AT+CMGF=1" : "AT+CMGF=0";

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 发送短信(文本模式)

* @param phone_number: 电话号码

* @param message: 短信内容

* @return 0: 成功, -1: 失败

*/

int at_send_sms(const char *phone_number, const char *message)

{

char cmd[64];

at_response_t result;

// 设置为文本模式

if (at_set_sms_format(true) != 0) {

return -1;

}

// 发送AT+CMGS命令

snprintf(cmd, sizeof(cmd), "AT+CMGS=\"%s\"", phone_number);

at_send_command(cmd);

// 等待'>'提示符

HAL_Delay(500);

// 发送短信内容

at_uart_send((const uint8_t*)message, strlen(message));

// 发送Ctrl+Z(0x1A)结束

uint8_t ctrl_z = 0x1A;

at_uart_send(&ctrl_z, 1);

// 等待响应(发送短信可能需要较长时间)

result = at_wait_response("+CMGS:", 30000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 读取短信

* @param index: 短信索引

* @param phone_number: 电话号码缓冲区

* @param message: 短信内容缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_read_sms(int index, char *phone_number, char *message, uint16_t max_len)

{

char cmd[32];

char line[256];

// 设置为文本模式

if (at_set_sms_format(true) != 0) {

return -1;

}

// 发送读取命令

snprintf(cmd, sizeof(cmd), "AT+CMGR=%d", index);

at_send_command(cmd);

// 读取响应头

if (at_read_line(line, sizeof(line), 2000) < 0) {

return -1;

}

// 解析电话号码

// +CMGR: "REC UNREAD","+8613800138000",,"21/01/01,12:00:00+32"

char params[5][64];

int count = at_parse_params(line, "+CMGR:", params, 5);

if (count >= 2) {

strncpy(phone_number, params[1], 31);

phone_number[31] = '\0';

}

// 读取短信内容

if (at_read_line(message, max_len, 2000) < 0) {

return -1;

}

// 等待OK

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 删除短信

* @param index: 短信索引

* @return 0: 成功, -1: 失败

*/

int at_delete_sms(int index)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+CMGD=%d", index);

at_response_t result = at_execute_command(cmd, NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 列出所有短信

* @param status: 短信状态("ALL", "REC UNREAD", "REC READ", "STO UNSENT", "STO SENT")

* @return 0: 成功, -1: 失败

*/

int at_list_sms(const char *status)

{

char cmd[64];

// 设置为文本模式

if (at_set_sms_format(true) != 0) {

return -1;

}

// 发送列表命令

snprintf(cmd, sizeof(cmd), "AT+CMGL=\"%s\"", status);

at_send_command(cmd);

// 读取所有短信

char line[256];

while (1) {

int len = at_read_line(line, sizeof(line), 2000);

if (len < 0) {

break; // 超时

}

if (strcmp(line, "OK") == 0) {

return 0; // 成功

}

// 处理短信列表项

if (strncmp(line, "+CMGL:", 6) == 0) {

// 这是短信头

// 下一行是短信内容

at_read_line(line, sizeof(line), 2000);

// 这里可以处理短信内容

}

}

return -1;

}

5.4 语音通话¶

/**

* @brief 拨打电话

* @param phone_number: 电话号码

* @return 0: 成功, -1: 失败

*/

int at_dial(const char *phone_number)

{

char cmd[64];

snprintf(cmd, sizeof(cmd), "ATD%s;", phone_number);

at_response_t result = at_execute_command(cmd, NULL, 30000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 接听电话

* @return 0: 成功, -1: 失败

*/

int at_answer_call(void)

{

at_response_t result = at_execute_command("ATA", NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 挂断电话

* @return 0: 成功, -1: 失败

*/

int at_hangup(void)

{

at_response_t result = at_execute_command("ATH", NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 启用来电显示

* @param enable: true启用,false禁用

* @return 0: 成功, -1: 失败

*/

int at_enable_caller_id(bool enable)

{

const char *cmd = enable ? "AT+CLIP=1" : "AT+CLIP=0";

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

第六部分:WiFi模块应用(ESP8266)¶

6.1 WiFi基本配置¶

/**

* @file at_wifi.c

* @brief WiFi模块(ESP8266)AT命令应用

*/

/**

* @brief 设置WiFi模式

* @param mode: 1=Station, 2=AP, 3=Station+AP

* @return 0: 成功, -1: 失败

*/

int at_wifi_set_mode(int mode)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+CWMODE=%d", mode);

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 连接WiFi网络

* @param ssid: WiFi名称

* @param password: WiFi密码

* @return 0: 成功, -1: 失败

*/

int at_wifi_connect(const char *ssid, const char *password)

{

char cmd[128];

snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\"", ssid, password);

// WiFi连接可能需要较长时间

at_response_t result = at_execute_command(cmd, NULL, 20000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 断开WiFi连接

* @return 0: 成功, -1: 失败

*/

int at_wifi_disconnect(void)

{

at_response_t result = at_execute_command("AT+CWQAP", NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询WiFi连接状态

* @param ssid: SSID缓冲区

* @param max_len: 最大长度

* @return 0: 已连接, -1: 未连接

*/

int at_wifi_get_connection(char *ssid, uint16_t max_len)

{

at_send_command("AT+CWJAP?");

char response[128];

if (at_read_response_data("+CWJAP:", response, sizeof(response), 1000) < 0) {

return -1;

}

// 解析SSID

char params[5][64];

int count = at_parse_params(response, "+CWJAP:", params, 5);

if (count >= 1) {

strncpy(ssid, params[0], max_len - 1);

ssid[max_len - 1] = '\0';

}

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 获取IP地址

* @param ip: IP地址缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_wifi_get_ip(char *ip, uint16_t max_len)

{

at_send_command("AT+CIFSR");

char line[128];

// 读取多行响应

while (1) {

int len = at_read_line(line, sizeof(line), 2000);

if (len < 0) {

return -1; // 超时

}

// 查找STAIP

if (strncmp(line, "+CIFSR:STAIP,", 13) == 0) {

// 解析IP地址

char *start = strchr(line, '"');

if (start != NULL) {

start++;

char *end = strchr(start, '"');

if (end != NULL) {

int ip_len = end - start;

if (ip_len < max_len) {

strncpy(ip, start, ip_len);

ip[ip_len] = '\0';

return 0;

}

}

}

}

if (strcmp(line, "OK") == 0) {

break;

}

}

return -1;

}

/**

* @brief 扫描WiFi网络

* @return 0: 成功, -1: 失败

*/

int at_wifi_scan(void)

{

at_send_command("AT+CWLAP");

char line[256];

// 读取扫描结果

while (1) {

int len = at_read_line(line, sizeof(line), 10000);

if (len < 0) {

return -1; // 超时

}

if (strncmp(line, "+CWLAP:", 7) == 0) {

// 解析WiFi信息

// +CWLAP:(3,"SSID",-65,"aa:bb:cc:dd:ee:ff",1)

// 这里可以处理扫描结果

}

if (strcmp(line, "OK") == 0) {

return 0; // 成功

}

}

}

6.2 TCP/IP通信¶

/**

* @brief 设置多连接模式

* @param enable: true启用多连接,false单连接

* @return 0: 成功, -1: 失败

*/

int at_wifi_set_mux(bool enable)

{

const char *cmd = enable ? "AT+CIPMUX=1" : "AT+CIPMUX=0";

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 建立TCP连接

* @param link_id: 连接ID(0-4,多连接模式)

* @param type: 连接类型("TCP"或"UDP")

* @param remote_ip: 远程IP地址

* @param remote_port: 远程端口

* @return 0: 成功, -1: 失败

*/

int at_wifi_connect_tcp(int link_id, const char *type, const char *remote_ip, int remote_port)

{

char cmd[128];

snprintf(cmd, sizeof(cmd), "AT+CIPSTART=%d,\"%s\",\"%s\",%d",

link_id, type, remote_ip, remote_port);

at_response_t result = at_execute_command(cmd, "CONNECT", 10000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 发送数据

* @param link_id: 连接ID

* @param data: 数据指针

* @param len: 数据长度

* @return 0: 成功, -1: 失败

*/

int at_wifi_send_data(int link_id, const uint8_t *data, uint16_t len)

{

char cmd[64];

at_response_t result;

// 发送AT+CIPSEND命令

snprintf(cmd, sizeof(cmd), "AT+CIPSEND=%d,%d", link_id, len);

at_send_command(cmd);

// 等待'>'提示符

HAL_Delay(100);

// 发送数据

at_uart_send(data, len);

// 等待SEND OK

result = at_wait_response("SEND OK", 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 接收数据

* @param link_id: 连接ID

* @param data: 数据缓冲区

* @param max_len: 最大长度

* @param timeout_ms: 超时时间

* @return 接收的字节数,-1表示失败

*/

int at_wifi_receive_data(int link_id, uint8_t *data, uint16_t max_len, uint32_t timeout_ms)

{

char line[256];

uint32_t start_time = HAL_GetTick();

while ((HAL_GetTick() - start_time) < timeout_ms) {

int len = at_read_line(line, sizeof(line), 100);

if (len > 0) {

// 检查是否是数据接收通知

// +IPD,,:

if (strncmp(line, "+IPD,", 5) == 0) {

// 解析参数

int recv_link_id, recv_len;

sscanf(line, "+IPD,%d,%d:", &recv_link_id, &recv_len);

if (recv_link_id == link_id && recv_len <= max_len) {

// 查找数据起始位置

char *data_start = strchr(line, ':');

if (data_start != NULL) {

data_start++;

// 复制数据

int copy_len = strlen(data_start);

if (copy_len > recv_len) {

copy_len = recv_len;

}

memcpy(data, data_start, copy_len);

return copy_len;

}

}

}

}

}

return -1; // 超时

}

/**

* @brief 关闭连接

* @param link_id: 连接ID

* @return 0: 成功, -1: 失败

*/

int at_wifi_close_connection(int link_id)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+CIPCLOSE=%d", link_id);

at_response_t result = at_execute_command(cmd, NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

6.3 HTTP请求¶

/**

* @brief HTTP GET请求

* @param url: URL地址

* @param response: 响应缓冲区

* @param max_len: 最大长度

* @return 接收的字节数,-1表示失败

*/

int at_wifi_http_get(const char *url, char *response, uint16_t max_len)

{

char cmd[256];

at_response_t result;

// 设置HTTP参数

snprintf(cmd, sizeof(cmd), "AT+HTTPCLIENT=2,0,\"%s\",,,2", url);

at_send_command(cmd);

// 读取响应

char line[256];

int total_len = 0;

while (1) {

int len = at_read_line(line, sizeof(line), 30000);

if (len < 0) {

return -1; // 超时

}

// 检查是否是数据行

if (strncmp(line, "+HTTPCLIENT:", 12) == 0) {

// 解析数据长度

int data_len;

sscanf(line, "+HTTPCLIENT:%d", &data_len);

// 读取数据

if (data_len > 0 && total_len + data_len < max_len) {

at_uart_receive((uint8_t*)&response[total_len], data_len);

total_len += data_len;

}

}

if (strcmp(line, "OK") == 0) {

response[total_len] = '\0';

return total_len;

}

if (strcmp(line, "ERROR") == 0) {

return -1;

}

}

}

/**

* @brief HTTP POST请求

* @param url: URL地址

* @param data: POST数据

* @param response: 响应缓冲区

* @param max_len: 最大长度

* @return 接收的字节数,-1表示失败

*/

int at_wifi_http_post(const char *url, const char *data, char *response, uint16_t max_len)

{

char cmd[512];

// 设置HTTP参数

snprintf(cmd, sizeof(cmd), "AT+HTTPCLIENT=3,0,\"%s\",,\"%s\",2", url, data);

at_send_command(cmd);

// 读取响应(类似GET)

char line[256];

int total_len = 0;

while (1) {

int len = at_read_line(line, sizeof(line), 30000);

if (len < 0) {

return -1;

}

if (strncmp(line, "+HTTPCLIENT:", 12) == 0) {

int data_len;

sscanf(line, "+HTTPCLIENT:%d", &data_len);

if (data_len > 0 && total_len + data_len < max_len) {

at_uart_receive((uint8_t*)&response[total_len], data_len);

total_len += data_len;

}

}

if (strcmp(line, "OK") == 0) {

response[total_len] = '\0';

return total_len;

}

if (strcmp(line, "ERROR") == 0) {

return -1;

}

}

}

第七部分:蓝牙模块应用(HC-05/HC-06)¶

7.1 蓝牙基本配置¶

/**

* @file at_bluetooth.c

* @brief 蓝牙模块(HC-05)AT命令应用

*/

/**

* @brief 进入AT命令模式

* @note HC-05需要在上电前将KEY引脚拉高

* @return 0: 成功, -1: 失败

*/

int at_bt_enter_at_mode(void)

{

// HC-05在AT模式下波特率为38400

at_uart_init(38400);

// 测试AT命令

at_response_t result = at_execute_with_retry("AT", NULL, 1000, 3);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询蓝牙名称

* @param name: 名称缓冲区

* @param max_len: 最大长度

* @return 0: 成功, -1: 失败

*/

int at_bt_get_name(char *name, uint16_t max_len)

{

at_send_command("AT+NAME?");

char response[64];

if (at_read_response_data("+NAME:", response, sizeof(response), 1000) < 0) {

return -1;

}

strncpy(name, response, max_len - 1);

name[max_len - 1] = '\0';

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 设置蓝牙名称

* @param name: 新名称

* @return 0: 成功, -1: 失败

*/

int at_bt_set_name(const char *name)

{

char cmd[64];

snprintf(cmd, sizeof(cmd), "AT+NAME=%s", name);

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询蓝牙地址

* @param addr: 地址缓冲区(格式:XXXX:XX:XXXXXX)

* @return 0: 成功, -1: 失败

*/

int at_bt_get_address(char *addr)

{

at_send_command("AT+ADDR?");

char response[32];

if (at_read_response_data("+ADDR:", response, sizeof(response), 1000) < 0) {

return -1;

}

strcpy(addr, response);

at_response_t result = at_wait_response(NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 设置蓝牙PIN码

* @param pin: PIN码(4位数字)

* @return 0: 成功, -1: 失败

*/

int at_bt_set_pin(const char *pin)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+PSWD=%s", pin);

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 设置蓝牙角色

* @param role: 0=从机,1=主机,2=从机循环

* @return 0: 成功, -1: 失败

*/

int at_bt_set_role(int role)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+ROLE=%d", role);

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 设置串口参数

* @param baudrate: 波特率

* @param stop_bits: 停止位(0=1位,1=2位)

* @param parity: 校验位(0=无,1=奇,2=偶)

* @return 0: 成功, -1: 失败

*/

int at_bt_set_uart(uint32_t baudrate, int stop_bits, int parity)

{

char cmd[64];

snprintf(cmd, sizeof(cmd), "AT+UART=%lu,%d,%d", baudrate, stop_bits, parity);

at_response_t result = at_execute_command(cmd, NULL, 1000);

return (result == AT_RESP_OK) ? 0 : -1;

}

7.2 蓝牙配对和连接¶

/**

* @brief 搜索蓝牙设备

* @param max_devices: 最大设备数量

* @param timeout_ms: 搜索超时时间

* @return 找到的设备数量,-1表示失败

*/

int at_bt_scan_devices(int max_devices, uint32_t timeout_ms)

{

char cmd[32];

snprintf(cmd, sizeof(cmd), "AT+INQM=1,5,%d", max_devices);

// 设置搜索模式

if (at_execute_command(cmd, NULL, 1000) != AT_RESP_OK) {

return -1;

}

// 开始搜索

at_send_command("AT+INQ");

char line[128];

int device_count = 0;

uint32_t start_time = HAL_GetTick();

while ((HAL_GetTick() - start_time) < timeout_ms) {

int len = at_read_line(line, sizeof(line), 1000);

if (len > 0) {

// 解析设备信息

// +INQ:1234:56:789ABC,1F00,7FFF

if (strncmp(line, "+INQ:", 5) == 0) {

device_count++;

// 这里可以保存设备信息

}

if (strcmp(line, "OK") == 0) {

return device_count;

}

}

}

// 取消搜索

at_send_command("AT+INQC");

return device_count;

}

/**

* @brief 连接蓝牙设备

* @param address: 设备地址(格式:XXXX,XX,XXXXXX)

* @return 0: 成功, -1: 失败

*/

int at_bt_connect(const char *address)

{

char cmd[64];

snprintf(cmd, sizeof(cmd), "AT+LINK=%s", address);

// 连接可能需要较长时间

at_response_t result = at_execute_command(cmd, NULL, 30000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 断开蓝牙连接

* @return 0: 成功, -1: 失败

*/

int at_bt_disconnect(void)

{

at_response_t result = at_execute_command("AT+DISC", NULL, 5000);

return (result == AT_RESP_OK) ? 0 : -1;

}

/**

* @brief 查询连接状态

* @return 0: 未连接, 1: 已连接, -1: 查询失败

*/

int at_bt_get_connection_status(void)

{

at_send_command("AT+STATE?");

char response[32];

if (at_read_response_data("+STATE:", response, sizeof(response), 1000) < 0) {

return -1;

}

at_wait_response(NULL, 1000);

// 解析状态

if (strstr(response, "CONNECTED") != NULL) {

return 1; // 已连接

} else {

return 0; // 未连接

}

}

第八部分:高级特性¶

8.1 命令队列¶

/**

* @file at_queue.c

* @brief AT命令队列实现

*/

#include

#include

#include

/* 命令队列大小 */

#define CMD_QUEUE_SIZE 10

/**

* @brief 命令队列项

*/

typedef struct {

char cmd[128]; // 命令字符串

char expected[64]; // 期望响应

uint32_t timeout; // 超时时间

at_response_t result; // 执行结果

bool completed; // 完成标志

} cmd_queue_item_t;

/**

* @brief 命令队列

*/

static cmd_queue_item_t cmd_queue[CMD_QUEUE_SIZE];

static int queue_head = 0;

static int queue_tail = 0;

static int queue_count = 0;

/**

* @brief 添加命令到队列

* @param cmd: 命令字符串

* @param expected: 期望响应

* @param timeout: 超时时间

* @return 0: 成功, -1: 队列已满

*/

int at_queue_add(const char *cmd, const char *expected, uint32_t timeout)

{

if (queue_count >= CMD_QUEUE_SIZE) {

return -1; // 队列已满

}

// 添加到队列尾部

strncpy(cmd_queue[queue_tail].cmd, cmd, sizeof(cmd_queue[queue_tail].cmd) - 1);

if (expected != NULL) {

strncpy(cmd_queue[queue_tail].expected, expected, sizeof(cmd_queue[queue_tail].expected) - 1);

} else {

cmd_queue[queue_tail].expected[0] = '\0';

}

cmd_queue[queue_tail].timeout = timeout;

cmd_queue[queue_tail].completed = false;

queue_tail = (queue_tail + 1) % CMD_QUEUE_SIZE;

queue_count++;

return 0;

}

/**

* @brief 处理命令队列

* @return 处理的命令数量

*/

int at_queue_process(void)

{

int processed = 0;

while (queue_count > 0) {

// 获取队列头部命令

cmd_queue_item_t *item = &cmd_queue[queue_head];

// 执行命令

const char *expected = (item->expected[0] != '\0') ? item->expected : NULL;

item->result = at_execute_command(item->cmd, expected, item->timeout);

item->completed = true;

// 移动队列头部

queue_head = (queue_head + 1) % CMD_QUEUE_SIZE;

queue_count--;

processed++;

}

return processed;

}

/**

* @brief 清空命令队列

*/

void at_queue_clear(void)

{

queue_head = 0;

queue_tail = 0;

queue_count = 0;

}

8.2 多线程安全¶

/**

* @file at_thread_safe.c

* @brief AT命令多线程安全实现

*/

#include "cmsis_os.h"

/* 互斥锁 */

static osMutexId_t at_mutex = NULL;

/**

* @brief 初始化AT命令互斥锁

* @return 0: 成功, -1: 失败

*/

int at_mutex_init(void)

{

at_mutex = osMutexNew(NULL);

return (at_mutex != NULL) ? 0 : -1;

}

/**

* @brief 线程安全的命令执行

* @param cmd: 命令字符串

* @param expected: 期望响应

* @param timeout_ms: 超时时间

* @return AT响应结果

*/

at_response_t at_execute_command_safe(const char *cmd, const char *expected, uint32_t timeout_ms)

{

at_response_t result;

// 获取互斥锁

if (osMutexAcquire(at_mutex, osWaitForever) != osOK) {

return AT_RESP_ERROR;

}

// 执行命令

result = at_execute_command(cmd, expected, timeout_ms);

// 释放互斥锁

osMutexRelease(at_mutex);

return result;

}

8.3 日志记录¶

/**

* @file at_log.c

* @brief AT命令日志记录

*/

#include

#include

/* 日志级别 */

typedef enum {

LOG_LEVEL_DEBUG,

LOG_LEVEL_INFO,

LOG_LEVEL_WARN,

LOG_LEVEL_ERROR

} log_level_t;

static log_level_t current_log_level = LOG_LEVEL_INFO;

/**

* @brief 设置日志级别

* @param level: 日志级别

*/

void at_log_set_level(log_level_t level)

{

current_log_level = level;

}

/**

* @brief 记录日志

* @param level: 日志级别

* @param fmt: 格式字符串

* @param ...: 可变参数

*/

void at_log(log_level_t level, const char *fmt, ...)

{

if (level < current_log_level) {

return;

}

const char *level_str[] = {"DEBUG", "INFO", "WARN", "ERROR"};

printf("[AT][%s] ", level_str[level]);

va_list args;

va_start(args, fmt);

vprintf(fmt, args);

va_end(args);

printf("\r\n");

}

/**

* @brief 记录命令

* @param cmd: 命令字符串

*/

void at_log_command(const char *cmd)

{

at_log(LOG_LEVEL_DEBUG, "TX: %s", cmd);

}

/**

* @brief 记录响应

* @param response: 响应字符串

*/

void at_log_response(const char *response)

{

at_log(LOG_LEVEL_DEBUG, "RX: %s", response);

}

第九部分:错误处理与故障排除¶

9.1 常见错误处理¶

/**

* @file at_error.c

* @brief AT命令错误处理

*/

/**

* @brief 错误码定义

*/

typedef enum {

AT_ERR_OK = 0, // 成功

AT_ERR_TIMEOUT, // 超时

AT_ERR_INVALID_PARAM, // 无效参数

AT_ERR_NOT_SUPPORTED, // 不支持

AT_ERR_SIM_NOT_READY, // SIM卡未就绪

AT_ERR_NETWORK_ERROR, // 网络错误

AT_ERR_CONNECTION_FAILED, // 连接失败

AT_ERR_SEND_FAILED, // 发送失败

AT_ERR_RECEIVE_FAILED, // 接收失败

AT_ERR_UNKNOWN // 未知错误

} at_error_t;

/**

* @brief 错误信息表

*/

static const char *error_messages[] = {

"Success",

"Timeout",

"Invalid parameter",

"Not supported",

"SIM card not ready",

"Network error",

"Connection failed",

"Send failed",

"Receive failed",

"Unknown error"

};

/**

* @brief 获取错误信息

* @param error: 错误码

* @return 错误信息字符串

*/

const char* at_get_error_message(at_error_t error)

{

if (error < sizeof(error_messages) / sizeof(error_messages[0])) {

return error_messages[error];

}

return "Invalid error code";

}

/**

* @brief 错误恢复策略

* @param error: 错误码

* @return 0: 成功恢复, -1: 无法恢复

*/

int at_error_recovery(at_error_t error)

{

switch (error) {

case AT_ERR_TIMEOUT:

// 超时:清空缓冲区,重试

at_uart_flush();

at_clear_line_buffer();

HAL_Delay(100);

return 0;

case AT_ERR_SIM_NOT_READY:

// SIM卡未就绪:等待SIM卡初始化

HAL_Delay(2000);

return at_check_sim_status();

case AT_ERR_NETWORK_ERROR:

// 网络错误:重新注册网络

return at_wait_network_registration(30000);

case AT_ERR_CONNECTION_FAILED:

// 连接失败:延迟后重试

HAL_Delay(5000);

return 0;

default:

return -1; // 无法恢复

}

}

9.2 超时处理¶

/**

* @brief 带超时重试的命令执行

* @param cmd: 命令字符串

* @param expected: 期望响应

* @param timeout_ms: 单次超时时间

* @param retry_count: 重试次数

* @param retry_delay_ms: 重试延迟

* @return AT响应结果

*/

at_response_t at_execute_with_timeout_retry(const char *cmd, const char *expected,

uint32_t timeout_ms, int retry_count,

uint32_t retry_delay_ms)

{

at_response_t result;

for (int i = 0; i <= retry_count; i++) {

// 执行命令

result = at_execute_command(cmd, expected, timeout_ms);

if (result == AT_RESP_OK) {

return result; // 成功

}

// 记录错误

at_log(LOG_LEVEL_WARN, "Command failed (attempt %d/%d): %s",

i + 1, retry_count + 1, cmd);

// 超时恢复

if (result == AT_RESP_TIMEOUT) {

at_error_recovery(AT_ERR_TIMEOUT);

}

// 延迟后重试

if (i < retry_count) {

HAL_Delay(retry_delay_ms);

}

}

at_log(LOG_LEVEL_ERROR, "Command failed after %d attempts: %s",

retry_count + 1, cmd);

return result;

}

9.3 故障诊断¶

/**

* @brief 诊断AT命令通信

* @return 0: 正常, -1: 异常

*/

int at_diagnose(void)

{

at_log(LOG_LEVEL_INFO, "Starting AT command diagnostics...");

// 1. 测试基本通信

at_log(LOG_LEVEL_INFO, "Testing basic communication...");

if (!at_test()) {

at_log(LOG_LEVEL_ERROR, "Basic communication failed");

return -1;

}

at_log(LOG_LEVEL_INFO, "Basic communication OK");

// 2. 查询模块信息

at_log(LOG_LEVEL_INFO, "Querying module information...");

char info[64];

if (at_get_manufacturer(info, sizeof(info)) == 0) {

at_log(LOG_LEVEL_INFO, "Manufacturer: %s", info);

}

if (at_get_model(info, sizeof(info)) == 0) {

at_log(LOG_LEVEL_INFO, "Model: %s", info);

}

if (at_get_firmware_version(info, sizeof(info)) == 0) {

at_log(LOG_LEVEL_INFO, "Firmware: %s", info);

}

// 3. 检查SIM卡(如果是GSM模块)

at_log(LOG_LEVEL_INFO, "Checking SIM card...");

if (at_check_sim_status() == 0) {

at_log(LOG_LEVEL_INFO, "SIM card ready");

if (at_get_imsi(info) == 0) {

at_log(LOG_LEVEL_INFO, "IMSI: %s", info);

}

} else {

at_log(LOG_LEVEL_WARN, "SIM card not ready");

}

// 4. 检查网络注册

at_log(LOG_LEVEL_INFO, "Checking network registration...");

network_status_t status;

if (at_get_network_status(&status) == 0) {

at_log(LOG_LEVEL_INFO, "Network status: %d", status);

if (status == NET_REGISTERED_HOME || status == NET_REGISTERED_ROAMING) {

// 查询信号强度

int rssi, ber;

if (at_get_signal_quality(&rssi, &ber) == 0) {

at_log(LOG_LEVEL_INFO, "Signal: RSSI=%d (%d dBm), BER=%d",

rssi, at_rssi_to_dbm(rssi), ber);

}

}

}

at_log(LOG_LEVEL_INFO, "Diagnostics completed");

return 0;

}

9.4 常见问题解决¶

AT命令常见问题及解决方案:

1. 模块无响应

原因:

- 串口参数不匹配

- 模块未上电或复位

- 接线错误

解决:

- 检查波特率、数据位、停止位、校验位

- 确认模块电源正常

- 检查TX/RX是否交叉连接

- 尝试不同波特率(9600, 115200等)

2. 响应乱码

原因:

- 波特率不匹配

- 电平不匹配(TTL vs RS232)

- 干扰

解决:

- 调整波特率

- 使用电平转换芯片

- 检查接地,缩短连线

3. 命令超时

原因:

- 超时时间设置过短

- 模块处理慢

- 网络延迟

解决:

- 增加超时时间

- 添加重试机制

- 检查网络状态

4. SIM卡错误

原因:

- SIM卡未插入

- SIM卡接触不良

- 需要PIN码

解决:

- 检查SIM卡安装

- 清洁SIM卡触点

- 输入正确PIN码

5. 网络注册失败

原因:

- 信号弱

- SIM卡欠费

- 运营商限制

解决:

- 改善天线位置

- 检查SIM卡余额

- 联系运营商

6. 连接失败

原因:

- 网络未注册

- APN设置错误

- 服务器地址错误

解决:

- 等待网络注册

- 设置正确APN

- 检查服务器地址和端口

7. 数据发送失败

原因:

- 连接已断开

- 缓冲区满

- 数据格式错误

解决:

- 检查连接状态

- 等待缓冲区空闲

- 检查数据格式

8. URC丢失

原因:

- 接收缓冲区溢出

- 处理不及时

解决:

- 增大接收缓冲区

- 使用中断接收

- 及时处理URC

第十部分:最佳实践¶

10.1 设计原则¶

AT命令应用设计原则:

1. 模块化设计

- 将AT命令功能封装成独立模块

- 提供清晰的API接口

- 便于移植和维护

2. 错误处理

- 所有函数都应返回错误码

- 实现完善的错误恢复机制

- 记录详细的错误日志

3. 超时机制

- 所有命令都应设置超时

- 根据命令类型设置合理的超时时间

- 实现超时重试机制

4. 状态管理

- 维护模块状态机

- 在执行命令前检查状态

- 状态转换要有明确的条件

5. 资源管理

- 合理分配缓冲区大小

- 及时释放资源

- 避免内存泄漏

6. 线程安全

- 多线程环境下使用互斥锁

- 避免竞态条件

- 保证命令的原子性

7. 日志记录

- 记录所有命令和响应

- 记录错误和异常

- 便于问题定位和调试

10.2 性能优化¶

/**

* @brief 批量命令执行

* @param commands: 命令数组

* @param count: 命令数量

* @return 成功执行的命令数量

*/

int at_execute_batch(const char *commands[], int count)

{

int success_count = 0;

for (int i = 0; i < count; i++) {

if (at_execute_command(commands[i], NULL, 1000) == AT_RESP_OK) {

success_count++;

} else {

at_log(LOG_LEVEL_WARN, "Batch command %d failed: %s", i, commands[i]);

}

}

return success_count;

}

/**

* @brief 使用DMA提高传输效率

*/

void at_enable_dma(void)

{

// 配置UART DMA接收

HAL_UART_Receive_DMA(&huart2, rx_buffer, RX_BUFFER_SIZE);

}

/**

* @brief 缓存常用响应

*/

typedef struct {

char key[32];

char value[128];

uint32_t timestamp;

uint32_t ttl; // 生存时间(毫秒)

} cache_entry_t;

#define CACHE_SIZE 10

static cache_entry_t response_cache[CACHE_SIZE];

/**

* @brief 从缓存获取响应

* @param key: 缓存键

* @param value: 输出值

* @return 0: 命中, -1: 未命中

*/

int at_cache_get(const char *key, char *value)

{

uint32_t current_time = HAL_GetTick();

for (int i = 0; i < CACHE_SIZE; i++) {

if (strcmp(response_cache[i].key, key) == 0) {

// 检查是否过期

if ((current_time - response_cache[i].timestamp) < response_cache[i].ttl) {

strcpy(value, response_cache[i].value);

return 0; // 命中

}

}

}

return -1; // 未命中

}

/**

* @brief 添加响应到缓存

* @param key: 缓存键

* @param value: 缓存值

* @param ttl: 生存时间(毫秒)

*/

void at_cache_set(const char *key, const char *value, uint32_t ttl)

{

static int cache_index = 0;

strncpy(response_cache[cache_index].key, key, sizeof(response_cache[cache_index].key) - 1);

strncpy(response_cache[cache_index].value, value, sizeof(response_cache[cache_index].value) - 1);

response_cache[cache_index].timestamp = HAL_GetTick();

response_cache[cache_index].ttl = ttl;

cache_index = (cache_index + 1) % CACHE_SIZE;

}

10.3 安全考虑¶

/**

* @brief 输入验证

* @param input: 输入字符串

* @return true: 有效, false: 无效

*/

bool at_validate_input(const char *input)

{

// 检查空指针

if (input == NULL) {

return false;

}

// 检查长度

size_t len = strlen(input);

if (len == 0 || len > 256) {

return false;

}

// 检查非法字符

for (size_t i = 0; i < len; i++) {

if (input[i] < 0x20 || input[i] > 0x7E) {

// 非可打印ASCII字符

if (input[i] != '\r' && input[i] != '\n') {

return false;

}

}

}

return true;

}

/**

* @brief 防止命令注入

* @param user_input: 用户输入

* @param safe_output: 安全输出

* @param max_len: 最大长度

*/

void at_sanitize_input(const char *user_input, char *safe_output, size_t max_len)

{

size_t j = 0;

for (size_t i = 0; user_input[i] != '\0' && j < max_len - 1; i++) {

// 只允许字母、数字和部分符号

if (isalnum(user_input[i]) || user_input[i] == '.' ||

user_input[i] == '-' || user_input[i] == '_') {

safe_output[j++] = user_input[i];

}

}

safe_output[j] = '\0';

}

第十一部分:实战项目¶

11.1 项目一:GSM短信收发系统¶

/**

* @file project_sms.c

* @brief GSM短信收发系统实现

*/

/**

* @brief SMS系统状态

*/

typedef enum {

SMS_STATE_INIT,

SMS_STATE_READY,

SMS_STATE_ERROR

} sms_state_t;

static sms_state_t sms_state = SMS_STATE_INIT;

/**

* @brief 初始化SMS系统

* @return 0: 成功, -1: 失败

*/

int sms_system_init(void)

{

at_log(LOG_LEVEL_INFO, "Initializing SMS system...");

// 1. 初始化UART

if (at_uart_init(115200) != HAL_OK) {

at_log(LOG_LEVEL_ERROR, "UART initialization failed");

return -1;

}

// 2. 测试AT命令

if (!at_test()) {

at_log(LOG_LEVEL_ERROR, "AT command test failed");

return -1;

}

// 3. 关闭回显

at_set_echo(false);

// 4. 检查SIM卡

if (at_check_sim_status() != 0) {

at_log(LOG_LEVEL_ERROR, "SIM card not ready");

return -1;

}

// 5. 等待网络注册

at_log(LOG_LEVEL_INFO, "Waiting for network registration...");

if (at_wait_network_registration(30000) != 0) {

at_log(LOG_LEVEL_ERROR, "Network registration failed");

return -1;

}

// 6. 设置短信格式为文本模式

if (at_set_sms_format(true) != 0) {

at_log(LOG_LEVEL_ERROR, "Failed to set SMS format");

return -1;

}

// 7. 启用短信接收通知

if (at_execute_command("AT+CNMI=2,1,0,0,0", NULL, 1000) != AT_RESP_OK) {

at_log(LOG_LEVEL_ERROR, "Failed to enable SMS notification");

return -1;

}

sms_state = SMS_STATE_READY;

at_log(LOG_LEVEL_INFO, "SMS system initialized successfully");

return 0;

}

/**

* @brief 发送短信

* @param phone: 电话号码

* @param message: 短信内容

* @return 0: 成功, -1: 失败

*/

int sms_send(const char *phone, const char *message)

{

if (sms_state != SMS_STATE_READY) {

at_log(LOG_LEVEL_ERROR, "SMS system not ready");

return -1;

}

at_log(LOG_LEVEL_INFO, "Sending SMS to %s: %s", phone, message);

if (at_send_sms(phone, message) == 0) {

at_log(LOG_LEVEL_INFO, "SMS sent successfully");

return 0;

} else {

at_log(LOG_LEVEL_ERROR, "Failed to send SMS");

return -1;

}

}

/**

* @brief 处理接收到的短信

* @param urc: URC字符串

* @param len: 长度

*/

void handle_sms_received(const char *urc, uint16_t len)

{

// 解析短信索引

// +CMTI: "SM",1

int index;

if (sscanf(urc, "+CMTI: \"SM\",%d", &index) == 1) {

at_log(LOG_LEVEL_INFO, "New SMS received at index %d", index);

// 读取短信

char phone[32];

char message[256];

if (at_read_sms(index, phone, message, sizeof(message)) == 0) {

at_log(LOG_LEVEL_INFO, "SMS from %s: %s", phone, message);

// 这里可以处理短信内容

// 例如:解析命令、自动回复等

// 删除已读短信

at_delete_sms(index);

}

}

}

/**

* @brief SMS系统主循环

*/

void sms_system_loop(void)

{

if (sms_state != SMS_STATE_READY) {

return;

}

// 处理URC

char line[256];

if (at_read_line(line, sizeof(line), 100) > 0) {

if (at_check_urc(line)) {

// URC已被处理

}

}

}

11.2 项目二:WiFi数据上传系统¶

/**

* @file project_wifi_upload.c

* @brief WiFi数据上传系统实现

*/

/**

* @brief WiFi上传系统配置

*/

typedef struct {

char ssid[64];

char password[64];

char server_ip[32];

int server_port;

int upload_interval; // 上传间隔(秒)

} wifi_upload_config_t;

static wifi_upload_config_t wifi_config = {

.ssid = "MyWiFi",

.password = "12345678",

.server_ip = "192.168.1.100",

.server_port = 8080,

.upload_interval = 60

};

/**

* @brief 初始化WiFi上传系统

* @return 0: 成功, -1: 失败

*/

int wifi_upload_init(void)

{

at_log(LOG_LEVEL_INFO, "Initializing WiFi upload system...");

// 1. 初始化UART

if (at_uart_init(115200) != HAL_OK) {

return -1;

}

// 2. 测试AT命令

if (!at_test()) {

return -1;

}

// 3. 设置WiFi模式为Station

if (at_wifi_set_mode(1) != 0) {

return -1;

}

// 4. 连接WiFi

at_log(LOG_LEVEL_INFO, "Connecting to WiFi: %s", wifi_config.ssid);

if (at_wifi_connect(wifi_config.ssid, wifi_config.password) != 0) {

at_log(LOG_LEVEL_ERROR, "WiFi connection failed");

return -1;

}

// 5. 获取IP地址

char ip[32];

if (at_wifi_get_ip(ip, sizeof(ip)) == 0) {

at_log(LOG_LEVEL_INFO, "IP address: %s", ip);

}

// 6. 设置多连接模式

at_wifi_set_mux(true);

at_log(LOG_LEVEL_INFO, "WiFi upload system initialized");

return 0;

}

/**

* @brief 上传传感器数据

* @param temperature: 温度

* @param humidity: 湿度

* @return 0: 成功, -1: 失败

*/

int wifi_upload_sensor_data(float temperature, float humidity)

{

char json_data[256];

char response[512];

int link_id = 0;

// 1. 构建JSON数据

snprintf(json_data, sizeof(json_data),

"{\"temperature\":%.2f,\"humidity\":%.2f,\"timestamp\":%lu}",

temperature, humidity, HAL_GetTick());

at_log(LOG_LEVEL_INFO, "Uploading data: %s", json_data);

// 2. 建立TCP连接

if (at_wifi_connect_tcp(link_id, "TCP", wifi_config.server_ip, wifi_config.server_port) != 0) {

at_log(LOG_LEVEL_ERROR, "Failed to connect to server");

return -1;

}

// 3. 构建HTTP POST请求

char http_request[512];

snprintf(http_request, sizeof(http_request),

"POST /api/data HTTP/1.1\r\n"

"Host: %s\r\n"

"Content-Type: application/json\r\n"

"Content-Length: %d\r\n"

"\r\n"

"%s",

wifi_config.server_ip, strlen(json_data), json_data);

// 4. 发送HTTP请求

if (at_wifi_send_data(link_id, (uint8_t*)http_request, strlen(http_request)) != 0) {

at_log(LOG_LEVEL_ERROR, "Failed to send data");

at_wifi_close_connection(link_id);

return -1;

}

// 5. 接收响应

int recv_len = at_wifi_receive_data(link_id, (uint8_t*)response, sizeof(response) - 1, 5000);

if (recv_len > 0) {

response[recv_len] = '\0';

at_log(LOG_LEVEL_INFO, "Server response: %s", response);

}

// 6. 关闭连接

at_wifi_close_connection(link_id);

at_log(LOG_LEVEL_INFO, "Data uploaded successfully");

return 0;

}

/**

* @brief WiFi上传系统主循环

*/

void wifi_upload_loop(void)

{

static uint32_t last_upload_time = 0;

uint32_t current_time = HAL_GetTick();

// 定时上传数据

if ((current_time - last_upload_time) >= (wifi_config.upload_interval * 1000)) {

// 读取传感器数据(示例)

float temperature = 25.5; // 实际应从传感器读取

float humidity = 60.0;

// 上传数据

wifi_upload_sensor_data(temperature, humidity);

last_upload_time = current_time;

}

}

11.3 项目三:蓝牙透传系统¶

/**

* @file project_bt_transparent.c

* @brief 蓝牙透传系统实现

*/

/**

* @brief 蓝牙透传状态

*/

typedef enum {

BT_TRANS_DISCONNECTED,

BT_TRANS_CONNECTED,

BT_TRANS_DATA_MODE

} bt_trans_state_t;

static bt_trans_state_t bt_state = BT_TRANS_DISCONNECTED;

/**

* @brief 初始化蓝牙透传系统

* @return 0: 成功, -1: 失败

*/

int bt_transparent_init(void)

{

at_log(LOG_LEVEL_INFO, "Initializing Bluetooth transparent transmission...");

// 1. 进入AT命令模式

if (at_bt_enter_at_mode() != 0) {

return -1;

}

// 2. 设置蓝牙名称

at_bt_set_name("BT_TRANS");

// 3. 设置PIN码

at_bt_set_pin("1234");

// 4. 设置为从机模式

at_bt_set_role(0);

// 5. 设置串口参数(透传模式)

at_bt_set_uart(9600, 0, 0);

// 6. 复位模块,进入透传模式

at_reset();

// 重新初始化UART为透传波特率

at_uart_init(9600);

at_log(LOG_LEVEL_INFO, "Bluetooth transparent transmission initialized");

return 0;

}

/**

* @brief 蓝牙透传发送数据

* @param data: 数据指针

* @param len: 数据长度

* @return 发送的字节数

*/

int bt_transparent_send(const uint8_t *data, uint16_t len)

{

if (bt_state != BT_TRANS_DATA_MODE) {

return -1;

}

return at_uart_send(data, len);

}

/**

* @brief 蓝牙透传接收数据

* @param data: 数据缓冲区

* @param max_len: 最大长度

* @return 接收的字节数

*/

int bt_transparent_receive(uint8_t *data, uint16_t max_len)

{

if (bt_state != BT_TRANS_DATA_MODE) {

return -1;

}

return at_uart_receive(data, max_len);

}

/**

* @brief 蓝牙透传主循环

*/

void bt_transparent_loop(void)

{

uint8_t buffer[256];

// 检查是否有数据接收

int len = bt_transparent_receive(buffer, sizeof(buffer));

if (len > 0) {

// 处理接收到的数据

at_log(LOG_LEVEL_DEBUG, "Received %d bytes", len);

// 回显数据(示例)

bt_transparent_send(buffer, len);

}

}

第十二部分:工具与资源¶

12.1 开发工具¶

AT命令开发常用工具:

1. 串口调试助手

- SSCOM(Windows)

- CoolTerm(跨平台)

- PuTTY(跨平台)

- minicom(Linux)

功能:

- 发送AT命令

- 查看响应

- 十六进制显示

- 日志记录

2. AT命令测试工具

- Tera Term(Windows)

- RealTerm(Windows)

- Serial Port Monitor(Windows)

功能:

- 脚本自动化

- 时间戳

- 数据分析

3. 网络调试工具

- Packet Sender(跨平台)

- SocketTest(Windows)

- netcat(Linux)

功能:

- TCP/UDP测试

- 数据收发

- 服务器模拟

4. HTTP测试工具

- Postman(跨平台)

- curl(命令行)

- HTTPie(命令行)

功能:

- HTTP请求测试

- API调试

- 响应分析

12.2 参考资料¶

AT命令参考资料:

1. 标准文档

- ITU-T V.250: AT命令标准

- 3GPP TS 27.007: GSM AT命令集

- 3GPP TS 27.005: SMS AT命令集

2. 厂商文档

- SIMCOM: SIM800/SIM900系列AT命令手册

- Espressif: ESP8266/ESP32 AT命令手册

- u-blox: SARA/TOBY系列AT命令手册

- Quectel: EC20/EC25系列AT命令手册

3. 在线资源

- AT Commands Reference: https://www.developershome.com/sms/

- GSM Modem AT Commands: https://www.smssolutions.net/

- ESP8266 AT Instruction Set: https://www.espressif.com/

4. 开源项目

- TinyGSM: https://github.com/vshymanskyy/TinyGSM

- ESP-AT: https://github.com/espressif/esp-at

- AT Command Parser: https://github.com/ARMmbed/ATParser

总结¶

关键要点¶

AT命令基础

AT命令是基于ASCII文本的命令协议

命令格式:AT+[=]

响应类型:OK、ERROR、+CME ERROR、+CMS ERROR

URC是模块主动上报的信息

UART通信

正确配置串口参数(波特率、数据位、停止位、校验位)

使用中断接收提高效率

实现环形缓冲区管理数据

实现行缓冲处理AT命令

命令解析

实现命令发送器

实现响应解析器

处理超时和重试

区分命令响应和URC

模块应用

GSM模块:SIM卡管理、网络注册、短信收发、语音通话

WiFi模块:网络连接、TCP/IP通信、HTTP请求

蓝牙模块:配对连接、数据传输、参数配置

高级特性

命令队列提高效率

多线程安全保护

日志记录便于调试

缓存优化性能

错误处理

完善的错误码定义

错误恢复策略

超时重试机制

故障诊断工具

最佳实践

模块化设计

输入验证

安全考虑

性能优化

学习路径¶

入门阶段

学习AT命令基础知识

使用串口工具手动发送AT命令

理解命令格式和响应类型

掌握基本的模块配置

进阶阶段

实现UART通信和命令解析

开发GSM/WiFi/蓝牙应用

处理URC和异步事件

实现错误处理和重试机制

高级阶段

优化性能和资源使用

实现多线程安全

开发复杂的应用系统

集成到实际产品中

下一步学习¶

TCP/IP协议栈 - 深入学习网络协议

MQTT协议 - 学习物联网通信协议

Modbus协议 - 学习工业通信协议

UART驱动开发 - 深入学习UART底层

参考资料¶

ITU-T Recommendation V.250: Serial asynchronous automatic dialling and control

3GPP TS 27.007: AT command set for User Equipment (UE)

3GPP TS 27.005: Use of Data Terminal Equipment - Data Circuit terminating Equipment (DTE-DCE) interface for Short Message Service (SMS)

SIMCOM SIM800 Series AT Command Manual

Espressif ESP8266 AT Instruction Set

HC-05 Bluetooth Module AT Command Reference

Quectel EC20 AT Commands Manual

u-blox AT Commands Manual

相关主题¶

TCP/IP协议栈基础

MQTT协议应用

CoAP协议详解

HTTP/HTTPS通信

Modbus协议实现

UART硬件接口

UART驱动开发

本文档持续更新中,最后更新时间:2026-04-06

[an error occurred while processing the directive]
Copyright © 2088 时代中心网 - 经典游戏活动回顾 All Rights Reserved.
友情链接