目录前言一、为什么选择ESP32作为WiFi模块二、ESP32 AT指令集基础2.1 什么是AT指令2.2 常用AT指令分类三、STM32驱动层设计3.1 硬件连接3.2 数据结构设计3.3 串口初始化3.4 核心通信函数3.5 发送命令的统一接口四、功能模块实现4.1 模块初始化与Boot等待4.2 WiFi连接4.3 WiFi状态查询4.4 NTP时间获取4.5 HTTP请求五、使用示例六、总结前言今天我们将深入讲解ESP32 AT指令集并实现STM32与ESP32的通信驱动层。这是整个项目中最关键的部分之一——让STM32通过AT指令控制WiFi模块联网。一、为什么选择ESP32作为WiFi模块在嵌入式开发中WiFi联网方案主要有以下几种方案优点缺点自带WiFi的MCU如ESP32集成度高、成本低开发复杂、资源受限外置WiFi模块ESP8266/ESP32主控选择灵活、开发简单增加BOM成本4G模块覆盖广、无需WiFi成本高、功耗大对于STM32开发者来说使用ESP32作为AT指令驱动的WiFi模块是最佳选择开发简单、稳定性好、成本可控。二、ESP32 AT指令集基础2.1 什么是AT指令AT指令Attention Commands是一种串行通信命令集最早用于控制调制解调器。ESP32固件通过解析串口接收的AT指令执行相应操作并返回结果。发送AT返回OK2.2 常用AT指令分类类别指令功能基础AT测试通信基础ATRESTORE恢复出厂设置WiFiATCWMODE设置WiFi模式WiFiATCWJAP连接APWiFiATCWSTATE查询WiFi状态网络ATCIPSNTPCFG配置NTP时间网络ATHTTPCLIENTHTTP请求更详细更全面的内容参考以下网站ESP-AT用户指南https://docs.espressif.com/projects/esp-at/zh_CN/latest/esp32/AT_Command_Set/HTTP_AT_Commands.html三、STM32驱动层设计3.1 硬件连接STM32ESP32说明PA2 (USART2_TX)RX数据发送PA3 (USART2_RX)TX数据接收GNDGND共地3.3V3.3V电源3.2 数据结构设计// AT指令响应类型 typedef enum { AT_ACK_NONE, // 无匹配 AT_ACK_OK, // 成功 AT_ACK_ERROR, // 失败 AT_ACK_BUSY, // 忙 AT_ACK_READY, // 就绪 } at_ack_t; // 响应匹配表 static const at_ack_match_t at_ack_matches[] { {AT_ACK_OK, OK\r\n}, {AT_ACK_ERROR, ERROR\r\n}, {AT_ACK_BUSY, busy p...\r\n}, {AT_ACK_READY, ready\r\n}, }; // WiFi信息结构体 typedef struct { bool connected; // 连接状态 char ssid[64]; // WiFi名称 char bssid[18]; // MAC地址 int channel; // 信道 int rssi; // 信号强度 } esp_wifi_info_t; // 时间日期结构体 typedef struct { uint16_t year; // 年份 uint8_t month; // 月份 uint8_t day; // 日期 uint8_t weekday; // 星期 uint8_t hour; // 小时 uint8_t minute; // 分钟 uint8_t second; // 秒钟 } esp_date_time_t;3.3 串口初始化static void esp_at_usart_init(void) { // 1. 配置USART2参数 USART_InitTypeDef USART_InitStructure; USART_StructInit(USART_InitStructure); USART_InitStructure.USART_BaudRate 115200; // ESP8266/32默认波特率 USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; // 2. 配置GPIO复用功能 GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); // 3. 初始化GPIO GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_High_Speed; GPIO_InitStructure.GPIO_Pin GPIO_Pin_2 | GPIO_Pin_3; GPIO_Init(GPIOA, GPIO_InitStructure); // 4. 使能USART2 USART_Init(USART2, USART_InitStructure); USART_Cmd(USART2, ENABLE); }3.4 核心通信函数发送AT指令static void esp_at_usart_write(const char *data) { // 发送数据 while (data *data) { while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, *data); } // 发送换行符 \r\n while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, \r); while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, \n); }等待并解析响应static at_ack_t esp_at_usart_wait_receive(uint32_t timeout) { uint32_t rxlen 0; const char *line rxbuf; uint64_t start cpu_get_ms(); rxbuf[0] \0; while (rxlen sizeof(rxbuf) - 1) { // 等待数据 while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) RESET) { if (cpu_get_ms() - start timeout) return AT_ACK_NONE; } // 接收一个字节 rxbuf[rxlen] USART_ReceiveData(USART2); rxbuf[rxlen] \0; // 收到换行符尝试匹配响应 if (rxbuf[rxlen - 1] \n) { at_ack_t ack match_internal_ack(line); if (ack ! AT_ACK_NONE) return ack; line rxbuf rxlen; } } return AT_ACK_NONE; }3.5 发送命令的统一接口bool esp_at_write_command(const char *command, uint32_t timeout) { #if ESP_AT_DEBUG printf([DEBUG] Send: %s\n, command); #endif // 发送命令 esp_at_usart_write(command); // 等待响应 at_ack_t ack esp_at_usart_wait_receive(timeout); #if ESP_AT_DEBUG printf([DEBUG] Response:\n%s\n, rxbuf); #endif return ack AT_ACK_OK; }四、功能模块实现4.1 模块初始化与Boot等待ESP32上电后需要一定时间启动通过循环发送AT指令检测模块是否就绪static bool esp_at_wait_boot(uint32_t timeout) { for (int t 0; t timeout; t 100) { if (esp_at_write_command(AT, 100)) return true; } return false; } bool esp_at_init(void) { esp_at_usart_init(); // 等待模块启动 if (!esp_at_wait_boot(3000)) return false; // 恢复出厂设置 if (!esp_at_write_command(ATRESTORE, 2000)) return false; // 等待模块就绪 if (!esp_at_wait_ready(5000)) return false; return true; }4.2 WiFi连接bool esp_at_wifi_init(void) { // 设置为Station模式 return esp_at_write_command(ATCWMODE1, 2000); } bool esp_at_connect_wifi(const char *ssid, const char *pwd, const char *mac) { if (ssid NULL || pwd NULL) return false; char cmd[128]; int len snprintf(cmd, sizeof(cmd), ATCWJAP\%s\,\%s\, ssid, pwd); if (mac) snprintf(cmd len, sizeof(cmd) - len, ,\%s\, mac); return esp_at_write_command(cmd, 5000); }4.3 WiFi状态查询static bool parse_cwstate_response(const char *response, esp_wifi_info_t *info) { response strstr(response, CWSTATE:); if (response NULL) return false; int wifi_state; if (sscanf(response, CWSTATE:%d,\%63[^\], wifi_state, info-ssid) ! 2) return false; info-connected (wifi_state 2); return true; } bool esp_at_get_wifi_info(esp_wifi_info_t *info) { if (!esp_at_write_command(ATCWSTATE?, 2000)) return false; if (!parse_cwstate_response(esp_at_get_response(), info)) return false; // 如果已连接获取详细信息 if (info-connected) { if (!esp_at_write_command(ATCWJAP?, 2000)) return false; sscanf(esp_at_get_response(), CWJAP:\%63[^\]\,\%17[^\]\,%d,%d, info-ssid, info-bssid, info-channel, info-rssi); } return true; }4.4 NTP时间获取bool esp_at_sntp_init(void) { // 配置NTP服务器8表示自动更新 return esp_at_write_command(ATCIPSNTPCFG1,8, 2000); } static bool parse_cipsntptime_response(const char *response, esp_date_time_t *date) { char weekday_str[8], month_str[4]; response strstr(response, CIPSNTPTIME:); if (sscanf(response, CIPSNTPTIME:%3s %3s %hhu %hhu:%hhu:%hhu %hu, weekday_str, month_str, date-day, date-hour, date-minute, date-second, date-year) ! 7) return false; date-weekday weekday_str_to_num(weekday_str); date-month month_str_to_num(month_str); return true; } bool esp_at_sntp_get_time(esp_date_time_t *date) { if (!esp_at_write_command(ATCIPSNTPTIME?, 2000)) return false; return parse_cipsntptime_response(esp_at_get_response(), date); }4.5 HTTP请求const char *esp_at_http_get(const char *url) { char *txbuf rxbuf; snprintf(txbuf, sizeof(rxbuf), ATHTTPCLIENT2,1,\%s\,,,2, url); bool ret esp_at_write_command(txbuf, 5000); return ret ? esp_at_get_response() : NULL; }五、使用示例int main(void) { esp_wifi_info_t wifi; esp_date_time_t time; // 1. 初始化ESP模块 if (!esp_at_init()) { printf(ESP module init failed!\r\n); while(1); } // 2. 配置WiFi模式 esp_at_wifi_init(); // 3. 连接WiFi if (esp_at_connect_wifi(MyWiFi, 12345678, NULL)) printf(WiFi connected!\r\n); // 4. 获取WiFi信息 esp_at_get_wifi_info(wifi); printf(SSID: %s, RSSI: %d\r\n, wifi.ssid, wifi.rssi); // 5. 初始化NTP esp_at_sntp_init(); // 6. 获取网络时间 if (esp_at_sntp_get_time(time)) printf(Time: %04d-%02d-%02d %02d:%02d:%02d\r\n, time.year, time.month, time.day, time.hour, time.minute, time.second); // 7. HTTP请求天气数据 const char *weather esp_at_http_get(http://api.weather.com/v1/current); if (weather) printf(Weather: %s\r\n, weather); while(1); }结果展示六、总结本章我们系统讲解了ESP32 AT指令集的基础知识并完整实现了STM32与ESP32的串口通信驱动层。我们从硬件连接开始逐步完成了串口初始化、AT指令发送、响应解析等核心函数并在此基础上封装了WiFi连接、状态查询、NTP时间获取和HTTP请求等实用功能模块。通过本章的学习你已经掌握了使用AT指令控制ESP32进行网络通信的全部技术要点后续章节我们将把这些功能与LCD显示结合起来实现天气数据的实时获取和展示。
智能天气时钟项目(一):ESP32 AT指令集详解与STM32驱动开发
目录前言一、为什么选择ESP32作为WiFi模块二、ESP32 AT指令集基础2.1 什么是AT指令2.2 常用AT指令分类三、STM32驱动层设计3.1 硬件连接3.2 数据结构设计3.3 串口初始化3.4 核心通信函数3.5 发送命令的统一接口四、功能模块实现4.1 模块初始化与Boot等待4.2 WiFi连接4.3 WiFi状态查询4.4 NTP时间获取4.5 HTTP请求五、使用示例六、总结前言今天我们将深入讲解ESP32 AT指令集并实现STM32与ESP32的通信驱动层。这是整个项目中最关键的部分之一——让STM32通过AT指令控制WiFi模块联网。一、为什么选择ESP32作为WiFi模块在嵌入式开发中WiFi联网方案主要有以下几种方案优点缺点自带WiFi的MCU如ESP32集成度高、成本低开发复杂、资源受限外置WiFi模块ESP8266/ESP32主控选择灵活、开发简单增加BOM成本4G模块覆盖广、无需WiFi成本高、功耗大对于STM32开发者来说使用ESP32作为AT指令驱动的WiFi模块是最佳选择开发简单、稳定性好、成本可控。二、ESP32 AT指令集基础2.1 什么是AT指令AT指令Attention Commands是一种串行通信命令集最早用于控制调制解调器。ESP32固件通过解析串口接收的AT指令执行相应操作并返回结果。发送AT返回OK2.2 常用AT指令分类类别指令功能基础AT测试通信基础ATRESTORE恢复出厂设置WiFiATCWMODE设置WiFi模式WiFiATCWJAP连接APWiFiATCWSTATE查询WiFi状态网络ATCIPSNTPCFG配置NTP时间网络ATHTTPCLIENTHTTP请求更详细更全面的内容参考以下网站ESP-AT用户指南https://docs.espressif.com/projects/esp-at/zh_CN/latest/esp32/AT_Command_Set/HTTP_AT_Commands.html三、STM32驱动层设计3.1 硬件连接STM32ESP32说明PA2 (USART2_TX)RX数据发送PA3 (USART2_RX)TX数据接收GNDGND共地3.3V3.3V电源3.2 数据结构设计// AT指令响应类型 typedef enum { AT_ACK_NONE, // 无匹配 AT_ACK_OK, // 成功 AT_ACK_ERROR, // 失败 AT_ACK_BUSY, // 忙 AT_ACK_READY, // 就绪 } at_ack_t; // 响应匹配表 static const at_ack_match_t at_ack_matches[] { {AT_ACK_OK, OK\r\n}, {AT_ACK_ERROR, ERROR\r\n}, {AT_ACK_BUSY, busy p...\r\n}, {AT_ACK_READY, ready\r\n}, }; // WiFi信息结构体 typedef struct { bool connected; // 连接状态 char ssid[64]; // WiFi名称 char bssid[18]; // MAC地址 int channel; // 信道 int rssi; // 信号强度 } esp_wifi_info_t; // 时间日期结构体 typedef struct { uint16_t year; // 年份 uint8_t month; // 月份 uint8_t day; // 日期 uint8_t weekday; // 星期 uint8_t hour; // 小时 uint8_t minute; // 分钟 uint8_t second; // 秒钟 } esp_date_time_t;3.3 串口初始化static void esp_at_usart_init(void) { // 1. 配置USART2参数 USART_InitTypeDef USART_InitStructure; USART_StructInit(USART_InitStructure); USART_InitStructure.USART_BaudRate 115200; // ESP8266/32默认波特率 USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; // 2. 配置GPIO复用功能 GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); // 3. 初始化GPIO GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_High_Speed; GPIO_InitStructure.GPIO_Pin GPIO_Pin_2 | GPIO_Pin_3; GPIO_Init(GPIOA, GPIO_InitStructure); // 4. 使能USART2 USART_Init(USART2, USART_InitStructure); USART_Cmd(USART2, ENABLE); }3.4 核心通信函数发送AT指令static void esp_at_usart_write(const char *data) { // 发送数据 while (data *data) { while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, *data); } // 发送换行符 \r\n while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, \r); while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, \n); }等待并解析响应static at_ack_t esp_at_usart_wait_receive(uint32_t timeout) { uint32_t rxlen 0; const char *line rxbuf; uint64_t start cpu_get_ms(); rxbuf[0] \0; while (rxlen sizeof(rxbuf) - 1) { // 等待数据 while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) RESET) { if (cpu_get_ms() - start timeout) return AT_ACK_NONE; } // 接收一个字节 rxbuf[rxlen] USART_ReceiveData(USART2); rxbuf[rxlen] \0; // 收到换行符尝试匹配响应 if (rxbuf[rxlen - 1] \n) { at_ack_t ack match_internal_ack(line); if (ack ! AT_ACK_NONE) return ack; line rxbuf rxlen; } } return AT_ACK_NONE; }3.5 发送命令的统一接口bool esp_at_write_command(const char *command, uint32_t timeout) { #if ESP_AT_DEBUG printf([DEBUG] Send: %s\n, command); #endif // 发送命令 esp_at_usart_write(command); // 等待响应 at_ack_t ack esp_at_usart_wait_receive(timeout); #if ESP_AT_DEBUG printf([DEBUG] Response:\n%s\n, rxbuf); #endif return ack AT_ACK_OK; }四、功能模块实现4.1 模块初始化与Boot等待ESP32上电后需要一定时间启动通过循环发送AT指令检测模块是否就绪static bool esp_at_wait_boot(uint32_t timeout) { for (int t 0; t timeout; t 100) { if (esp_at_write_command(AT, 100)) return true; } return false; } bool esp_at_init(void) { esp_at_usart_init(); // 等待模块启动 if (!esp_at_wait_boot(3000)) return false; // 恢复出厂设置 if (!esp_at_write_command(ATRESTORE, 2000)) return false; // 等待模块就绪 if (!esp_at_wait_ready(5000)) return false; return true; }4.2 WiFi连接bool esp_at_wifi_init(void) { // 设置为Station模式 return esp_at_write_command(ATCWMODE1, 2000); } bool esp_at_connect_wifi(const char *ssid, const char *pwd, const char *mac) { if (ssid NULL || pwd NULL) return false; char cmd[128]; int len snprintf(cmd, sizeof(cmd), ATCWJAP\%s\,\%s\, ssid, pwd); if (mac) snprintf(cmd len, sizeof(cmd) - len, ,\%s\, mac); return esp_at_write_command(cmd, 5000); }4.3 WiFi状态查询static bool parse_cwstate_response(const char *response, esp_wifi_info_t *info) { response strstr(response, CWSTATE:); if (response NULL) return false; int wifi_state; if (sscanf(response, CWSTATE:%d,\%63[^\], wifi_state, info-ssid) ! 2) return false; info-connected (wifi_state 2); return true; } bool esp_at_get_wifi_info(esp_wifi_info_t *info) { if (!esp_at_write_command(ATCWSTATE?, 2000)) return false; if (!parse_cwstate_response(esp_at_get_response(), info)) return false; // 如果已连接获取详细信息 if (info-connected) { if (!esp_at_write_command(ATCWJAP?, 2000)) return false; sscanf(esp_at_get_response(), CWJAP:\%63[^\]\,\%17[^\]\,%d,%d, info-ssid, info-bssid, info-channel, info-rssi); } return true; }4.4 NTP时间获取bool esp_at_sntp_init(void) { // 配置NTP服务器8表示自动更新 return esp_at_write_command(ATCIPSNTPCFG1,8, 2000); } static bool parse_cipsntptime_response(const char *response, esp_date_time_t *date) { char weekday_str[8], month_str[4]; response strstr(response, CIPSNTPTIME:); if (sscanf(response, CIPSNTPTIME:%3s %3s %hhu %hhu:%hhu:%hhu %hu, weekday_str, month_str, date-day, date-hour, date-minute, date-second, date-year) ! 7) return false; date-weekday weekday_str_to_num(weekday_str); date-month month_str_to_num(month_str); return true; } bool esp_at_sntp_get_time(esp_date_time_t *date) { if (!esp_at_write_command(ATCIPSNTPTIME?, 2000)) return false; return parse_cipsntptime_response(esp_at_get_response(), date); }4.5 HTTP请求const char *esp_at_http_get(const char *url) { char *txbuf rxbuf; snprintf(txbuf, sizeof(rxbuf), ATHTTPCLIENT2,1,\%s\,,,2, url); bool ret esp_at_write_command(txbuf, 5000); return ret ? esp_at_get_response() : NULL; }五、使用示例int main(void) { esp_wifi_info_t wifi; esp_date_time_t time; // 1. 初始化ESP模块 if (!esp_at_init()) { printf(ESP module init failed!\r\n); while(1); } // 2. 配置WiFi模式 esp_at_wifi_init(); // 3. 连接WiFi if (esp_at_connect_wifi(MyWiFi, 12345678, NULL)) printf(WiFi connected!\r\n); // 4. 获取WiFi信息 esp_at_get_wifi_info(wifi); printf(SSID: %s, RSSI: %d\r\n, wifi.ssid, wifi.rssi); // 5. 初始化NTP esp_at_sntp_init(); // 6. 获取网络时间 if (esp_at_sntp_get_time(time)) printf(Time: %04d-%02d-%02d %02d:%02d:%02d\r\n, time.year, time.month, time.day, time.hour, time.minute, time.second); // 7. HTTP请求天气数据 const char *weather esp_at_http_get(http://api.weather.com/v1/current); if (weather) printf(Weather: %s\r\n, weather); while(1); }结果展示六、总结本章我们系统讲解了ESP32 AT指令集的基础知识并完整实现了STM32与ESP32的串口通信驱动层。我们从硬件连接开始逐步完成了串口初始化、AT指令发送、响应解析等核心函数并在此基础上封装了WiFi连接、状态查询、NTP时间获取和HTTP请求等实用功能模块。通过本章的学习你已经掌握了使用AT指令控制ESP32进行网络通信的全部技术要点后续章节我们将把这些功能与LCD显示结合起来实现天气数据的实时获取和展示。