--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — WiFi章节 + 配套代码10~11" author: "尚硅谷研究院" date: 2026-07-16 created: 2026-07-15 --- # WiFi通信与ESP32-C3 > **用生活理解**:ESP32-C3 就像一个随身 WiFi 翻译官——你的单片机不会说 WiFi,但 ESP32-C3 会。你用串口告诉它:"连这个路由器"、"发这串数据到这个 IP",它帮你搞定所有 WiFi 通信。AT 指令就是你和翻译官之间的暗号手册。 --- ## ESP32-C3 简介 ESP32-C3 是乐鑫科技(Espressif)推出的**RISC-V 32 位**WiFi + BLE 5.0 芯片。 ### 核心规格 | 特性 | 值 | |------|-----| | **内核** | RISC-V 32 位单核,最高 160MHz | | **WiFi** | 802.11 b/g/n, 2.4GHz(仅 2.4GHz,不支持 5GHz) | | **BLE** | Bluetooth 5.0 (LE) | | **RAM** | 400KB SRAM | | **Flash** | 4MB | | **接口** | UART、SPI、I2C、I2S、PWM、ADC | | **工作模式** | Station(连接路由器)/ SoftAP(自建热点)/ Station+AP | > **参考**:乐鑫 ESP32-C3 数据手册、ESP32-C3 AT 指令集用户手册 > **注意**:ESP32-C3 与 ESP8266 不兼容(不同内核、不同指令集) ### AT 指令工作模式 ESP32-C3 默认出厂固件为**AT 指令模式**——芯片从 Flash 启动后,通过串口接收 AT 指令并返回响应。 **通信方式**: ``` STM32(USART2) ── TX ──→ RX(ESP32-C3) ── RX ←── TX(ESP32-C3) ── GND ── GND ``` **响应格式**:每条指令执行后返回 `\r\nOK\r\n` 或 `\r\nERROR\r\n` --- ## AT 指令集详解 ### 基础测试指令 | AT 指令 | 预期响应 | 说明 | |---------|---------|------| | `AT\r\n` | `OK` | 测试连接是否正常 | | `AT+RST\r\n` | `ready` | 软复位模块 | | `AT+GMR\r\n` | 版本信息 | 查看 AT 固件版本 | | `ATE0\r\n` | `OK` | 关闭回显(默认开启,ATE1=开) | | `AT+UART_CUR=115200,8,1,0,0\r\n` | `OK` | 设置当前串口参数(波特率/数据位/停止位/校验/流控) | ### WiFi 操作指令 | AT 指令 | 说明 | 响应 | |---------|------|------| | `AT+CWMODE=1\r\n` | Station 模式(连路由器) | `OK` | | `AT+CWMODE=2\r\n` | SoftAP 模式(自建热点) | `OK` | | `AT+CWMODE=3\r\n` | Station + AP 模式 | `OK` | | `AT+CWJAP="SSID","password"\r\n` | 连接指定 WiFi | `WIFI CONNECTED`/`WIFI DISCONNECT` | | `AT+CWQAP\r\n` | 断开当前 WiFi | `OK` | | `AT+CWLAP\r\n` | 扫描附近 WiFi 列表 | `+CWLAP:enc,auth,ssid,rssi,mac` | | `AT+CIFSR\r\n` | 查询本地 IP 地址 | `+CIFSR:STAIP,"192.168.x.x"` | ### TCP/UDP 通信指令 | AT 指令 | 说明 | 响应 | |---------|------|------| | `AT+CIPSTART="TCP","ip",port\r\n` | 建立 TCP 连接 | `CONNECT` | | `AT+CIPSTART="UDP","ip",port\r\n` | 建立 UDP 连接 | `CONNECT` | | `AT+CIPMODE=1\r\n` | 开启**透传模式** | `OK` | | `AT+CIPSEND\r\n` | 透传模式下开始发送(出现 `>` 提示符) | `>` | | `AT+CIPSEND=len\r\n` | 发送指定长度的数据(非透传) | `>` 后发数据 | | `AT+CIPSTATUS\r\n` | 查询连接状态 | `STATUS:3`(已连接)/`STATUS:4`(已断开) | | `AT+CIPCLOSE\r\n` | 关闭当前连接 | `CLOSED` | | `AT+CIPMUX=0\r\n` | 单连接模式 | `OK` | | `AT+CIPMUX=1\r\n` | 多连接模式(最多 5 个) | `OK` | | `AT+CIPSERVER=1,port\r\n` | 创建 TCP Server | `OK` | | `AT+CIPDINFO=1\r\n` | IPD 显示格式含对端信息 | `OK` | ### 透传模式详解 透传模式(Transparent Transmission)下,进入 `AT+CIPSEND` 后出现的 `>` 提示符,此后所有串口输入数据**自动转发**到 TCP 连接,收到的 TCP 数据也自动从串口输出。 **退出透传模式**:**在 20ms 内没有数据的时间间隔后**,发送 `+++`(不需要换行回车),可退出透传返回 AT 指令模式。 ``` 非透传模式(CIPMODE=0): AT+CIPSEND=5\r\n → 发送指令 > → 提示符 Hello → 发送 5 字节 SEND OK → 完成 透传模式(CIPMODE=1): AT+CIPMODE=1\r\n OK AT+CIPSEND\r\n > → 进入透传 Hello World... → 所有串口数据直接变为 TCP 数据发送 +++(间隔20ms) → 退出透传 ``` --- ## 实验:ESP32-C3 TCP 通信 **项目路径**:`stm32/10_wifi_test_hal`、`stm32/11_wifi_tcp_server_hal` ### 实验 1:基础 AT 通信测试 **文件:`stm32/10_wifi_test_hal/Core/Src/main.c`** 两个实验共享同一个 ESP32 底层驱动(`esp32.c`),通过 USART2 与 ESP32-C3 通信。实验 1 仅发送基础 AT 测试指令: ```c #include "main.h" #include "usart.h" #include "gpio.h" #include "esp32.h" int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // MX_USART2_UART_Init(); printf("尚硅谷Wi-Fi通信实验:AT指令...\n"); ESP32_Init(); printf("ESP32 初始化完成!\n"); uint8_t *cmd = "AT\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); cmd = "AT+GMR\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); while (1) { } } ``` **文件:`stm32/10_wifi_test_hal/Interface/ESP32/esp32.c`**(底层驱动,所有真实 AT 指令在此): ```c #include "esp32.h" uint8_t respBuff[1024]; uint16_t respLen; void ESP32_Init(void) { MX_USART2_UART_Init(); // AT+RST=0 复位 ESP32(此处的 =0 为特定固件版本的复位参数) uint8_t *cmd = "AT+RST=0\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); HAL_Delay(2000); } void ESP32_SendCmd(uint8_t *cmd, uint16_t cmdLen) { memset(respBuff, 0, 1024); HAL_UART_Transmit(&huart2, cmd, cmdLen, 1000); do { ESP32_ReadResp(respBuff, &respLen); } while (strstr((char *)respBuff, "OK") == NULL); printf("%.*s\n", respLen, respBuff); } void ESP32_ReadResp(uint8_t buff[], uint16_t *len) { HAL_UARTEx_ReceiveToIdle(&huart2, buff, 1024, len, 1000); } ``` **文件:`stm32/10_wifi_test_hal/Interface/ESP32/esp32.h`** ```c #ifndef __ESP32_H #define __ESP32_H #include "usart.h" #include void ESP32_Init(void); void ESP32_SendCmd(uint8_t *cmd, uint16_t cmdLen); void ESP32_ReadResp(uint8_t buff[], uint16_t *len); #endif ``` ### 实验 2:WiFi TCP 服务器 **文件:`stm32/11_wifi_tcp_server_hal/Core/Src/main.c`** ```c #include "main.h" #include "usart.h" #include "gpio.h" #include "wifi.h" uint8_t rxBuff[1024]; uint16_t rxLen; uint8_t id; uint8_t ip[16]; uint16_t port; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("尚硅谷Wi-Fi通信实验:TCP服务器...\n"); WIFI_Init(AP); WIFI_TCP_ServerStart(); while (1) { WIFI_TCP_ReadData(rxBuff, &rxLen, &id, ip, &port); if (rxLen > 0) { printf("收到数据:连接ID = %d, 对端IP:端口号 = %s:%d, 数据长度 = %d, 内容 = %.*s\n", id, ip, port, rxLen, rxLen, rxBuff); WIFI_TCP_SendData(id, rxBuff, rxLen); rxLen = 0; } } } ``` **文件:`stm32/11_wifi_tcp_server_hal/App/WIFI/wifi.h`** ```c #ifndef __WIFI_H #define __WIFI_H #include "esp32.h" typedef enum { STA = 1, AP = 2 } WIFI_MODE; void WIFI_Init(WIFI_MODE mode); void WIFI_TCP_ServerStart(void); void WIFI_TCP_SendData(uint8_t id, uint8_t *data, uint16_t len); void WIFI_TCP_ReadData(uint8_t rxBuff[], uint16_t *rxLen, uint8_t *id, uint8_t *ip, uint16_t *port); #endif ``` **文件:`stm32/11_wifi_tcp_server_hal/App/WIFI/wifi.c`**(包含所有真实 WiFi AT 指令和 TCP 通信): ```c #include "wifi.h" static void WIFI_STA_Mode(void); static void WIFI_AP_Mode(void); void WIFI_Init(WIFI_MODE mode) { ESP32_Init(); if (mode == STA) WIFI_STA_Mode(); else if (mode == AP) WIFI_AP_Mode(); printf("Wi-Fi 初始化完成!\n"); } static void WIFI_STA_Mode(void) { printf("设置为STA模式...\n"); uint8_t *cmd = "AT+CWMODE=1\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); printf("连接AP...\n"); cmd = "AT+CWJAP=\"wsr\",\"1234abcd\"\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); printf("查询IP地址为:\n"); cmd = "AT+CIPSTA?\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); } static void WIFI_AP_Mode(void) { printf("设置为AP模式...\n"); uint8_t *cmd = "AT+CWMODE=2\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); printf("设置AP...\n"); cmd = "AT+CWSAP=\"atguigu-esp32\",\"12345678\",5,3\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); printf("设置本地IP地址...\n"); cmd = "AT+CIPAP=\"192.168.8.1\"\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); } void WIFI_TCP_ServerStart(void) { printf("使能多连接...\n"); uint8_t *cmd = "AT+CIPMUX=1\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); printf("启动TCP服务器...\n"); cmd = "AT+CIPSERVER=1,8080\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); printf("设置IPD数据格式...\n"); cmd = "AT+CIPDINFO=1\r\n"; ESP32_SendCmd(cmd, strlen((char *)cmd)); } void WIFI_TCP_SendData(uint8_t id, uint8_t *data, uint16_t len) { printf("准备发送数据...\n"); uint8_t sendCmd[50] = {0}; sprintf((char *)sendCmd, "AT+CIPSEND=%d,%d\r\n", id, len); ESP32_SendCmd(sendCmd, strlen((char *)sendCmd)); HAL_UART_Transmit(&huart2, data, len, 1000); } uint8_t tempBuff[1024]; uint16_t tempLen; void WIFI_TCP_ReadData(uint8_t rxBuff[], uint16_t *rxLen, uint8_t *id, uint8_t *ip, uint16_t *port) { HAL_UARTEx_ReceiveToIdle(&huart2, tempBuff, 1024, &tempLen, 1000); if ( strstr((char *)tempBuff, "+IPD") != NULL ) { sscanf((char *)tempBuff, "%*[\r\n]+IPD,%hhu,%hu,\"%[^\"]\",%hu", id, rxLen, ip, port); char * pData = strstr((char *)tempBuff, ":") + 1; memcpy(rxBuff, pData, *rxLen); } } ``` --- ## 核心速查表 | 操作 | AT 指令 | 等待响应 | 超时 | |------|---------|---------|------| | 测试连接 | `AT` | `OK` | 2s | | 复位模块 | `AT+RST=0` | `ready` | 2s | | 设置 Station | `AT+CWMODE=1` | `OK` | 1s | | 设置 AP | `AT+CWMODE=2` | `OK` | 1s | | 扫描 WiFi | `AT+CWLAP` | `OK` | 10s | | 连接 WiFi | `AT+CWJAP="SSID","pwd"` | `WIFI GOT IP` | 15s | | 查询 IP | `AT+CIFSR` / `AT+CIPSTA?` | `OK` | 2s | | 设置 AP 参数 | `AT+CWSAP="ssid","pwd",ch,enc` | `OK` | 2s | | 设置本机 IP | `AT+CIPAP="192.168.x.x"` | `OK` | 2s | | 多连接使能 | `AT+CIPMUX=1` | `OK` | 2s | | 启动 TCP Server | `AT+CIPSERVER=1,port` | `OK` | 2s | | IPD 含对端信息 | `AT+CIPDINFO=1` | `OK` | 2s | | TCP 连接(客户端) | `AT+CIPSTART="TCP",ip,port` | `CONNECT` | 10s | | 发送数据 | `AT+CIPSEND=id,len` → 数据 | `SEND OK` | 5s | | 进入透传 | `AT+CIPMODE=1` → `AT+CIPSEND` | `>` | 3s | | 退出透传 | `+++`(无换行,20ms 间隔) | `OK` | — | | 关闭连接 | `AT+CIPCLOSE` | `CLOSED` | 5s | ## 常见问题与避坑 1. **AT 指令无响应** → 检查波特率(确保一致)、TX↔RX 交叉连接、模块电源(峰值 500mA 需足够供电) 2. **WiFi 连不上** → 仅支持 2.4GHz(不支持 5GHz)、检查 SSID 密码、AP 是否隐藏 3. **TCP 连接失败** → 检查目标 IP 可达性、端口是否开放、防火墙规则 4. **透传模式数据回显** → `ATE0` 关闭回显;否则发送的数据会同时从 ESP 返回 5. **+++ 退不出透传** → 要求前后 20ms 无数据,保证足够间隔 6. **数据长度错** → 非透传下,AT+CIPSEND=id,len 的 len 必须与后续发送的数据字节数严格一致