当前位置: 首页 > news >正文

智能天气时钟项目(一):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
返回:OK

2.2 常用AT指令分类

类别指令功能
基础AT测试通信
基础AT+RESTORE恢复出厂设置
WiFiAT+CWMODE设置WiFi模式
WiFiAT+CWJAP连接AP
WiFiAT+CWSTATE查询WiFi状态
网络AT+CIPSNTPCFG配置NTP时间
网络AT+HTTPCLIENTHTTP请求

更详细更全面的内容参考以下网站:(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("AT+RESTORE", 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("AT+CWMODE=1", 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), "AT+CWJAP=\"%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("AT+CWSTATE?", 2000)) return false; if (!parse_cwstate_response(esp_at_get_response(), info)) return false; // 如果已连接,获取详细信息 if (info->connected) { if (!esp_at_write_command("AT+CWJAP?", 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("AT+CIPSNTPCFG=1,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("AT+CIPSNTPTIME?", 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), "AT+HTTPCLIENT=2,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显示结合起来,实现天气数据的实时获取和展示。

http://www.jsqmd.com/news/567130/

相关文章:

  • 技术人的副业探索:从技术博客到独立开发者的可能性
  • Outstatic项目架构剖析:理解monorepo设计与模块化思想
  • 用PyTorch从零搭建C3D网络:手把手教你实现视频动作识别(附完整代码与UCF101数据集处理)
  • 学习---3
  • 如何快速安装webMAN-MOD:PS3全能插件终极指南
  • AI赋能构思与实现:在快马平台协同设计智能旅行推荐Agent Skill
  • 深入PSINS工具箱:手把手教你自定义卡尔曼滤波器状态与观测模型(以15状态为例)
  • 鲁班猫3网络连接问题解决记录(主机名直连)
  • 新手友好:基于快马平台快速上手dhnvr416h-hd设备数据监控开发
  • Intv_AI_MK11可视化设计工具链:从Visio流程图到AI可理解的需求文档
  • MySQL IF 和 IFNULL 用法详解
  • 抢抓AI发展红利,钧略AIGEO助力企业高效获客 - 企业推荐官【官方】
  • springboot+vue基于web的线上超市团购系统的设计与实现
  • AI安全:从技术加固到体系化防御的实战演进
  • 别再只查基因列表了!深度解析MSigDB中Hallmark基因集的生物学意义与科研应用场景
  • 实战react项目:基于快马ai快速构建包含图表与导航的用户数据仪表盘
  • 字符串(字符数组) 速览功能
  • 优先选择钧略AI:携手合作伙伴,共拓AI搜索优化新未来 - 企业推荐官【官方】
  • PDF转Markdown神器MinerU 2.5:支持多栏、表格、公式,效果实测
  • OpCore-Simplify:从3天到15分钟的黑苹果配置革命
  • springboot+vue基于web的咖啡点单程序设计
  • 实战应用:基于编译原理,利用快马AI构建你的首个代码压缩工具
  • 「Elasticsearch 底层原理与性能调优全攻略」
  • ArcGIS Desktop图例设置避坑指南:为什么你的图例总对不齐?从布局视图到符号系统的全流程解析
  • 扎根合肥,老钱全屋定制工厂以实力服务万家 - 企业推荐官【官方】
  • Alibaba DASD-4B Thinking 对话工具与.NET生态集成:开发企业级智能应用
  • 让Agent真正“行动”起来,Agent Skill开发者大赛正式启动!
  • 别再只看CPU跑分了!用Stream实测内存带宽,教你给服务器/PC内存性能“把把脉”
  • 京东科技重磅发布ClawTip,为AI Agent生态打造新型支付基础设施
  • 用C语言实现唐诗输入输出